|
4 | 4 | branches: |
5 | 5 | - master |
6 | 6 | - main |
| 7 | + workflow_dispatch: |
7 | 8 | jobs: |
| 9 | + # Decide whether there is anything to release, from the actual signal: the crate's version is not |
| 10 | + # on crates.io yet. Every push to master used to run the full `make ci` here and then ask the |
| 11 | + # `release` environment for approval, whether or not the commit changed the version. |
| 12 | + # |
| 13 | + # This deliberately does NOT key off the commit message. A guard requiring the head commit to |
| 14 | + # start with "release:" is silently defeated by squash-merging a release PR: the squash replaces |
| 15 | + # the commit message with the PR title, the prefix is lost, and the workflow skips while still |
| 16 | + # reporting success. A version that is not on the index is the condition that actually means |
| 17 | + # "publish", and no merge strategy can lose it. |
| 18 | + decide: |
| 19 | + # Skip the whole workflow on forks: a contributor pushing to their own master has no `release` |
| 20 | + # environment and no publish credentials, so the run could only fail or hang for them. |
| 21 | + if: github.repository == 'jaemk/self_update' |
| 22 | + runs-on: ubuntu-latest |
| 23 | + outputs: |
| 24 | + should_publish: ${{ steps.decide.outputs.should_publish }} |
| 25 | + version: ${{ steps.decide.outputs.version }} |
| 26 | + steps: |
| 27 | + - uses: actions/checkout@v6 |
| 28 | + - name: Check whether the crate version is already published |
| 29 | + id: decide |
| 30 | + run: | |
| 31 | + set -euo pipefail |
| 32 | + version=$(cargo metadata --no-deps --format-version 1 \ |
| 33 | + | jq -r '.packages[] | select(.name == "self_update") | .version') |
| 34 | + if [ -z "$version" ]; then |
| 35 | + echo "Could not determine the crate version." >&2 |
| 36 | + exit 1 |
| 37 | + fi |
| 38 | + echo "version=$version" >> "$GITHUB_OUTPUT" |
| 39 | +
|
| 40 | + # A network or API failure must fail loudly rather than silently resolving to "nothing to |
| 41 | + # publish", which would skip a real release. |
| 42 | + body=$(curl -sS --fail-with-body \ |
| 43 | + -H "User-Agent: jaemk-self_update-release-workflow" \ |
| 44 | + "https://crates.io/api/v1/crates/self_update/versions") |
| 45 | +
|
| 46 | + if jq -e --arg v "$version" '.versions[] | select(.num == $v)' >/dev/null <<<"$body"; then |
| 47 | + echo "self_update $version is already on crates.io - nothing to release." |
| 48 | + echo "should_publish=false" >> "$GITHUB_OUTPUT" |
| 49 | + else |
| 50 | + echo "self_update $version is not on crates.io - releasing." |
| 51 | + echo "should_publish=true" >> "$GITHUB_OUTPUT" |
| 52 | + fi |
| 53 | +
|
8 | 54 | check: |
| 55 | + needs: decide |
| 56 | + # Only spend a full CI run when something is actually going out. `build.yml` already ran the |
| 57 | + # same pipeline on the PR, but a release is the one place worth re-proving it against the |
| 58 | + # merged tree. |
| 59 | + if: github.event_name == 'workflow_dispatch' || needs.decide.outputs.should_publish == 'true' |
9 | 60 | runs-on: ubuntu-latest |
10 | 61 | steps: |
11 | 62 | - uses: actions/checkout@v4 |
|
17 | 68 | - run: make ci |
18 | 69 |
|
19 | 70 | publish: |
20 | | - needs: check |
| 71 | + needs: [decide, check] |
| 72 | + # Only request release-environment approval when there is actually a new version to publish (or |
| 73 | + # on a manual dispatch, the escape hatch for re-running a release whose earlier attempt failed |
| 74 | + # partway). |
| 75 | + if: github.event_name == 'workflow_dispatch' || needs.decide.outputs.should_publish == 'true' |
21 | 76 | runs-on: ubuntu-latest |
22 | 77 | environment: release # Optional: for enhanced security |
23 | 78 | permissions: |
|
0 commit comments