157 lines
5.5 KiB
Bash
157 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
# shellcheck disable=SC2030,SC2031 # Provider arms isolate PATH/credentials in subshells.
|
|
# The commit whose CI was guarded must be the commit the provider atomically merges.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-merge-head-pin}"
|
|
SHA=0123456789abcdef0123456789abcdef01234567
|
|
|
|
make_fixture() {
|
|
local name="$1" remote="$2"
|
|
local root="$WORK_DIR/$name"
|
|
local tools="$root/tools/git"
|
|
mkdir -p "$tools" "$root/repo"
|
|
cp "$SCRIPT_DIR/pr-merge.sh" "$tools/pr-merge.sh"
|
|
cp "$SCRIPT_DIR/detect-platform.sh" "$tools/detect-platform.sh"
|
|
git -C "$root/repo" init -q
|
|
git -C "$root/repo" remote add origin "$remote"
|
|
cat > "$tools/pr-metadata.sh" <<SH
|
|
#!/usr/bin/env bash
|
|
printf '%s\n' '{"baseRefName":"main","headRefName":"fix/pinned","headRefOid":"$SHA","headRepository":"contributor/widgets-fork"}'
|
|
SH
|
|
cat > "$tools/ci-queue-wait.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
SH
|
|
chmod +x "$tools"/*.sh
|
|
}
|
|
|
|
rm -rf "$WORK_DIR"
|
|
make_fixture gitea https://git.example.test/acme/widgets.git
|
|
make_fixture github https://github.com/acme/widgets.git
|
|
|
|
cat > "$WORK_DIR/gitea/curl" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
payload=""
|
|
for ((i=1; i<=$#; i++)); do
|
|
if [[ "${!i}" == "-d" ]]; then
|
|
j=$((i + 1))
|
|
payload="${!j}"
|
|
fi
|
|
done
|
|
printf '%s' "$payload" > "${MOSAIC_MERGE_PAYLOAD_LOG:?}"
|
|
printf '200'
|
|
SH
|
|
chmod +x "$WORK_DIR/gitea/curl"
|
|
|
|
set +e
|
|
(
|
|
cd "$WORK_DIR/gitea/repo"
|
|
export PATH="$WORK_DIR/gitea:$PATH"
|
|
export GITEA_TOKEN=stub-token
|
|
export GITEA_URL=https://git.example.test
|
|
export MOSAIC_CREDENTIALS_FILE="$WORK_DIR/no-credentials.json"
|
|
export MOSAIC_MERGE_PAYLOAD_LOG="$WORK_DIR/gitea-payload.json"
|
|
env -u MOSAIC_GIT_IDENTITY "$WORK_DIR/gitea/tools/git/pr-merge.sh" -n 123
|
|
) >"$WORK_DIR/gitea.out" 2>&1
|
|
gitea_rc=$?
|
|
set -e
|
|
if [[ "$gitea_rc" -ne 0 ]]; then
|
|
echo "FAIL gitea-pin: merge fixture returned $gitea_rc" >&2
|
|
cat "$WORK_DIR/gitea.out" >&2
|
|
exit 1
|
|
fi
|
|
python3 - "$WORK_DIR/gitea-payload.json" "$SHA" <<'PY'
|
|
import json
|
|
import sys
|
|
payload = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
assert set(payload) <= {"Do", "head_commit_id", "delete_branch_after_merge"}, payload
|
|
assert payload.get("Do") == "squash", payload
|
|
assert payload.get("head_commit_id") == sys.argv[2], payload
|
|
PY
|
|
|
|
# A merge-gate verdict is commit-bound. A stale expected head must fail before merge.
|
|
wrong_sha=ffffffffffffffffffffffffffffffffffffffff
|
|
rm -f "$WORK_DIR/gitea-payload-stale.json"
|
|
set +e
|
|
(
|
|
cd "$WORK_DIR/gitea/repo"
|
|
export PATH="$WORK_DIR/gitea:$PATH"
|
|
export GITEA_TOKEN=stub-token
|
|
export GITEA_URL=https://git.example.test
|
|
export MOSAIC_CREDENTIALS_FILE="$WORK_DIR/no-credentials.json"
|
|
export MOSAIC_MERGE_PAYLOAD_LOG="$WORK_DIR/gitea-payload-stale.json"
|
|
env -u MOSAIC_GIT_IDENTITY "$WORK_DIR/gitea/tools/git/pr-merge.sh" -n 123 --expect-head "$wrong_sha"
|
|
) >"$WORK_DIR/gitea-stale.out" 2>&1
|
|
stale_rc=$?
|
|
set -e
|
|
if [[ "$stale_rc" -eq 0 ]] || [[ -e "$WORK_DIR/gitea-payload-stale.json" ]]; then
|
|
echo "FAIL stale-verdict: moved head was not refused before provider merge" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# A merge-capable path cannot bypass the mandatory queue guard. The legacy
|
|
# --skip-queue-guard option must be rejected before any provider merge call.
|
|
cat > "$WORK_DIR/gitea/tools/git/ci-queue-wait.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
exit 99
|
|
SH
|
|
chmod +x "$WORK_DIR/gitea/tools/git/ci-queue-wait.sh"
|
|
rm -f "$WORK_DIR/gitea-payload-bypass.json"
|
|
set +e
|
|
(
|
|
cd "$WORK_DIR/gitea/repo"
|
|
export PATH="$WORK_DIR/gitea:$PATH"
|
|
export GITEA_TOKEN=stub-token
|
|
export GITEA_URL=https://git.example.test
|
|
export MOSAIC_CREDENTIALS_FILE="$WORK_DIR/no-credentials.json"
|
|
export MOSAIC_MERGE_PAYLOAD_LOG="$WORK_DIR/gitea-payload-bypass.json"
|
|
env -u MOSAIC_GIT_IDENTITY "$WORK_DIR/gitea/tools/git/pr-merge.sh" -n 123 --skip-queue-guard
|
|
) >"$WORK_DIR/gitea-bypass.out" 2>&1
|
|
bypass_rc=$?
|
|
set -e
|
|
if [[ "$bypass_rc" -eq 0 ]] || [[ -e "$WORK_DIR/gitea-payload-bypass.json" ]]; then
|
|
echo "FAIL merge-bypass: --skip-queue-guard reached the provider merge path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Dry-run is the only path that may omit the guard because it exits before the
|
|
# provider merge dispatch. Prove the exit and absence of a merge payload.
|
|
rm -f "$WORK_DIR/gitea-payload-dry-run.json"
|
|
(
|
|
cd "$WORK_DIR/gitea/repo"
|
|
export PATH="$WORK_DIR/gitea:$PATH"
|
|
export GITEA_TOKEN=stub-token
|
|
export GITEA_URL=https://git.example.test
|
|
export MOSAIC_CREDENTIALS_FILE="$WORK_DIR/no-credentials.json"
|
|
export MOSAIC_MERGE_PAYLOAD_LOG="$WORK_DIR/gitea-payload-dry-run.json"
|
|
env -u MOSAIC_GIT_IDENTITY "$WORK_DIR/gitea/tools/git/pr-merge.sh" -n 123 --dry-run
|
|
) >"$WORK_DIR/gitea-dry-run.out" 2>&1
|
|
if [[ -e "$WORK_DIR/gitea-payload-dry-run.json" ]]; then
|
|
echo "FAIL dry-run: non-merging preflight reached the provider merge path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
cat > "$WORK_DIR/github/gh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
printf '%s\n' "$*" > "${MOSAIC_GH_MERGE_LOG:?}"
|
|
SH
|
|
chmod +x "$WORK_DIR/github/gh"
|
|
(
|
|
cd "$WORK_DIR/github/repo"
|
|
export PATH="$WORK_DIR/github:$PATH"
|
|
export MOSAIC_GH_MERGE_LOG="$WORK_DIR/github-call.log"
|
|
"$WORK_DIR/github/tools/git/pr-merge.sh" -n 123
|
|
) >"$WORK_DIR/github.out" 2>&1
|
|
if ! grep -q -- "--match-head-commit $SHA" "$WORK_DIR/github-call.log"; then
|
|
echo "FAIL github-pin: merge command omitted --match-head-commit $SHA" >&2
|
|
cat "$WORK_DIR/github-call.log" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "PR merge exact-head pin regression passed (Gitea + GitHub)"
|