Update the release pipeline to early-exit on non-Release branches.
This commit is contained in:
parent
2971b6e709
commit
03d0b6a50f
2 changed files with 113 additions and 4 deletions
|
|
@ -99,6 +99,10 @@ jobs:
|
|||
# for before it will run.
|
||||
# libcap2-bin provides capsh, which the Test step uses to drop the two
|
||||
# DAC capabilities so root obeys permission bits.
|
||||
# jq parses the tags API for the releasable check below. Named rather
|
||||
# than assumed: the release job gets it from its image, and a base
|
||||
# image change that dropped it would turn that check into a silent
|
||||
# pass rather than a failure.
|
||||
# zsync and appstream are for build-appimage.sh: appimagetool shells
|
||||
# out to zsyncmake rather than bundling it, and reports success while
|
||||
# writing nothing when it is absent, so the script checks for it up
|
||||
|
|
@ -107,7 +111,44 @@ jobs:
|
|||
apt-get install -y --no-install-recommends \
|
||||
build-essential perl pkg-config \
|
||||
binutils dpkg-dev desktop-file-utils gzip libcap2-bin \
|
||||
zsync appstream
|
||||
zsync appstream jq
|
||||
|
||||
- name: Check the release tag is free
|
||||
# The other half of the guard above, for the path that actually cuts
|
||||
# most releases: pushing a Release* branch. There the tag comes from
|
||||
# [workspace.package] rather than the ref, so the mistake is not a
|
||||
# mismatched tag but a *forgotten bump* — the version still points at a
|
||||
# release that already shipped.
|
||||
#
|
||||
# The release job checks this too and remains the authority; it just
|
||||
# cannot check it until both build jobs are green, so forgetting the
|
||||
# bump used to cost two full release builds, packaging and an artifact
|
||||
# upload before anything said so. This says so in seconds.
|
||||
#
|
||||
# Deliberately as lenient as the release job: a tag at *this* commit is
|
||||
# a re-run and fine, and an unreachable API reads as "not found" and
|
||||
# lets the build proceed rather than failing on a network hiccup. An
|
||||
# early check that blocks a good release is worse than one that misses
|
||||
# a bad one, because the late check still catches it.
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
version=$(sed -n '/^\[workspace\.package\]/,/^\[/{ s/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p }' Cargo.toml)
|
||||
[ -n "$version" ] || { echo "could not read the version from Cargo.toml" >&2; exit 1; }
|
||||
tag="v$version"
|
||||
api="${GITHUB_API_URL:-$GITHUB_SERVER_URL/api/v1}"
|
||||
at=$(curl -sS -H "Authorization: token $RELEASE_TOKEN" \
|
||||
"$api/repos/$GITHUB_REPOSITORY/tags/$tag" | jq -r '.commit.sha // empty')
|
||||
if [ -n "$at" ] && [ "$at" != "$GITHUB_SHA" ]; then
|
||||
echo "ERROR: $tag already exists at $at, not $GITHUB_SHA." >&2
|
||||
echo "Bump [workspace.package] version in Cargo.toml, refresh Cargo.lock" >&2
|
||||
echo "with 'cargo update -w', commit and push again." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $tag is free (or already at this commit)"
|
||||
|
||||
- name: Trust the workspace
|
||||
# checkout writes as root into a directory git then considers dubiously
|
||||
|
|
@ -168,7 +209,23 @@ jobs:
|
|||
echo "capabilities in test shell: $(grep CapEff /proc/self/status | tr -d "\t")"
|
||||
cargo test --release --locked --workspace'
|
||||
|
||||
# Everything from here down produces release assets, so it runs only
|
||||
# where a release can actually come out: a v* tag or a Release* branch.
|
||||
# On master and pull requests the job stops after Build and Test, which
|
||||
# is what those runs are for — the packaging that used to follow built a
|
||||
# .deb, an AppImage, a tarball and a 14-day artifact upload that nothing
|
||||
# would ever download, because the release job is skipped there anyway.
|
||||
#
|
||||
# The condition is repeated rather than hoisted into an env var: Actions
|
||||
# has no workflow-level expression alias, YAML anchors are not supported,
|
||||
# and `env.X` inside `if:` would fail *closed* on a runner that did not
|
||||
# populate it — silently skipping packaging on a real release. Spelled
|
||||
# out, it is the same form the release job's own gate uses.
|
||||
- name: Build the .deb
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
# --no-build reuses the binaries from the Build step rather than
|
||||
# recompiling. SOURCE_DATE_EPOCH pins the generated changelog date so
|
||||
# repeat builds of the same commit are byte-identical.
|
||||
|
|
@ -177,8 +234,14 @@ jobs:
|
|||
./packaging/build-deb.sh --no-build
|
||||
|
||||
- name: Check the glibc floor
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
# The whole point of pinning the container. If someone bumps the image,
|
||||
# this fails loudly instead of quietly shipping an uninstallable package.
|
||||
# Gated with the .deb it inspects — there is no package to read without
|
||||
# the step above.
|
||||
run: |
|
||||
deb=$(ls dist/*.deb)
|
||||
depends=$(dpkg-deb -f "$deb" Depends)
|
||||
|
|
@ -194,6 +257,10 @@ jobs:
|
|||
echo "OK: glibc floor $floor <= $MAX_GLIBC"
|
||||
|
||||
- name: Build the AppImage
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
# After the glibc gate, so the cheaper check still fails first. --no-build
|
||||
# reuses the binaries from the Build step, as the .deb step does, and
|
||||
# SOURCE_DATE_EPOCH pins the date substituted into the AppStream release
|
||||
|
|
@ -207,6 +274,10 @@ jobs:
|
|||
./packaging/build-appimage.sh --no-build
|
||||
|
||||
- name: Package the binaries
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
# A tarball for anyone not installing the .deb, stripped to match what
|
||||
# build-deb.sh ships.
|
||||
run: |
|
||||
|
|
@ -231,6 +302,12 @@ jobs:
|
|||
# node16, which current Forgejo runner images no longer ship; the
|
||||
# -node20 tags are Forgejo's builds for precisely this combination.
|
||||
- uses: actions/upload-artifact@v3-node20
|
||||
# `if-no-files-found: error` below would fail every master push once the
|
||||
# packaging steps above are gated, so this carries the same gate.
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
with:
|
||||
name: linux-x86_64
|
||||
# The .zsync is not optional: the update URL baked into every AppImage
|
||||
|
|
@ -322,7 +399,16 @@ jobs:
|
|||
fi
|
||||
done
|
||||
|
||||
# As in the linux job, the asset-producing steps run only where a release
|
||||
# can come out. The DLL check above deliberately stays ungated: it
|
||||
# validates the .exe itself rather than packaging it, costs an objdump,
|
||||
# and is exactly the kind of regression worth catching on master rather
|
||||
# than at release time.
|
||||
- name: Build the installer
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
# --no-build reuses the binaries from the Build step rather than
|
||||
# cross-compiling them a second time. The installer and the .zip below
|
||||
# are alternatives, not a two-step download: the installer puts the app
|
||||
|
|
@ -332,6 +418,10 @@ jobs:
|
|||
run: ./packaging/build-installer.sh --no-build
|
||||
|
||||
- name: Package the binaries
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
run: |
|
||||
version=$(sed -n '/^\[workspace\.package\]/,/^\[/{ s/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p }' Cargo.toml)
|
||||
[ -n "$version" ] || { echo "could not read the version from Cargo.toml" >&2; exit 1; }
|
||||
|
|
@ -354,6 +444,12 @@ jobs:
|
|||
# node16, which current Forgejo runner images no longer ship; the
|
||||
# -node20 tags are Forgejo's builds for precisely this combination.
|
||||
- uses: actions/upload-artifact@v3-node20
|
||||
# Same gate as the packaging steps, for the same `if-no-files-found`
|
||||
# reason as the linux job.
|
||||
if: >-
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| startsWith(github.ref, 'refs/heads/Release')
|
||||
|| startsWith(github.ref, 'refs/heads/release')
|
||||
with:
|
||||
name: windows-x86_64
|
||||
path: |
|
||||
|
|
|
|||
19
README.md
19
README.md
|
|
@ -695,13 +695,26 @@ microseconds regardless of row count.
|
|||
allocation counts, measure that separately before concluding a path is cheap,
|
||||
and measure the GUI rather than a one-shot `quicksearch-cli` run — a
|
||||
short-lived process cannot show what a typing session retains.
|
||||
- `.forgejo/workflows/ci.yml`: builds both platforms on every push to `master`
|
||||
and every pull request. To cut a release, bump `[workspace.package] version`
|
||||
- `.forgejo/workflows/ci.yml`: builds and tests both platforms on every push to
|
||||
`master` and every pull request. Those runs stop there — packaging (the
|
||||
`.deb`, the AppImage, the tarball, the Windows installer and `.zip`, and the
|
||||
artifact upload) runs only where a release can actually come out, which is a
|
||||
`v*` tag or a `Release...` branch. The Windows non-system-DLL check is
|
||||
deliberately not gated that way: it validates the `.exe` rather than
|
||||
packaging it, so it runs everywhere and catches a regression on `master`
|
||||
rather than at release time.
|
||||
|
||||
To cut a release, bump `[workspace.package] version`
|
||||
in `Cargo.toml` and push the commit on a branch named `Release...`; CI runs
|
||||
`cargo update -w` first, so a lockfile still pinning the old member versions
|
||||
is not something you have to remember. That only re-resolves the workspace
|
||||
crates, so the `--locked` build after it still fails on a dependency added or
|
||||
bumped without committing `Cargo.lock`. Once both
|
||||
bumped without committing `Cargo.lock`. Forgetting the version bump is caught
|
||||
in seconds rather than after two full release builds: each release path gets
|
||||
a guard before anything is compiled — a `v*` tag is checked against the
|
||||
workspace version, and a `Release...` branch is checked against the tags that
|
||||
already exist. Both are accelerators, not the authority; the release job
|
||||
re-checks and remains the thing that actually refuses. Once both
|
||||
build jobs are green, CI tags that commit `v<version>` and publishes a release
|
||||
with the `.deb`, an AppImage and its `.zsync` sidecar, a Linux tarball, the
|
||||
Windows installer and a Windows zip attached; pushing a `v*` tag by hand does
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue