From 5e4416c9cb2c5b458bad7f5add4991b38d75a7ee Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 25 Aug 2026 23:32:36 +0200 Subject: [PATCH 1/3] Publish rolling pre-releases for main and staging Workflow artifacts can only be downloaded by signed-in users with access to the repository, so they are useless for handing a build to anyone else. After the matrix publishes, a release job recreates a rolling pre-release from the same packages; its assets are downloadable without an account: releases/download/latest/ from main releases/download/staging/ from staging Each branch owns its tag, so a staging build never overwrites the main one. The release is deleted and created again rather than edited, so the tag follows its branch and assets from an older build do not linger. Write access is granted to that job only, the workflow default stays read. --- .github/workflows/dotnet.yml | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7e70c7c..b262753 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -4,10 +4,12 @@ on: push: branches: - main + - staging pull_request: branches: - main + - staging workflow_dispatch: @@ -105,3 +107,46 @@ jobs: path: ${{ matrix.package }} if-no-files-found: error archive: false + + # Workflow artifacts are only reachable for signed-in users with repository + # access, so every build of a release branch also lands in a rolling + # pre-release, whose assets anyone can download: + # https://github.com/${{ github.repository }}/releases/download// + # main -> "latest", staging -> "staging"; the two never overwrite each other. + release: + name: Rolling pre-release + needs: publish + if: github.event_name == 'push' + runs-on: ubuntu-latest + + permissions: + contents: write + + env: + RELEASE_TAG: ${{ github.ref_name == 'main' && 'latest' || 'staging' }} + RELEASE_NAME: ${{ github.ref_name == 'main' && 'Latest build' || 'Staging build' }} + + steps: + - name: Collect the packages + uses: actions/download-artifact@v7 + with: + path: dist + merge-multiple: true + + # Recreated rather than edited, so the tag follows its branch and no asset + # from an older build is left behind. + - name: Replace the rolling pre-release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + ls -l dist + + gh release delete "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --yes --cleanup-tag || true + gh release create "$RELEASE_TAG" dist/* \ + --repo "$GITHUB_REPOSITORY" \ + --target "$GITHUB_SHA" \ + --prerelease \ + --title "$RELEASE_NAME" \ + --notes "Automatic build of $GITHUB_REF_NAME (${GITHUB_SHA::7}), $(date -u '+%Y-%m-%d %H:%M UTC')." From 5d29d6031db1d450e3805867ea17c56edf1cd2dd Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 25 Aug 2026 23:46:27 +0200 Subject: [PATCH 2/3] Download the packages with download-artifact v8 The packages are uploaded with archive: false, and non-zipped artifacts can only be fetched by v8 of download-artifact - v7 fetched the first one, then failed the run with "Unable to download and extract artifact". --- .github/workflows/dotnet.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index b262753..be232c3 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -127,8 +127,10 @@ jobs: RELEASE_NAME: ${{ github.ref_name == 'main' && 'Latest build' || 'Staging build' }} steps: + # v8, not v7: the packages are uploaded with archive: false, and only v8 + # can pull those non-zipped artifacts back down. - name: Collect the packages - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: dist merge-multiple: true From 20da0a4f77d584d88be7150a5dbb7f7fd9941d62 Mon Sep 17 00:00:00 2001 From: maj00r Date: Tue, 25 Aug 2026 23:50:59 +0200 Subject: [PATCH 3/3] Keep the Windows package zipped on the way to the release download-artifact unpacks a zip it detects, so the Windows package arrived in dist/ as loose files (Starter.exe, the native dlls, startercfg/) and gh tried to upload the startercfg directory as a release asset. skip-decompress leaves the artifact as the file it was uploaded as. The two packages are also named explicitly instead of globbing dist/*, so a missing one fails the step rather than quietly publishing half a release. --- .github/workflows/dotnet.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index be232c3..9ad94e7 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -128,12 +128,15 @@ jobs: steps: # v8, not v7: the packages are uploaded with archive: false, and only v8 - # can pull those non-zipped artifacts back down. + # can pull those non-zipped artifacts back down. skip-decompress keeps the + # Windows .zip a file - unpacked, its contents would end up in the release + # instead of the package. - name: Collect the packages uses: actions/download-artifact@v8 with: path: dist merge-multiple: true + skip-decompress: true # Recreated rather than edited, so the tag follows its branch and no asset # from an older build is left behind. @@ -146,7 +149,9 @@ jobs: ls -l dist gh release delete "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --yes --cleanup-tag || true - gh release create "$RELEASE_TAG" dist/* \ + gh release create "$RELEASE_TAG" \ + dist/StarterNG-windows-x64.zip \ + dist/StarterNG-linux-x64.tar.gz \ --repo "$GITHUB_REPOSITORY" \ --target "$GITHUB_SHA" \ --prerelease \