mirror of
https://github.com/cozystack/cozystack.git
synced 2026-10-23 03:27:50 +09:00
The existing suite asserts on workflow YAML. That catches a rename or a deleted `if:` and nothing else: it cannot tell whether the shell inside a `run:` block does what the step name claims, and it stayed green while the logic underneath was wrong — twice, in the two preceding rounds. Add hack/release-changelog-behaviour.bats, which runs things for real. The GitHub Actions semantics the whole non-blocking design rests on are now verified by executing them. hack/testdata/needs-semantics.yaml mirrors the promote / changelog / open-pr shapes with a fatal (non-AI) step in the changelog job, and `act` runs it. Result: open-pr DOES run when a job it needs has failed, `needs.changelog.result` is `failure`, and — the part that could not be read off the YAML — a failed job's outputs come back EMPTY, while a successful job's propagate. That empty-output behaviour is precisely why open-pr re-establishes changelog presence from disk instead of trusting needs.changelog.outputs. Deleting the `if:` from the fixture makes open-pr get skipped, so the gate is demonstrably load-bearing. The git logic moves into hack/changelog-preserve.sh and is exercised against real bare remotes: a branch that exists, one that does not, one with no changelog, one with an empty changelog, and the full preserve/checkout -B/restore/force-push cycle promote actually performs. The last of these directly reproduces the regression the preserve step exists to prevent. An empty changelog is deliberately NOT preserved — it would suppress generation and then publish as blank notes. finalize's release-body selection runs under node across all three inputs (good, whitespace-only, absent) and the resulting decision table is compared as a whole. Replace the shell overlay with a sparse second actions/checkout of the dispatch ref at .release-tooling. The overlay had a bootstrap problem I introduced: the script that fetches missing tooling was itself missing from the rc tree, so it could never have worked for exactly the trees that need it. actions/checkout comes from the runner, not the repo, so it has no such circularity. .release-tooling is gitignored because promote runs `git add -A`, and the contract suite pins that. All 21 tests mutation-checked. Breaking changelog-preserve.sh, making it return an empty file, removing the fixture's `if:`, dropping the tooling checkout, and renaming its path each fail the suite and pass on restore. Not covered, and stated in the suite header rather than implied: the AI generation step, the real GitHub API calls, and whether the sparse checkout resolves on GitHub's runners. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
60 lines
2.4 KiB
Bash
Executable File
60 lines
2.4 KiB
Bash
Executable File
#!/bin/sh
|
|
# Usage: changelog-preserve.sh <staging-branch> <version> <outfile>
|
|
#
|
|
# Rescue an existing changelog from a staging branch that is about to be rebuilt.
|
|
#
|
|
# promote-rc.yaml reconstructs release-X.Y.Z from the rc tree on every dispatch
|
|
# and force-pushes it. Anything committed onto that branch in the meantime is
|
|
# discarded — including the two things most worth keeping: a changelog a
|
|
# maintainer wrote by hand (the documented recovery when generation fails) and an
|
|
# earlier dispatch's reviewed output. Losing either to a retry silently replaces
|
|
# human-edited release notes with a fresh AI run, or with nothing at all.
|
|
#
|
|
# Run BEFORE `git checkout -B <staging-branch>`, from inside the repo, with
|
|
# `origin` configured. On success the changelog is written to <outfile> for the
|
|
# caller to restore after the reset.
|
|
#
|
|
# Best-effort by contract: a missing branch, a branch without a changelog, and an
|
|
# unreachable remote are all "nothing to preserve" (exit 1), never a hard error.
|
|
# The caller is mid-promotion and must not be wedged by this.
|
|
#
|
|
# Exit 0 = preserved, <outfile> written. Exit 1 = nothing to preserve.
|
|
|
|
set -eu
|
|
|
|
BRANCH="${1:?usage: changelog-preserve.sh <staging-branch> <version> <outfile>}"
|
|
VERSION="${2:?usage: changelog-preserve.sh <staging-branch> <version> <outfile>}"
|
|
OUTFILE="${3:?usage: changelog-preserve.sh <staging-branch> <version> <outfile>}"
|
|
|
|
CL="docs/changelogs/v${VERSION}.md"
|
|
|
|
# `git fetch <branch>` fails when the branch does not exist on the remote, which
|
|
# is the ordinary first-promotion case — not an error worth reporting loudly.
|
|
if ! git fetch -q origin "$BRANCH" 2>/dev/null; then
|
|
echo "No remote branch ${BRANCH}; nothing to preserve." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! git cat-file -e "FETCH_HEAD:${CL}" 2>/dev/null; then
|
|
echo "Branch ${BRANCH} carries no ${CL}; nothing to preserve." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# An empty file on the branch is not worth carrying over: it would suppress
|
|
# generation (the caller treats a present file as authoritative) while being
|
|
# useless as release notes. Let it be rebuilt instead.
|
|
if [ ! -s "$OUTFILE" ] && ! git show "FETCH_HEAD:${CL}" > "$OUTFILE" 2>/dev/null; then
|
|
echo "Could not read ${CL} from ${BRANCH}; nothing to preserve." >&2
|
|
rm -f "$OUTFILE"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -s "$OUTFILE" ]; then
|
|
echo "${CL} on ${BRANCH} is empty; nothing to preserve." >&2
|
|
rm -f "$OUTFILE"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Preserved ${CL} from ${BRANCH} ($(wc -l < "$OUTFILE" | tr -d ' ') lines)." >&2
|
|
exit 0
|