From b466c871287f90ff5b4dd5ad93bb017dda421021 Mon Sep 17 00:00:00 2001 From: B3CKDOOR Date: Sun, 24 May 2026 22:59:58 +0200 Subject: [PATCH 1/4] Split buid/sign for split over cloud/selfhosted machines --- build.yml | 63 ++++++++++++ sign.yml | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 build.yml create mode 100644 sign.yml diff --git a/build.yml b/build.yml new file mode 100644 index 00000000..bab4995a --- /dev/null +++ b/build.yml @@ -0,0 +1,63 @@ +name: Build Release Artifacts + +on: + push: + tags: + - 'v*' + workflow_dispatch: + inputs: + version: + description: 'Version tag (e.g., v1.0.0)' + required: true + type: string + +jobs: + build: + runs-on: windows-2022 + + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '10.0.x' + + - name: Publish Bootstrap + working-directory: SS14.Launcher.Bootstrap + run: dotnet publish -r win-x64 + + - name: Create Builds + run: | + ./publish.py osx linux + ./publish.py windows --x64-only + + - name: Upload Unsigned Artifacts + uses: actions/upload-artifact@v4 + with: + # Unique name based on SHA to ensure correct pairing + name: unsigned-builds-${{ github.sha }} + path: | + ./SS14.Launcher_Windows.zip + ./SS14.Launcher_Linux.zip + ./SS14.Launcher_macOS.zip + SS14.Launcher.Bootstrap/bin/Release/net10.0-windows/win-x64/publish/Space Station 14 Launcher.exe + retention-days: 1 + + - name: Generate App Token for Trigger + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Trigger Signing Workflow + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh workflow run sign.yml \ + --ref ${{ github.ref }} \ + -f version="${{ github.event.inputs.version || github.ref_name }}" diff --git a/sign.yml b/sign.yml new file mode 100644 index 00000000..e845f40d --- /dev/null +++ b/sign.yml @@ -0,0 +1,286 @@ +name: Sign and Publish Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version tag to sign' + required: true + type: string + +jobs: + sign-and-publish: + runs-on: [self-hosted, signing] + + steps: + - name: Generate App Token + id: bot-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - name: Determine Tag Name + id: tag_name + shell: pwsh + run: | + $tag = "${{ github.event.inputs.version }}" + if (-not $tag.StartsWith("v")) { + Write-Error "Tag must start with 'v'. Received: $tag" + exit 1 + } + "tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Determine Release Flags + id: release_flags + shell: pwsh + run: | + $tagLower = "${{ steps.tag_name.outputs.tag }}".ToLower() + $isPreRelease = "false" + $isDraft = "false" + + if ($tagLower -match "alpha" -or $tagLower -match "beta") { + $isPreRelease = "true" + } elseif ($tagLower -match "test") { + $isPreRelease = "true" + $isDraft = "true" + } + + "prerelease=$isPreRelease" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + "draft=$isDraft" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + tag_name: ${{ steps.tag_name.outputs.tag }} + release_name: Release ${{ steps.tag_name.outputs.tag }} + draft: ${{ steps.release_flags.outputs.draft }} + prerelease: ${{ steps.release_flags.outputs.prerelease }} + + - name: Download Unsigned Artifacts + uses: actions/download-artifact@v4 + with: + name: unsigned-builds-${{ github.sha }} + path: ./build-output + + - name: Sign All Artifacts & Generate Formatted Hashes + shell: pwsh + run: | + Write-Host "Starting Signing (VS Build Tools 2022) & Hash Generation..." + Write-Host "Ensure YubiKey is plugged in and VirtualHere is connected." + + $formattedLines = @() + + $signtoolPath = $null + + $vsBase = "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools" + if (Test-Path $vsBase) { + $sdkBin = Join-Path $vsBase "VC\Tools\MSVC" + if (Test-Path $sdkBin) { + $latestMsvc = Get-ChildItem $sdkBin | Sort-Object Name -Descending | Select-Object -First 1 + $signtoolPath = Join-Path $latestMsvc.FullName "bin\Hostx64\x64\signtool.exe" + } + } + + if (-not (Test-Path $signtoolPath)) { + $kitBin = "C:\Program Files (x86)\Windows Kits\10\bin" + if (Test-Path $kitBin) { + $latestKit = Get-ChildItem $kitBin -Directory | Sort-Object Name -Descending | Select-Object -First 1 + $signtoolPath = Join-Path $latestKit.FullName "x64\signtool.exe" + } + } + + if (-not (Test-Path $signtoolPath)) { + Write-Error "signtool.exe not found. Ensure 'Visual Studio Build Tools 2022' with 'C++ Build Tools' is installed." + exit 1 + } + + $winFiles = @( + "./build-output/SS14.Launcher_Windows.zip", + "./build-output/Space Station 14 Launcher.exe" + ) + + foreach ($file in $winFiles) { + if (Test-Path $file) { + Write-Host "Signing Windows: $file" + + & $signtoolPath sign ` + /tr "http://timestamp.digicert.com" ` + /td sha256 ` + /fd sha256 ` + /sm ` + /kp "Microsoft Base Smart Card Crypto Provider" ` + /n "SandwichStation-Code" ` # REPLACE WITH YOUR CERT CN if your not us! + $file + + if ($LASTEXITCODE -ne 0) { + Write-Error "Windows signing failed for $file. Did you touch the YubiKey?" + exit 1 + } + + $hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash + $fileName = $file.Substring(14) + $formattedLines += "- $fileName ``$hash``" + Write-Host "Signed & Hashed: $fileName" + } + } + + # --- 3. SIGN LINUX/MAC FILES (GPG) --- + $gpgPath = "C:\Program Files (x86)\GnuPG\bin\gpg.exe" + if (-not (Test-Path $gpgPath)) { + $gpgPaths = @("C:\Program Files\GnuPG\bin\gpg.exe", "gpg") + foreach ($p in $gpgPaths) { if (Get-Command $p -ErrorAction SilentlyContinue) { $gpgPath = $p; break } } + } + + $nixFiles = @( + "./build-output/SS14.Launcher_Linux.zip", + "./build-output/SS14.Launcher_macOS.zip" + ) + + if (Test-Path $gpgPath) { + foreach ($file in $nixFiles) { + if (Test-Path $file) { + Write-Host "Signing Unix: $file" + & $gpgPath --batch --yes --detach-sign --armor $file + + if ($LASTEXITCODE -ne 0) { + Write-Error "GPG signing failed for $file." + exit 1 + } + + $hash = (Get-FileHash -Path $file -Algorithm SHA256).Hash + $fileName = $file.Substring(14) + $formattedLines += "- $fileName ``$hash``" + Write-Host "Signed & Hashed: $fileName" + + $ascFile = "$file.asc" + if (Test-Path $ascFile) { + $ascHash = (Get-FileHash -Path $ascFile -Algorithm SHA256).Hash + $ascName = $ascFile.Substring(14) + $formattedLines += "- $ascName ``$ascHash``" + } + } + } + } else { + Write-Warning "gpg.exe not found. Skipping Linux/Mac signing." + } + + $sumsPath = "./build-output/SHA256SUMS.txt" + $standardLines = $formattedLines | ForEach-Object { + if ($_ -match "^-\s+(.+)\s+``([a-f0-9]+)``$") { + "$($matches[2]) $($matches[1])" + } + } + $standardLines | Set-Content -Path $sumsPath + Write-Host "Created: $sumsPath" + + $releaseBodySection = $formattedLines -join "`n" + "sha_markdown=$releaseBodySection" | Out-File -FilePath $env:GITHUB_OUTPUT -Append + + - name: Update Release with SHA256 + uses: actions/github-script@v7 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + script: | + const shaMarkdown = process.env.sha_markdown; + const releaseId = '${{ steps.create_release.outputs.id }}'; + + const { data: release } = await github.rest.repos.getRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: releaseId + }); + + const newBody = `${release.body}\n\n## SHA256 Hashes for verification:\n${shaMarkdown}`; + + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: releaseId, + body: newBody + }); + + - name: Upload Signed Windows Assets + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SS14.Launcher_Windows.zip + asset_name: SS14.Launcher_Windows.zip + asset_content_type: application/zip + + - name: Upload Signed Bootstrap + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/Space Station 14 Launcher.exe + asset_name: Space Station 14 Launcher.exe + asset_content_type: application/x-msdownload + + - name: Upload Signed Linux Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SS14.Launcher_Linux.zip + asset_name: SS14.Launcher_Linux.zip + asset_content_type: application/zip + + - name: Upload Signed macOS Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SS14.Launcher_macOS.zip + asset_name: SS14.Launcher_macOS.zip + asset_content_type: application/zip + + - name: Upload GPG Signatures + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SS14.Launcher_Linux.zip.asc + asset_name: SS14.Launcher_Linux.zip.asc + asset_content_type: application/pgp-signature + + - name: Upload GPG Signature (macOS) + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SS14.Launcher_macOS.zip.asc + asset_name: SS14.Launcher_macOS.zip.asc + asset_content_type: application/pgp-signature + + - name: Upload SHA256SUMS.txt + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ steps.bot-token.outputs.token }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build-output/SHA256SUMS.txt + asset_name: SHA256SUMS.txt + asset_content_type: text/plain + + - name: Cleanup + shell: pwsh + run: | + Remove-Item -Recurse -Force ./build-output + Write-Host "Local artifacts cleaned up." From aeb52c51fd8c7b94b2d7ae4097c7e4f7a112595c Mon Sep 17 00:00:00 2001 From: B3CKDOOR Date: Sun, 24 May 2026 23:00:22 +0200 Subject: [PATCH 2/4] depricate the old script, but keep for now --- .../workflows/{publish-release.yml => publish-release.yml.old} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{publish-release.yml => publish-release.yml.old} (100%) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml.old similarity index 100% rename from .github/workflows/publish-release.yml rename to .github/workflows/publish-release.yml.old From f02db1648d0accd45aff7608bd8a0ff77b9f0062 Mon Sep 17 00:00:00 2001 From: B3CKDOOR Date: Sun, 24 May 2026 23:00:36 +0200 Subject: [PATCH 3/4] update SLN file --- SS14.Launcher.sln | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/SS14.Launcher.sln b/SS14.Launcher.sln index 1354701e..e408025e 100644 --- a/SS14.Launcher.sln +++ b/SS14.Launcher.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 +# Visual Studio Version 18 +VisualStudioVersion = 18.6.11819.183 stable MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Launcher", "SS14.Launcher\SS14.Launcher.csproj", "{15FE716D-0987-4F2F-8354-2D9CCAAC7B98}" EndProject @@ -10,26 +10,28 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Launcher.Bootstrap", "SS14.Launcher.Bootstrap\SS14.Launcher.Bootstrap.csproj", "{505C4E21-0B62-4F1E-82C6-8FDBA914757A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{12BB41B7-34BA-44EF-9ECA-434A276D83BD}" -ProjectSection(SolutionItems) = preProject - Readme.md = Readme.md - LICENSE.txt = LICENSE.txt - .editorconfig = .editorconfig - Launcher.props = Launcher.props - Directory.Packages.props = Directory.Packages.props -EndProjectSection + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Packages.props = Directory.Packages.props + Launcher.props = Launcher.props + LICENSE.txt = LICENSE.txt + Readme.md = Readme.md + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{31D3CCC5-36F4-48C3-8885-E029A2026B57}" -ProjectSection(SolutionItems) = preProject - .github\workflows\build-test.yml = .github\workflows\build-test.yml - .github\workflows\publish-release.yml = .github\workflows\publish-release.yml -EndProjectSection + ProjectSection(SolutionItems) = preProject + .github\workflows\build-test.yml = .github\workflows\build-test.yml + build.yml = build.yml + .github\workflows\publish-release.yml.old = .github\workflows\publish-release.yml.old + sign.yml = sign.yml + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Scripts", "Build Scripts", "{DBA9443D-A94E-4CD4-8603-ACC2F1434F0F}" -ProjectSection(SolutionItems) = preProject - download_net_runtime.py = download_net_runtime.py - exe_set_subsystem.py = exe_set_subsystem.py - publish.py = publish.py -EndProjectSection + ProjectSection(SolutionItems) = preProject + download_net_runtime.py = download_net_runtime.py + exe_set_subsystem.py = exe_set_subsystem.py + publish.py = publish.py + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SS14.Loader", "SS14.Loader\SS14.Loader.csproj", "{4480C40E-ABCE-4012-8481-021A9ACCF9BB}" EndProject @@ -49,9 +51,6 @@ Global Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {15FE716D-0987-4F2F-8354-2D9CCAAC7B98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {15FE716D-0987-4F2F-8354-2D9CCAAC7B98}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -114,8 +113,14 @@ Global {D7B7D05D-4C78-41AB-9643-6543EF8EBE29}.Release|x86.ActiveCfg = Release|Any CPU {D7B7D05D-4C78-41AB-9643-6543EF8EBE29}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection GlobalSection(NestedProjects) = preSolution {31D3CCC5-36F4-48C3-8885-E029A2026B57} = {12BB41B7-34BA-44EF-9ECA-434A276D83BD} {DBA9443D-A94E-4CD4-8603-ACC2F1434F0F} = {12BB41B7-34BA-44EF-9ECA-434A276D83BD} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C97F7E85-A0F0-4020-B993-4D1F4BA9D037} + EndGlobalSection EndGlobal From 95cd670f127dec9b1cb252a440c8838efa8338fe Mon Sep 17 00:00:00 2001 From: B3CKDOOR Date: Sun, 24 May 2026 23:08:35 +0200 Subject: [PATCH 4/4] Whoops, dont go to root pls --- build.yml => .github/workflows/build.yml | 0 sign.yml => .github/workflows/sign.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename build.yml => .github/workflows/build.yml (100%) rename sign.yml => .github/workflows/sign.yml (100%) diff --git a/build.yml b/.github/workflows/build.yml similarity index 100% rename from build.yml rename to .github/workflows/build.yml diff --git a/sign.yml b/.github/workflows/sign.yml similarity index 100% rename from sign.yml rename to .github/workflows/sign.yml