62 lines
3.1 KiB
Bash
Executable File
62 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Promote a tag's tree from the source repo into the PUBLIC repo working dir as one clean release
|
|
# commit + tag. Excludes .git (via git archive) and .github (private CI stays private). The public
|
|
# repo's own .git is preserved. The PUSH is done by the calling workflow (it holds the credentials).
|
|
# usage: promote.sh <src-repo> <tag> <public-repo-dir> <commit-message>
|
|
set -euo pipefail
|
|
|
|
src="${1:?src repo required}"
|
|
tag="${2:?tag required}"
|
|
pub="${3:?public repo dir required}"
|
|
msg="${4:?commit message required}"
|
|
|
|
git -C "$src" rev-parse "$tag" >/dev/null 2>&1 || { echo "unknown tag: $tag" >&2; exit 1; }
|
|
|
|
# Fail fast BEFORE mutating the public tree if this release was already promoted — re-promoting a
|
|
# published release must be deliberate, not a silent half-commit left on the runner.
|
|
if git -C "$pub" rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then
|
|
echo "tag already promoted in public repo: $tag" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Clear the public working tree but KEEP its .git.
|
|
find "$pub" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} +
|
|
|
|
# Extract exactly the tag's tree (git archive omits .git automatically).
|
|
git -C "$src" archive --format=tar "$tag" | tar -x -C "$pub"
|
|
|
|
# Private CI never goes public.
|
|
rm -rf "${pub:?}/.github"
|
|
|
|
# Leak guard — refuse to publish a tree that still carries an internal-only doc. These paths are
|
|
# export-ignored on current trees, so their presence means the tag predates the leak-safety fixes
|
|
# (such a tree would also ship the old private URLs + dev notes). Refuse rather than leak the
|
|
# dev/staging repos. Checking for the docs (not grepping for URL strings) keeps this guard free of
|
|
# the very identifiers it protects — so it never trips on its own source or on legitimate content.
|
|
for f in CLAUDE.md rules.md handoff.md kickoff-prompt.md docs; do
|
|
if [ -e "$pub/$f" ]; then
|
|
echo "leak-guard: REFUSING to publish $tag — internal path '$f' present; promote a tag cut after the leak-safety fixes (>= v0.9.58)." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# String leak-guard: refuse to publish a tree that mentions the private upstream by name. The
|
|
# patterns are the always-forbidden literals plus any passed in CLUSEV_PROMOTE_FORBIDDEN (the
|
|
# private host/slug — supplied from a CI secret so they never live in a tracked file). This script
|
|
# is itself export-ignored, so grepping the assembled tree never trips on the guard's own source.
|
|
forbidden=$'gitea\n.env.gitea'
|
|
[ -n "${CLUSEV_PROMOTE_FORBIDDEN:-}" ] && forbidden+=$'\n'"${CLUSEV_PROMOTE_FORBIDDEN// /$'\n'}"
|
|
while IFS= read -r pat; do
|
|
[ -n "$pat" ] || continue
|
|
if grep -rIiF --exclude-dir=.git -- "$pat" "$pub" >/dev/null 2>&1; then
|
|
echo "leak-guard: REFUSING to publish $tag — forbidden pattern present in the public tree." >&2
|
|
exit 1
|
|
fi
|
|
done <<< "$forbidden"
|
|
|
|
git -C "$pub" add -A
|
|
# --allow-empty: stable promotion re-tags the same source commit as the last release candidate, so the
|
|
# extracted tree is byte-identical and there is nothing to stage — still produce a release commit.
|
|
git -C "$pub" commit -q --allow-empty -m "$msg"
|
|
git -C "$pub" tag "$tag"
|