Files
xjxandgd-ops 790046d9a8 Add protected parallel delivery workflow (#6)
## Development lane / 开发通道

- Lane: `direct-to-main` bootstrap
- Base branch: `main`
- Active train: `n/a`
- Owning Agent/session: workflow coordinator

## Change / 改动

- Split authoritative CI into parallel static, contract, Rust lint/test
shards, Swift interaction, stress, overlay, bundle, and stable `Required
CI` jobs.
- Make every ready PR to `main` a complete gate while keeping task PRs
to `gd-ops/train/*` path-scoped.
- Add direct/train worktree coordination, bounded changelog fragments,
protected main/train ruleset automation, and safe same-repository squash
auto-merge.
- Let GitHub Release promote an exact successful main commit, reuse its
source proof, and create a missing release tag only after proof
validation.
- Keep local pre-push validation bounded to basic syntax, formatting,
contracts, and compilation; complete duplicate proof stays in GitHub CI.

This is the one bootstrap PR: the auto-merge workflow and required rules
do not exist on the base branch yet. After this PR passes and is merged
once, the exact `main` push must pass `Required CI`; then the
fail-closed applicator can activate both rulesets and all later ready
`gd-ops/*` PRs follow the automated path.

## Validation / 验证

- `actionlint .github/workflows/ci.yml .github/workflows/release.yml
.github/workflows/auto-merge.yml`
- `./script/validate_source_syntax.sh`
- `./script/validate_build_scripts_safety.sh --skip-source-syntax`
- Exact staged diff was reapplied to a clean detached worktree before
the final validation run.
- 28 validation-tooling tests, 40 release-distribution tests, macOS
build-contract tests, Release shell contracts, and test-isolation
validation passed.

## Risk / 风险

- No product runtime or credential access is added.
- `pull_request_target` never checks out or executes PR code and only
enables auto-merge after API verification of active PR and `Required CI`
rules.
- Full ruleset activation fails closed until the exact remote main
commit has a trusted successful GitHub Actions push run.
- Reversible repository settings are already enabled: auto-merge,
squash-only, update branch, and delete merged branches.

---------

Co-authored-by: gd-ops <gd-ops@xmfunny.com>
2026-08-11 20:28:02 +08:00

159 lines
4.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BASE_REF=""
PLAN_ONLY=0
usage() {
cat <<'EOF'
usage: validate_pre_push.sh [--base REF] [--plan-only]
Runs the bounded local fast gate: diff hygiene, source syntax, lightweight
contracts, formatting, and compilation for touched languages. Complete tests,
App assembly, stress, and artifact validation are authoritative remote gates.
EOF
}
while (($# > 0)); do
case "$1" in
--base)
(($# >= 2)) || { usage >&2; exit 2; }
BASE_REF="$2"
shift 2
;;
--plan-only)
PLAN_ONLY=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage >&2
exit 2
;;
esac
done
if [[ -z "$BASE_REF" ]]; then
if upstream="$(git -C "$ROOT_DIR" rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null)"; then
BASE_REF="$(git -C "$ROOT_DIR" merge-base HEAD "$upstream")"
else
BASE_REF="HEAD"
fi
fi
git -C "$ROOT_DIR" rev-parse --verify "$BASE_REF^{commit}" >/dev/null
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/apc-pre-push.XXXXXX")"
trap 'rm -rf "$TMP_DIR"' EXIT
CHANGED_PATHS="$TMP_DIR/changed-paths"
UNTRACKED_PATHS="$TMP_DIR/untracked-paths"
git -C "$ROOT_DIR" diff --name-only -z "$BASE_REF" -- >"$CHANGED_PATHS"
git -C "$ROOT_DIR" ls-files --others --exclude-standard -z >"$UNTRACKED_PATHS"
cat "$UNTRACKED_PATHS" >>"$CHANGED_PATHS"
eval "$(
"$ROOT_DIR/script/validation_scope.py" \
--root "$ROOT_DIR" \
--paths-file "$CHANGED_PATHS" \
--format shell
)"
PLAN=("git diff --check against $BASE_REF" "source syntax and local Markdown links")
if [[ "$APC_CHANGED_LOCALIZATION" == "1" ]]; then
PLAN+=("String Catalog/.strings parity")
fi
if [[ "$APC_CHANGED_SCRIPTS" == "1" ]]; then
PLAN+=("static build and workflow contract safety")
fi
if [[ "$APC_CHANGED_PRODUCER" == "1" ]]; then
PLAN+=("pet Skill structure contracts")
fi
case "$APC_RUST_MODE" in
workspace) PLAN+=("Rust format plus workspace compile check") ;;
petcore) PLAN+=("Rust format plus petcore/petcore-cli compile check") ;;
cli) PLAN+=("Rust format plus petcore-cli compile check") ;;
esac
case "$APC_SWIFT_MODE" in
overlay|full) PLAN+=("Swift package compile check") ;;
esac
if [[ "$APC_CHANGED_PLUGIN_VERSION" == "1" ]]; then
PLAN+=("Codex plugin and bundled Skill version discipline")
fi
if [[ "$APC_COMPUTER_USE" == "recommended" ]]; then
PLAN+=("Computer Use recommended after automated checks; never run automatically")
else
PLAN+=("Computer Use not required for this change scope")
fi
echo "Pre-push plan for $APC_CHANGED_COUNT changed path(s):"
printf ' - %s\n' "${PLAN[@]}"
if [[ "$PLAN_ONLY" == "1" ]]; then
exit 0
fi
run() {
local label="$1"
shift
printf '\n== %s ==\n' "$label"
"$@"
}
run "Whitespace and conflict-marker check" git -C "$ROOT_DIR" diff --check "$BASE_REF" --
while IFS= read -r -d '' untracked_path; do
untracked_check=""
untracked_status=0
if untracked_check="$(
git -C "$ROOT_DIR" diff --no-index --check -- /dev/null "$ROOT_DIR/$untracked_path" 2>&1
)"; then
untracked_status=0
else
untracked_status=$?
fi
if ((untracked_status > 1)) || [[ -n "$untracked_check" ]]; then
printf '%s\n' "$untracked_check" >&2
echo "untracked file failed whitespace validation: $untracked_path" >&2
exit 1
fi
done <"$UNTRACKED_PATHS"
run "Source syntax" "$ROOT_DIR/script/validate_source_syntax.sh"
if [[ "$APC_CHANGED_LOCALIZATION" == "1" ]]; then
run "Localization parity" "$ROOT_DIR/script/validate_localizations.py"
fi
if [[ "$APC_CHANGED_SCRIPTS" == "1" ]]; then
run "Build and release-script safety" \
"$ROOT_DIR/script/validate_build_scripts_safety.sh" \
--skip-source-syntax --static-only
fi
if [[ "$APC_CHANGED_PLUGIN_VERSION" == "1" ]]; then
run "Codex plugin and Skill version discipline" \
"$ROOT_DIR/script/validate_codex_plugin_version.py" \
--base-ref "$BASE_REF"
fi
if [[ "$APC_CHANGED_PRODUCER" == "1" ]]; then
run "Pet Skill contracts" "$ROOT_DIR/script/validate_pet_skills.sh"
fi
RUST_PACKAGE_ARGS=()
case "$APC_RUST_MODE" in
workspace) RUST_PACKAGE_ARGS=(--workspace) ;;
petcore) RUST_PACKAGE_ARGS=(-p petcore -p petcore-cli) ;;
cli) RUST_PACKAGE_ARGS=(-p petcore-cli) ;;
esac
if ((${#RUST_PACKAGE_ARGS[@]} > 0)); then
run "Rust formatting" cargo fmt --all --manifest-path "$ROOT_DIR/Cargo.toml" -- --check
run "Scoped Rust compile check" cargo check \
--manifest-path "$ROOT_DIR/Cargo.toml" \
"${RUST_PACKAGE_ARGS[@]}" \
--locked
fi
case "$APC_SWIFT_MODE" in
overlay|full)
run "Swift compile check" swift build --package-path "$ROOT_DIR/apps/macos"
;;
esac
echo 'Local fast gate passed. GitHub CI remains authoritative for complete tests and App assembly.'