From 584b74d288cad7ab25736fb2a034629988be4aa8 Mon Sep 17 00:00:00 2001 From: yohimik Date: Fri, 28 Aug 2026 19:57:53 +0400 Subject: [PATCH 1/3] ci: publish releases automatically on a tag push The Linux, macOS and Windows workflows already build every file a release needs, but nothing published them: the artifacts were downloaded from the Actions run and attached to a hand-made GitHub Release. Add a Release workflow that runs on a v* tag. It calls the three build workflows, so everything that ships is also tested in the same run, then collects their artifacts into a draft release. It first checks the tag against goenv/version.go, because the release filenames are derived from that constant rather than from the tag, and it checks all nine files arrived so a build that stopped uploading cannot produce a half-complete release. The three build workflows gain a workflow_call trigger. Their concurrency groups had to stop using github.workflow: inside a called workflow that context is the caller's, so all three would resolve to the same group and, with cancel-in-progress, cancel each other and the caller. The literals used instead evaluate to the same string as before for pull request and push runs. Co-Authored-By: Claude Opus 5 --- .github/workflows/build-macos.yml | 3 +- .github/workflows/linux.yml | 3 +- .github/workflows/release.yml | 108 ++++++++++++++++++++++++++++++ .github/workflows/windows.yml | 3 +- BUILDING.md | 28 ++++++++ 5 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index c1a263c475..44c12301d5 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -6,9 +6,10 @@ on: branches: - dev - release + workflow_call: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: macos-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ce3f0acb49..095fde1924 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -6,9 +6,10 @@ on: branches: - dev - release + workflow_call: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: linux-${{ github.ref }} cancel-in-progress: true jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..66806fa669 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,108 @@ +# Build and publish a GitHub Release when a version tag is pushed. +# +# This workflow does not build anything itself: it calls the regular Linux, +# macOS and Windows workflows, which already produce every file a release +# needs, and then collects their artifacts into a single draft release. +# +# The release is created as a draft on purpose, so the release notes can be +# reviewed (and the CHANGELOG.md entry pasted in) before publishing. +name: Release + +on: + push: + tags: + - 'v*' + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + check-version: + # The release filenames are derived from goenv/version.go, not from the + # tag, so check they agree before spending an hour building everything. + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Check the tag matches goenv/version.go + run: | + version=$(./.github/workflows/tinygo-extract-version.sh | cut -d= -f2-) + if [ "v$version" != "$GITHUB_REF_NAME" ]; then + echo "::error::tag $GITHUB_REF_NAME does not match version $version in goenv/version.go" + exit 1 + fi + + linux: + needs: check-version + uses: ./.github/workflows/linux.yml + macos: + needs: check-version + uses: ./.github/workflows/build-macos.yml + windows: + needs: check-version + uses: ./.github/workflows/windows.yml + + release: + needs: [linux, macos, windows] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Download all release artifacts + # Every build job uploads with `archive: false`, which names the + # artifact after the file, so this leaves the release files (and + # nothing else) directly in dist/. + uses: actions/download-artifact@v8 + with: + path: dist + merge-multiple: true + - name: Check all release files are present + # A build that silently stopped uploading must not result in a + # half-complete release, so list what is expected explicitly. + run: | + version=$(./.github/workflows/tinygo-extract-version.sh | cut -d= -f2-) + missing=0 + for file in \ + "tinygo$version.linux-amd64.tar.gz" "tinygo_${version}_amd64.deb" \ + "tinygo$version.linux-arm.tar.gz" "tinygo_${version}_armhf.deb" \ + "tinygo$version.linux-arm64.tar.gz" "tinygo_${version}_arm64.deb" \ + "tinygo$version.darwin-amd64.tar.gz" \ + "tinygo$version.darwin-arm64.tar.gz" \ + "tinygo$version.windows-amd64.zip"; do + if [ ! -f "dist/$file" ]; then + echo "::error::missing release file $file" + missing=1 + fi + done + ls -l dist + exit $missing + - name: Create the draft release + env: + GH_TOKEN: ${{ github.token }} + run: | + # GitHub computes a SHA-256 digest for every asset it stores, but + # only exposes it through the API, so say how to read it. These notes + # are prepended to the notes GitHub generates from the commit log. + notes=$(cat < Date: Fri, 28 Aug 2026 20:14:26 +0400 Subject: [PATCH 2/3] ci: don't save LLVM caches on tag runs Caches are scoped to the ref that created them. A run triggered by a tag can restore caches from the default branch, but anything it saves lands in a scope named after that one tag, which no later run can ever read: not another tag, not the default branch. So on a release run the two LLVM caches are written once and never used again, while still counting against the repository's 10 GB quota. Since eviction is by least recent access, a release that missed the cache could push out the default-branch entries that every other run depends on. Restore still happens on tag runs, which is where the benefit is. Only the save is skipped. Co-Authored-By: Claude Opus 5 --- .github/workflows/build-macos.yml | 4 ++-- .github/workflows/linux.yml | 12 ++++++------ .github/workflows/windows.yml | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 44c12301d5..ec97fd186a 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -59,7 +59,7 @@ jobs: run: make llvm-source - name: Save LLVM source cache uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' + if: steps.cache-llvm-source.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} path: | @@ -87,7 +87,7 @@ jobs: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - name: Save LLVM build cache uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' + if: steps.cache-llvm-build.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} path: llvm-build diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 095fde1924..dd221c97d3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -79,7 +79,7 @@ jobs: run: make llvm-source - name: Save LLVM source cache uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' + if: steps.cache-llvm-source.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} path: | @@ -108,7 +108,7 @@ jobs: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - name: Save LLVM build cache uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' + if: steps.cache-llvm-build.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} path: llvm-build @@ -281,7 +281,7 @@ jobs: run: make llvm-source - name: Save LLVM source cache uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' + if: steps.cache-llvm-source.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} path: | @@ -308,7 +308,7 @@ jobs: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - name: Save LLVM build cache uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' + if: steps.cache-llvm-build.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} path: llvm-build @@ -390,7 +390,7 @@ jobs: run: make llvm-source - name: Save LLVM source cache uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' + if: steps.cache-llvm-source.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} path: | @@ -419,7 +419,7 @@ jobs: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - name: Save LLVM build cache uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' + if: steps.cache-llvm-build.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} path: llvm-build diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5f8559917d..a77d646d1e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -53,7 +53,7 @@ jobs: run: make llvm-source - name: Save cached LLVM source uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' + if: steps.cache-llvm-source.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} path: | @@ -81,7 +81,7 @@ jobs: find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - name: Save cached LLVM build uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' + if: steps.cache-llvm-build.outputs.cache-hit != 'true' && github.ref_type != 'tag' with: key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} path: llvm-build From 578078d5814f315d261d4cb6e2f3a0a603159a74 Mon Sep 17 00:00:00 2001 From: yohimik Date: Sat, 29 Aug 2026 00:24:57 +0400 Subject: [PATCH 3/3] ci: keep the Windows release file intact when collecting artifacts The build jobs upload with archive: false, which stores the file as-is rather than wrapping it in a zip. The Windows release is itself a .zip, so on download it was detected as an archive and unpacked: dist/ ended up with a tinygo/ directory instead of tinygo.windows-amd64.zip, and the release would have shipped without a Windows build. Set skip-decompress so the file is kept as it was uploaded. The unpacking cannot be turned off at the upload side, because the Windows test jobs download that same artifact and rely on getting it unpacked. Co-Authored-By: Claude Opus 5 --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 66806fa669..f1a50aceef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,11 +54,14 @@ jobs: - name: Download all release artifacts # Every build job uploads with `archive: false`, which names the # artifact after the file, so this leaves the release files (and - # nothing else) directly in dist/. + # nothing else) directly in dist/. skip-decompress matters for the + # Windows release, which is itself a .zip: without it the download + # would helpfully unpack the release instead of keeping the file. uses: actions/download-artifact@v8 with: path: dist merge-multiple: true + skip-decompress: true - name: Check all release files are present # A build that silently stopped uploading must not result in a # half-complete release, so list what is expected explicitly.