Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions .github/workflows/promote-branches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ jobs:
FROM: ${{ github.event.inputs.from_module }}
run: |
set -euo pipefail
# Make current remote alias tips available so we only move a learner branch when
# its content actually changed -- avoids gratuitously rewriting unchanged public
# start-of-module-* branches to new commit shas on every promotion.
# No "|| true": a wildcard that matches nothing (e.g. the first-ever promotion,
# before any start branch exists) already exits 0, so the only thing || true would
# hide is a real failure (network/auth/remote down). Swallowing that would leave
# cur_tree empty for every branch and force-push ALL of them, so let it abort.
git fetch --no-tags origin "refs/heads/start-of-module-*:refs/remotes/origin/start-of-module-*"
REFSPECS=()
for start in $(node -e "console.log(JSON.parse(process.env.BRANCHES).join(' '))"); do
# start = start-of-module-NN ; module = NN-1
Expand All @@ -92,23 +100,43 @@ jobs:
fi
staged="regen/${DID}/${start}"
sha="$(git rev-parse "$staged")"
# Mutable alias (force — it moves) + immutable version tag (must NOT already exist).
staged_tree="$(git rev-parse "${staged}^{tree}")"

# Mutable alias: move it only when the built content differs from what the branch
# already points at (or the branch does not exist yet). Force, because promotion
# rebuilds commits (new commit shas even for identical trees).
cur_tree="$(git rev-parse --verify --quiet "refs/remotes/origin/${start}^{tree}" 2>/dev/null || true)"
if [ "$cur_tree" != "$staged_tree" ]; then
REFSPECS+=("+${sha}:refs/heads/${start}")
echo "alias ${start}: content changed -> ${staged_tree:0:12} (move)"
else
echo "alias ${start}: unchanged (${staged_tree:0:12}); leaving in place."
fi

# Immutable version tag: cut exactly once. Never overwrite an existing tag (that
# is what "immutable" means); skip it so re-running promotion is idempotent
# instead of aborting the whole job.
tag="acc-${VERSION}/${start}"
if git rev-parse --verify --quiet "refs/tags/${tag}" >/dev/null; then
echo "ERROR: immutable tag ${tag} already exists; refusing to overwrite." >&2
exit 1
echo "tag ${tag}: already exists; leaving unchanged."
else
REFSPECS+=("${sha}:refs/tags/${tag}")
echo "tag ${tag}: creating at ${sha:0:12}."
fi
REFSPECS+=("+${sha}:refs/heads/${start}")
REFSPECS+=("${sha}:refs/tags/${tag}")
done
if [ "${#REFSPECS[@]}" -eq 0 ]; then
echo "No refs to promote." >&2; exit 1
echo "Nothing to promote: all aliases current and all version tags present."
echo "changed=false" >> "$GITHUB_OUTPUT"
: > /tmp/refspecs.txt
exit 0
fi
echo "changed=true" >> "$GITHUB_OUTPUT"
printf '%s\n' "${REFSPECS[@]}"
# persist for next step
printf '%s\n' "${REFSPECS[@]}" > /tmp/refspecs.txt

- name: Atomic push (all aliases + tags, or nothing)
if: steps.refs.outputs.changed == 'true'
run: |
set -euo pipefail
mapfile -t REFSPECS < /tmp/refspecs.txt
Expand Down