mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-17 16:38:54 +00:00
tools/release.sh: fix idempotency gaps in branch checks, SRPM reuse, release notes (#2136)
This commit is contained in:
parent
486f013870
commit
3682077d32
1 changed files with 75 additions and 11 deletions
|
|
@ -35,6 +35,13 @@
|
|||
# safe to re-run after a partial failure. `rpmbuild` is mandatory for
|
||||
# `pre-release` and the script aborts in phase 0 if missing; `rpmlint` is
|
||||
# optional (phase 3 runs it if installed, otherwise skips the lint step).
|
||||
#
|
||||
# Phase 0 also refuses to run `pre-release`/`docs` unless the working tree is
|
||||
# currently on `--branch` (default `main`): committing/pushing from the wrong
|
||||
# branch would otherwise fail silently, since `git push origin main` reports
|
||||
# "Everything up-to-date" when the local `main` ref itself has no new commits,
|
||||
# even if HEAD (on some other branch) does. `afterwork` is exempt since it
|
||||
# explicitly `git checkout`s the branch itself.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
|
@ -101,6 +108,25 @@ confirm() {
|
|||
esac
|
||||
}
|
||||
|
||||
# push_if_needed pushes $1 (a local branch that is presumed checked out, or
|
||||
# at least resolvable) to origin, but skips the confirmation prompt and the
|
||||
# push entirely when origin already has those exact commits -- e.g. a
|
||||
# resumed run whose earlier attempt already pushed successfully. Any
|
||||
# additional args (e.g. -u) are passed through to `git push`.
|
||||
push_if_needed() {
|
||||
local branch="$1"; shift
|
||||
run_cmd git fetch origin "$branch" >/dev/null 2>&1 || true
|
||||
local local_sha remote_sha
|
||||
local_sha="$(git rev-parse "$branch" 2>/dev/null || true)"
|
||||
remote_sha="$(git rev-parse "origin/$branch" 2>/dev/null || true)"
|
||||
if [ -n "$local_sha" ] && [ "$local_sha" = "$remote_sha" ]; then
|
||||
note "origin/$branch already matches local $branch (idempotent: skipping push)"
|
||||
return 0
|
||||
fi
|
||||
confirm "push branch '$branch' to origin?"
|
||||
run_cmd git push "$@" origin "$branch"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Argument parsing
|
||||
# ============================================================================
|
||||
|
|
@ -187,6 +213,20 @@ preflight_common() {
|
|||
die "working tree has uncommitted changes; commit or stash first"
|
||||
fi
|
||||
|
||||
# `afterwork` explicitly checks out $BRANCH itself, so it self-heals from
|
||||
# being invoked on the wrong branch. `pre-release` and `docs` do not: they
|
||||
# commit/push against whatever is currently checked out, so catch a stray
|
||||
# branch here rather than silently committing to it (and having
|
||||
# `git push origin $BRANCH` report "Everything up-to-date" without actually
|
||||
# publishing the new commit).
|
||||
if [ "$SUBCOMMAND" != "afterwork" ]; then
|
||||
local current_branch
|
||||
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||
if [ "$current_branch" != "$BRANCH" ]; then
|
||||
die "currently on branch '$current_branch', expected '$BRANCH' -- 'git checkout $BRANCH' first, or pass --branch '$current_branch' if that's intentional"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "preflight (common) ok"
|
||||
}
|
||||
|
||||
|
|
@ -382,8 +422,7 @@ phase_1_bump_versions() {
|
|||
run_cmd git commit -m "Prepare ${VERSION} release"
|
||||
fi
|
||||
|
||||
confirm "push branch '$BRANCH' to origin?"
|
||||
run_cmd git push origin "$BRANCH"
|
||||
push_if_needed "$BRANCH"
|
||||
|
||||
log "phase 1 complete"
|
||||
}
|
||||
|
|
@ -434,13 +473,30 @@ phase_3_srpm() {
|
|||
run_cmd mkdir -p "$rpm_top/SPECS" "$rpm_top/SOURCES" "$rpm_top/SRPMS"
|
||||
|
||||
# Check for an already-built SRPM for this version before rebuilding.
|
||||
# Reuse is content-aware, not just version-keyed: a same-named SRPM from
|
||||
# an earlier aborted run could have been built from a since-superseded
|
||||
# tarball (e.g. a bugfix rebuild before the tag was ever pushed), so we
|
||||
# only reuse it if its recorded source-tarball sha256 still matches the
|
||||
# tarball on disk right now.
|
||||
local tgz_sha=""
|
||||
if [ "$DRY_RUN" != "yes" ]; then
|
||||
tgz_sha="$(shasum -a 256 "$tgz" | awk '{ print $1 }')"
|
||||
fi
|
||||
|
||||
local existing
|
||||
existing="$(ls -1 "$rpm_top"/SRPMS/miller-"${VERSION}"-1*.src.rpm 2>/dev/null | head -n1 || true)"
|
||||
if [ -n "$existing" ]; then
|
||||
note "existing SRPM found (idempotent: reusing): $existing"
|
||||
SRPM_PATH="$existing"
|
||||
log "phase 3 complete"
|
||||
return 0
|
||||
local sha_marker="${existing}.srctarball.sha256"
|
||||
local recorded_sha=""
|
||||
[ -f "$sha_marker" ] && recorded_sha="$(cat "$sha_marker")"
|
||||
if [ -n "$tgz_sha" ] && [ "$recorded_sha" = "$tgz_sha" ]; then
|
||||
note "existing SRPM found and matches current $tgz sha256 (idempotent: reusing): $existing"
|
||||
SRPM_PATH="$existing"
|
||||
log "phase 3 complete"
|
||||
return 0
|
||||
else
|
||||
warn "existing SRPM found but its recorded source tarball sha256 doesn't match current $tgz -- rebuilding: $existing"
|
||||
fi
|
||||
fi
|
||||
|
||||
run_cmd cp miller.spec "$rpm_top/SPECS/"
|
||||
|
|
@ -466,6 +522,7 @@ phase_3_srpm() {
|
|||
else
|
||||
SRPM_PATH="$(ls -1 "$rpm_top"/SRPMS/miller-"${VERSION}"-1*.src.rpm | head -n1)"
|
||||
[ -n "$SRPM_PATH" ] && [ -f "$SRPM_PATH" ] || die "rpmbuild reported success but no SRPM found under $rpm_top/SRPMS"
|
||||
echo "$tgz_sha" > "${SRPM_PATH}.srctarball.sha256"
|
||||
log "built SRPM: $SRPM_PATH"
|
||||
fi
|
||||
|
||||
|
|
@ -483,7 +540,16 @@ phase_4_github_release() {
|
|||
[ -n "${SRPM_PATH:-}" ] || die "SRPM_PATH not set -- phase 3 did not run?"
|
||||
|
||||
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||
note "gh release $TAG already exists (idempotent: will only upload missing assets)"
|
||||
note "gh release $TAG already exists"
|
||||
if [ -n "$NOTES_FILE" ]; then
|
||||
# Resuming with a (possibly edited) --notes-file should still take
|
||||
# effect -- otherwise editing NOTES_FILE and re-running silently
|
||||
# leaves the already-created release's notes frozen at first creation.
|
||||
log "updating existing release notes from '$NOTES_FILE'"
|
||||
run_cmd gh release edit "$TAG" --notes-file "$NOTES_FILE"
|
||||
else
|
||||
note "no --notes-file given; leaving existing release notes/title untouched"
|
||||
fi
|
||||
else
|
||||
[ -n "$NOTES_FILE" ] || die "gh release $TAG does not exist and --notes-file was not provided"
|
||||
confirm "create GitHub pre-release $TAG from notes file '$NOTES_FILE'?"
|
||||
|
|
@ -567,8 +633,7 @@ phase_5_docs_branch() {
|
|||
run_cmd git commit -m "docs: Miller $VERSION Documentation site_name"
|
||||
fi
|
||||
|
||||
confirm "push branch '$VERSION' to origin?"
|
||||
run_cmd git push -u origin "$VERSION"
|
||||
push_if_needed "$VERSION" -u
|
||||
|
||||
banner "PHASE 5: manual ReadTheDocs steps"
|
||||
cat <<EOF
|
||||
|
|
@ -626,8 +691,7 @@ phase_6_afterwork_bump() {
|
|||
run_cmd git commit -m "Post-${VERSION} release: back to ${target}"
|
||||
fi
|
||||
|
||||
confirm "push branch '$BRANCH' to origin?"
|
||||
run_cmd git push origin "$BRANCH"
|
||||
push_if_needed "$BRANCH"
|
||||
fi
|
||||
|
||||
log "phase 6 complete"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue