|
1 | | -# Release — tag-triggered workflow that runs the full packaging matrix |
2 | | -# and assembles a draft GitHub Release with the artifacts attached. |
| 1 | +# Release — produces a draft GitHub Release with every platform's |
| 2 | +# artifact attached. Two entry points, both ending in the same draft: |
3 | 3 | # |
4 | | -# Pushing a release-like tag fires the packaging matrix as a reusable |
5 | | -# workflow, then a release job collects every platform's final artifact |
6 | | -# and creates a draft Release on the repository the tag was pushed to. |
| 4 | +# 1. One-click (workflow_dispatch): a maintainer opens Actions → |
| 5 | +# Release → Run workflow, picks the branch to release from, and |
| 6 | +# types a version (e.g. 3.0.1). The prepare-tag job creates and |
| 7 | +# pushes that tag at the tip of the selected branch if it doesn't |
| 8 | +# already exist, then the full packaging matrix runs and the draft |
| 9 | +# is assembled. No local `git tag` / `git push` needed. Re-running |
| 10 | +# with an existing tag just re-assembles the assets (retry path). |
| 11 | +# |
| 12 | +# 2. Push a release-like tag from a local checkout — same downstream |
| 13 | +# flow, kept for scripted / habitual taggers. |
7 | 14 | # |
8 | 15 | # Tag filter: digit-leading tags (aMule's convention — 2.3.3, 2.4.0-rc1, |
9 | 16 | # 3.0-beta) plus optional v-prefix (v2.4.0) for compatibility. Excludes |
10 | 17 | # branch-backup / topic tags like `backup/*`, `per-peer-cap-fix`, etc. |
11 | 18 | # |
12 | | -# Test on a fork by pushing a tag like `0.0.0-fork-test` — the draft |
13 | | -# Release lands on the fork's Releases page, not upstream. |
| 19 | +# Test on a fork by dispatching (or pushing) a version like |
| 20 | +# `0.0.0-fork-test` — the tag and the draft Release land on the fork, |
| 21 | +# not upstream. |
| 22 | +# |
| 23 | +# Before the draft is created, the release job asserts that all ten |
| 24 | +# expected platform assets are present, so a half-built matrix can never |
| 25 | +# silently produce a draft that's missing a platform. |
14 | 26 | # |
15 | 27 | # Output is always a draft so a maintainer can review the asset list |
16 | 28 | # and edit the auto-generated notes before publishing. |
|
24 | 36 | - 'v[0-9]*' |
25 | 37 | workflow_dispatch: |
26 | 38 | inputs: |
27 | | - tag: |
| 39 | + version: |
28 | 40 | description: >- |
29 | | - Tag to release. Must already exist as a git tag on the repo. |
30 | | - Use this to retry asset assembly without re-tagging. |
| 41 | + Version to release, e.g. 3.0.1 or 3.1.0-rc1. Created and |
| 42 | + pushed as a tag at the tip of the branch selected above if it |
| 43 | + doesn't already exist. Re-run with an existing version to |
| 44 | + retry asset assembly without re-tagging. |
31 | 45 | required: true |
32 | 46 | type: string |
33 | 47 |
|
34 | 48 | permissions: |
35 | 49 | contents: write |
36 | 50 |
|
37 | 51 | jobs: |
| 52 | + # One-click entry point: turn the dispatched version into a real tag |
| 53 | + # so the rest of the pipeline (which keys off git tags for versioning) |
| 54 | + # behaves identically to the push-a-tag path. On the push trigger this |
| 55 | + # job is a trivial no-op — the tag already exists — but it still runs |
| 56 | + # so build/source-bundle can depend on it unconditionally. |
| 57 | + prepare-tag: |
| 58 | + name: Create tag |
| 59 | + runs-on: ubuntu-latest |
| 60 | + steps: |
| 61 | + - uses: actions/checkout@v6 |
| 62 | + with: |
| 63 | + fetch-depth: 0 |
| 64 | + - name: Create and push tag if missing |
| 65 | + if: github.event_name == 'workflow_dispatch' |
| 66 | + env: |
| 67 | + TAG: ${{ inputs.version }} |
| 68 | + run: | |
| 69 | + set -euo pipefail |
| 70 | + if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then |
| 71 | + echo "Tag ${TAG} already exists — re-assembling assets, not re-tagging." |
| 72 | + exit 0 |
| 73 | + fi |
| 74 | + git config user.name "github-actions[bot]" |
| 75 | + git config user.email "github-actions[bot]@users.noreply.github.com" |
| 76 | + # Annotated tag at the dispatched commit (tip of the selected |
| 77 | + # branch). fetch-depth:0 above means git describe in the build |
| 78 | + # jobs resolves this to an exact-match tag, so artifacts are |
| 79 | + # versioned cleanly (aMule-3.0.1-... not aMule-...-gabc123). |
| 80 | + git tag -a "${TAG}" -m "aMule ${TAG}" |
| 81 | + git push origin "refs/tags/${TAG}" |
| 82 | + echo "Created and pushed tag ${TAG} at ${GITHUB_SHA}." |
| 83 | +
|
38 | 84 | build: |
39 | 85 | name: Build artifacts |
| 86 | + needs: prepare-tag |
40 | 87 | uses: ./.github/workflows/packaging.yml |
41 | 88 |
|
42 | 89 | source-bundle: |
43 | 90 | name: Build source bundle with pre-rendered manpages |
| 91 | + needs: prepare-tag |
44 | 92 | runs-on: ubuntu-latest |
45 | 93 | steps: |
46 | 94 | - uses: actions/checkout@v6 |
|
51 | 99 | id: tag |
52 | 100 | run: | |
53 | 101 | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then |
54 | | - echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT" |
| 102 | + echo "tag=${{ inputs.version }}" >> "$GITHUB_OUTPUT" |
55 | 103 | else |
56 | 104 | echo "tag=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" |
57 | 105 | fi |
@@ -156,13 +204,47 @@ jobs: |
156 | 204 | - name: List artifacts |
157 | 205 | run: ls -la dist/ |
158 | 206 |
|
| 207 | + - name: Verify all platform artifacts are present |
| 208 | + # Belt-and-suspenders manifest check. The build matrix uses |
| 209 | + # fail-fast:false and this job `needs: [build, source-bundle]`, |
| 210 | + # so a failed platform already blocks the draft via `needs` — |
| 211 | + # but that surfaces as a generic "dependency failed". This step |
| 212 | + # instead names exactly which asset is missing, and guards |
| 213 | + # against the subtler case where a platform's upload succeeded |
| 214 | + # with an unexpected filename (so the glob below finds nothing). |
| 215 | + run: | |
| 216 | + set -euo pipefail |
| 217 | + fail=0 |
| 218 | + check() { |
| 219 | + local label="$1" expected="$2" glob="$3" n |
| 220 | + # shellcheck disable=SC2086 -- intentional glob expansion |
| 221 | + n=$(ls -1 dist/$glob 2>/dev/null | wc -l | tr -d ' ') |
| 222 | + if [ "$n" -ne "$expected" ]; then |
| 223 | + echo "::error::${label}: expected ${expected}, found ${n} (dist/${glob})" |
| 224 | + fail=1 |
| 225 | + else |
| 226 | + echo "ok: ${label} (${n}/${expected})" |
| 227 | + fi |
| 228 | + } |
| 229 | + check "Linux AppImage (x86_64 + aarch64)" 2 'aMule-*-Linux-*.AppImage' |
| 230 | + check "Linux Flatpak (x86_64 + aarch64)" 2 'aMule-*-Linux-*.flatpak' |
| 231 | + check "macOS Universal2 .dmg" 1 'aMule-*-macOS-universal2.dmg' |
| 232 | + check "Windows portable .zip (x64 + arm64)" 2 'aMule-*-Windows-*.zip' |
| 233 | + check "Windows installer .exe (x64 + arm64)" 2 'aMule-*-Windows-Setup-*.exe' |
| 234 | + check "Source bundle" 1 'aMule-*-src.tar.gz' |
| 235 | + if [ "$fail" -ne 0 ]; then |
| 236 | + echo "::error::Artifact manifest incomplete — refusing to assemble the draft Release." |
| 237 | + exit 1 |
| 238 | + fi |
| 239 | + echo "All 10 expected release artifacts present." |
| 240 | +
|
159 | 241 | - name: Create draft Release |
160 | 242 | env: |
161 | 243 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
162 | 244 | run: | |
163 | 245 | set -euo pipefail |
164 | 246 | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then |
165 | | - TAG="${{ inputs.tag }}" |
| 247 | + TAG="${{ inputs.version }}" |
166 | 248 | else |
167 | 249 | TAG="${{ github.ref_name }}" |
168 | 250 | fi |
|
0 commit comments