Skip to content

Commit 7d2bb14

Browse files
authored
trigger the publish workflow on an unpublished version (#212)
`release.yml` ran on every push to `master`: a full `make ci` (duplicating `build.yml`, which already ran the same pipeline on the PR) followed by a `release` environment approval request, whether or not the commit changed the version. The workflow now asks crates.io whether the current version is published and does nothing when it is. A commit-message gate ("release: ...") was considered and rejected: squash merges replace the message with the PR title, which drops the prefix and makes the workflow skip a real release while reporting success. The version not being on the index cannot be lost by a merge strategy. Also skip the workflow on forks, where it can only fail for lack of publish credentials (reported in #147), and add `workflow_dispatch` to re-run a release that failed partway.
1 parent 9558822 commit 7d2bb14

2 files changed

Lines changed: 61 additions & 1 deletion

File tree

.github/workflows/release.yml

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,59 @@ on:
44
branches:
55
- master
66
- main
7+
workflow_dispatch:
78
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+
854
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'
960
runs-on: ubuntu-latest
1061
steps:
1162
- uses: actions/checkout@v4
@@ -17,7 +68,11 @@ jobs:
1768
- run: make ci
1869

1970
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'
2176
runs-on: ubuntu-latest
2277
environment: release # Optional: for enhanced security
2378
permissions:

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
so it cannot leak into logs.
2121

2222
### Changed
23+
- The publish workflow now runs only when the crate version is not yet on crates.io, instead of on
24+
every push to `master`. A push that does not bump the version no longer spends a full CI run in
25+
`release.yml` (`build.yml` already covers it) and no longer requests `release`-environment
26+
approval. The workflow also skips itself on forks, where it could only fail for lack of publish
27+
credentials, and gains a `workflow_dispatch` trigger for re-running a release that failed partway.
2328

2429
### Fixed
2530
- `Extract::extract_file` (and so `bin_path_in_archive` on the update path) now finds an entry

0 commit comments

Comments
 (0)