ci: isolate metadata edits and recover squash commits (#47)
## Development claim / 开发声明
- Domain: `release-control-plane`
- Claim: `control-plane`
- Base commit: `f7e9f52db946b4edb77cea61b6c67dd0cb6a5538`
- Release preparation: `no`
- Focused validation:
- `python3 script/tests/test_validation_tooling.py`
- `./script/validate_build_scripts_safety.sh --static-only`
<!-- apc-engineering-coordination-v1
{"claim_overlap_rejections":0,"merge_tree_conflicts":0,"schema_version":"apc.engineering-coordination.v1","task_created_at":"2026-08-18T19:04:47Z","train_conflict_resolutions":0}
-->
Co-authored-by: gd-ops <gd-ops@xmfunny.com>
This commit is contained in:
1 parent
f7e9f52db9
commit
4181533050
3 files changed
+62
-13
No files matched your search
@@ -88,7 +88,19 @@ jobs:
|
||||
test "$(jq -er '.head.ref' <<<"$resolved_pull")" = "$HEAD_BRANCH"
|
||||
test "$(jq -er '.head.sha' <<<"$resolved_pull")" = "$HEAD_SHA"
|
||||
test "$(jq -er '.merged_at | type' <<<"$resolved_pull")" = "string"
|
||||
merge_commit="$(jq -er '.merge_commit_sha' <<<"$resolved_pull")"
|
||||
merged_pr="$(
|
||||
gh pr view "$pr_number" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--json number,state,mergedAt,mergeCommit,baseRefName,headRefName,headRefOid,isCrossRepository
|
||||
)"
|
||||
test "$(jq -er '.number' <<<"$merged_pr")" = "$pr_number"
|
||||
test "$(jq -er '.state' <<<"$merged_pr")" = "MERGED"
|
||||
test "$(jq -er '.baseRefName' <<<"$merged_pr")" = "$base_branch"
|
||||
test "$(jq -er '.headRefName' <<<"$merged_pr")" = "$HEAD_BRANCH"
|
||||
test "$(jq -er '.headRefOid' <<<"$merged_pr")" = "$HEAD_SHA"
|
||||
test "$(jq -er '.isCrossRepository' <<<"$merged_pr")" = "false"
|
||||
test "$(jq -er '.mergedAt | type' <<<"$merged_pr")" = "string"
|
||||
merge_commit="$(jq -er '.mergeCommit.oid' <<<"$merged_pr")"
|
||||
[[ "$merge_commit" =~ ^[0-9a-f]{40}$ ]]
|
||||
fi
|
||||
|
||||
@@ -151,8 +163,9 @@ jobs:
|
||||
newly_merged=1
|
||||
fi
|
||||
pull=""
|
||||
merged_pr=""
|
||||
merge_observed=0
|
||||
for attempt in 1 2 3 4 5 6; do
|
||||
for attempt in $(seq 1 15); do
|
||||
pull="$(
|
||||
gh api \
|
||||
-H 'Accept: application/vnd.github+json' \
|
||||
@@ -165,12 +178,33 @@ jobs:
|
||||
'.merged == true and
|
||||
.base.ref == $base_branch and
|
||||
.head.sha == $head_sha and
|
||||
(.merge_commit_sha | strings | test("^[0-9a-f]{40}$"))' \
|
||||
(.merged_at | type) == "string"' \
|
||||
<<<"$pull" >/dev/null; then
|
||||
merge_observed=1
|
||||
break
|
||||
if ! merged_pr="$(
|
||||
gh pr view "$PR_NUMBER" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--json number,state,mergedAt,mergeCommit,baseRefName,headRefName,headRefOid,isCrossRepository
|
||||
)"; then
|
||||
merged_pr=""
|
||||
elif jq -e \
|
||||
--argjson pr_number "$PR_NUMBER" \
|
||||
--arg base_branch "$BASE_BRANCH" \
|
||||
--arg head_branch "$HEAD_BRANCH" \
|
||||
--arg head_sha "$HEAD_SHA" \
|
||||
'.number == $pr_number and
|
||||
.state == "MERGED" and
|
||||
.baseRefName == $base_branch and
|
||||
.headRefName == $head_branch and
|
||||
.headRefOid == $head_sha and
|
||||
.isCrossRepository == false and
|
||||
(.mergedAt | type) == "string" and
|
||||
(.mergeCommit.oid | strings | test("^[0-9a-f]{40}$"))' \
|
||||
<<<"$merged_pr" >/dev/null; then
|
||||
merge_observed=1
|
||||
break
|
||||
fi
|
||||
fi
|
||||
if ((attempt < 6)); then
|
||||
if ((attempt < 15)); then
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
@@ -178,7 +212,7 @@ jobs:
|
||||
test "$(jq -er '.merged' <<<"$pull")" = "true"
|
||||
test "$(jq -er '.base.ref' <<<"$pull")" = "$BASE_BRANCH"
|
||||
test "$(jq -er '.head.sha' <<<"$pull")" = "$HEAD_SHA"
|
||||
merge_commit="$(jq -er '.merge_commit_sha' <<<"$pull")"
|
||||
merge_commit="$(jq -er '.mergeCommit.oid' <<<"$merged_pr")"
|
||||
[[ "$merge_commit" =~ ^[0-9a-f]{40}$ ]]
|
||||
if [[ "$PR_STATE" == "closed" ]]; then
|
||||
test "$merge_commit" = "$RECOVERED_COMMIT"
|
||||
|
||||
@@ -17,7 +17,11 @@ permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
# Metadata edits still get a cheap routing run, but must never cancel an
|
||||
# in-flight source validation for the same PR head. Base-ref edits remain
|
||||
# independently fail-closed because the merge verifier rechecks the exact
|
||||
# current base identity before accepting either run.
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}-${{ github.event_name == 'pull_request' && github.event.action == 'edited' && format('edited-{0}', github.run_id) || 'validation' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
|
||||
@@ -1117,6 +1117,14 @@ class CIWorkflowContractTests(unittest.TestCase):
|
||||
self.assertIn('"$GITHUB_SHA" != "$EXPECTED_SHA"', source)
|
||||
self.assertIn("MAIN_PARENT: ${{ needs.scope.outputs.base }}", source)
|
||||
|
||||
def test_ci_metadata_edits_do_not_cancel_source_validation(self) -> None:
|
||||
source = (ROOT / ".github/workflows/ci.yml").read_text(encoding="utf-8")
|
||||
concurrency = source[source.index("concurrency:") : source.index("env:")]
|
||||
self.assertIn("github.event.action == 'edited'", concurrency)
|
||||
self.assertIn("format('edited-{0}', github.run_id)", concurrency)
|
||||
self.assertIn("|| 'validation'", concurrency)
|
||||
self.assertIn("cancel-in-progress: true", concurrency)
|
||||
|
||||
def test_successful_protected_pr_ci_merges_and_dispatches_main_without_pr_code(self) -> None:
|
||||
source = (ROOT / ".github/workflows/auto-merge.yml").read_text(
|
||||
encoding="utf-8"
|
||||
@@ -1141,9 +1149,13 @@ class CIWorkflowContractTests(unittest.TestCase):
|
||||
)
|
||||
]
|
||||
self.assertIn("merge_observed=0", merge_step)
|
||||
self.assertIn("for attempt in 1 2 3 4 5 6", merge_step)
|
||||
self.assertIn("for attempt in $(seq 1 15)", merge_step)
|
||||
self.assertIn('test "$merge_observed" = "1"', merge_step)
|
||||
self.assertIn(".merged == true", merge_step)
|
||||
self.assertIn("gh pr view \"$PR_NUMBER\"", merge_step)
|
||||
self.assertIn('if ! merged_pr="$(', merge_step)
|
||||
self.assertIn(".mergeCommit.oid", merge_step)
|
||||
self.assertNotIn(".merge_commit_sha", merge_step)
|
||||
self.assertLess(merge_step.index("gh pr merge"), merge_step.index("merge_observed=0"))
|
||||
cleanup = source.index("./script/ci_proof_promotion.py delete-merged-head")
|
||||
self.assertGreater(cleanup, source.index("Squash or recover only the PR proven"))
|
||||
@@ -1181,10 +1193,9 @@ class CIWorkflowContractTests(unittest.TestCase):
|
||||
'test "$(jq -er \'.head.sha\' <<<"$resolved_pull")" = "$HEAD_SHA"',
|
||||
closed_replay,
|
||||
)
|
||||
self.assertIn(
|
||||
'merge_commit="$(jq -er \'.merge_commit_sha\' <<<"$resolved_pull")"',
|
||||
closed_replay,
|
||||
)
|
||||
self.assertIn('gh pr view "$pr_number"', closed_replay)
|
||||
self.assertIn(".mergeCommit.oid", closed_replay)
|
||||
self.assertNotIn(".merge_commit_sha", closed_replay)
|
||||
|
||||
def test_ci_prepares_producer_image_dependencies_and_pins_actions(self) -> None:
|
||||
source = (ROOT / ".github/workflows/ci.yml").read_text(encoding="utf-8")
|
||||
|
||||
Reference in new issue
Block a user