Skip to content
Merged
Show file tree
Hide file tree
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
36 changes: 36 additions & 0 deletions .github/scripts/__tests__/sync_pr_lease_contract.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
parseDeliveryRecord,
mergeEligibility,
} = require('../sync_pr_lease_contract');
const { selectMergeEligibleSyncPr } = require('../sync_pr_merge_contract');

const current = {
schema: DELIVERY_RECORD_SCHEMA,
Expand Down Expand Up @@ -37,3 +38,38 @@ test('an unexpired matching delivery record is merge eligible', () => {
}).reason, 'lease_expired');
assert.equal(mergeEligibility(parsed, { now: '2026-08-01T22:00:00Z', desiredTreeHash: 'other' }).reason, 'desired_tree_mismatch');
});

test('only the newest matching generation is selected for merge', () => {
const old = {
number: 10,
created_at: '2026-08-01T20:00:00Z',
head: { ref: 'sync/workflows-old' },
body: formatDeliveryRecord({ ...current, generation: 'new', desired_tree_hash: 'tree-new' }),
};
const newest = {
number: 11,
created_at: '2026-08-01T21:00:00Z',
head: { ref: 'sync/workflows-new' },
body: formatDeliveryRecord({ ...current, generation: 'new', desired_tree_hash: 'tree-new' }),
};

const result = selectMergeEligibleSyncPr([old, newest], {
syncHash: 'new',
now: '2026-08-01T22:00:00Z',
planId: 'plan-abc',
repository: 'stranske/Ready',
desiredTreeHash: 'tree-new',
});

assert.equal(result.active.number, newest.number);
assert.deepEqual(result.stale.map((pr) => pr.number), [old.number]);
assert.deepEqual(result.eligibility, { eligible: true, reason: 'current_unexpired' });
});
Comment thread
stranske marked this conversation as resolved.

test('deliberate break: expired or terminal records cannot merge', () => {
const now = '2026-08-01T22:00:00Z';
assert.equal(mergeEligibility({ ...current, lease_expires_at: '2026-08-01T21:59:59Z' }, { now }).eligible, false);
for (const terminal_disposition of ['merged', 'superseded', 'expired', 'blocked']) {
assert.equal(mergeEligibility({ ...current, terminal_disposition }, { now }).eligible, false);
}
});
107 changes: 86 additions & 21 deletions .github/workflows/maint-68-sync-consumer-repos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -742,24 +742,66 @@

branch_name="sync/workflows-${{ needs.prepare.outputs.template_hash }}"

# Configure git for push/fetch authentication using credential helper
# This avoids exposing token in git remote URL or command output.
# Must be configured before any git operations that need auth
# (including the early fetch of an existing sync branch).
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
credential_helper="!f() { echo \"username=x-access-token\"; echo \"password=\$GH_TOKEN\"; }; f"
git config credential.helper "$credential_helper"

# Keep an existing generated PR current instead of treating it as a
# terminal delivery attempt: rebuild its branch and refresh its lease.
# terminal delivery attempt: rebuild its branch and refresh its lease
# only when the existing attempt still has a current delivery record.
existing_pr=$(gh pr list --head "$branch_name" --json number -q '.[0].number' || echo "")
existing_head=""
existing_base=""
existing_tree=""
existing_refreshable=false
existing_refresh_reason="none"
if [ -n "$existing_pr" ]; then
echo "Refreshing existing sync PR #$existing_pr"
echo "Inspecting existing sync PR #$existing_pr for same-generation refresh"
# Capture the remote head now so a later force push cannot overwrite
# an intervening consumer edit.
git fetch origin "$branch_name"
existing_head=$(git rev-parse FETCH_HEAD)
existing_base=$(git rev-parse "${existing_head}^")
existing_tree=$(git rev-parse "${existing_head}^{tree}")
existing_body_file=$(mktemp)
gh pr view "$existing_pr" --json body -q .body >"$existing_body_file" || true
# shellcheck disable=SC2016 # The embedded Node source must remain literal.
refresh_check=$(
EXISTING_BODY_FILE="$existing_body_file" \
DELIVERY_GENERATION="$DELIVERY_GENERATION" \
PLAN_ID="$PLAN_ID" \
DELIVERY_REPOSITORY="$DELIVERY_REPOSITORY" \
node -e '
const fs = require("fs");
const { parseDeliveryRecord, mergeEligibility } = require("../workflows/.github/scripts/sync_pr_lease_contract");
const body = fs.readFileSync(process.env.EXISTING_BODY_FILE || "", "utf8");
const record = parseDeliveryRecord(body);
if (!record) {
process.stdout.write("false missing_or_invalid");
process.exit(0);
}
if (record.generation !== (process.env.DELIVERY_GENERATION || "")) {
process.stdout.write("false generation_mismatch");
process.exit(0);
}
const result = mergeEligibility(record, {
planId: process.env.PLAN_ID || "",
repository: process.env.DELIVERY_REPOSITORY || "",
});
process.stdout.write(`${result.eligible ? "true" : "false"} ${result.reason}`);
'
)
rm -f "$existing_body_file"
existing_refreshable=${refresh_check%% *}
existing_refresh_reason=${refresh_check#* }
echo "Existing PR #$existing_pr refreshable=${existing_refreshable} reason=${existing_refresh_reason}"
fi

# Configure git for push authentication using credential helper
# This avoids exposing token in git remote URL or command output
# Must be configured before any git operations that need auth
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# shellcheck disable=SC2016 # GH_TOKEN expands at runtime
credential_helper='!f() { echo "username=x-access-token";'
credential_helper+=" echo \"password=${GH_TOKEN}\";"
credential_helper+=' }; f'
git config credential.helper "$credential_helper"

# Remote branch exists but no PR was found
# (likely from a failed previous run) - deleting and recreating
if [ -z "$existing_pr" ] && git ls-remote --exit-code origin "refs/heads/$branch_name" >/dev/null 2>&1; then
Expand All @@ -779,7 +821,8 @@
}
fi

# Create branch and commit
# Create the current-generation branch from the current consumer base.
base_sha=$(git rev-parse HEAD)
git checkout -B "$branch_name"

# Stage ONLY manifest-declared targets, to avoid accidentally committing
Expand All @@ -801,23 +844,45 @@
git add --force -A
fi

if git diff --cached --quiet; then
desired_tree_hash=$(git write-tree)
matching_existing=false
if [ -n "$existing_pr" ] \
&& [ "$existing_refreshable" = "true" ] \
&& [ "$existing_base" = "$base_sha" ] \
&& [ "$existing_tree" = "$desired_tree_hash" ]; then
matching_existing=true
echo "Existing PR #$existing_pr already matches this base and desired tree with a current lease; refreshing only its lease"
elif [ -n "$existing_pr" ] \
&& [ "$existing_base" = "$base_sha" ] \
&& [ "$existing_tree" = "$desired_tree_hash" ] \
&& [ "$existing_refreshable" != "true" ]; then
echo "Existing PR #$existing_pr matches base/tree but is not refreshable (${existing_refresh_reason}); leaving it for Maint 71 disposition."
echo "status=existing_pr_not_refreshable" >> "$GITHUB_OUTPUT"
exit 0
fi

if git diff --cached --quiet && [ "$matching_existing" != "true" ]; then
echo "No changes to commit after sync; skipping PR creation."
echo "status=no_committed_changes" >> "$GITHUB_OUTPUT"
exit 0
fi

git commit -m "chore: sync workflow templates from Workflows repo
if [ "$matching_existing" != "true" ]; then
git commit -m "chore: sync workflow templates from Workflows repo

Automated sync from stranske/Workflows
Template hash: ${{ needs.prepare.outputs.template_hash }}
Automated sync from stranske/Workflows
Template hash: ${{ needs.prepare.outputs.template_hash }}
Comment thread
stranske marked this conversation as resolved.
Comment thread
stranske marked this conversation as resolved.

Changes synced from sync-manifest.yml"
Changes synced from sync-manifest.yml"

git push --quiet -u origin "$branch_name"
if [ -n "$existing_pr" ]; then
git push --quiet --force-with-lease="refs/heads/$branch_name:$existing_head" -u origin "$branch_name"
else
git push --quiet -u origin "$branch_name"
fi
fi

# Create PR
desired_tree_hash=$(git rev-parse 'HEAD^{tree}')
lease_expires_at=$(date -u -d '+72 hours' +%Y-%m-%dT%H:%M:%SZ)
sync_marker=$(jq -nc \
--arg schema "workflows-consumer-sync-pr/v1" \
Expand Down
36 changes: 36 additions & 0 deletions tests/workflows/test_sync_manifest_delivery.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,39 @@ def test_maint_71_emits_canary_evidence_with_review_debt() -> None:
assert "active_review_thread_count" in source
assert "required_check_state" in source
assert "plan_id" in source


def test_maint68_refreshes_only_a_same_base_and_tree_delivery_attempt() -> None:
"""A current generation reuses its PR; a changed base/tree must be replaced safely."""
source = SYNC_WORKFLOW_PATH.read_text(encoding="utf-8")

credential_idx = source.index('git config credential.helper "$credential_helper"')
fetch_idx = source.index('git fetch origin "$branch_name"')
assert credential_idx < fetch_idx

assert 'git fetch origin "$branch_name"' in source
assert 'existing_base=$(git rev-parse "${existing_head}^")' in source
assert 'existing_tree=$(git rev-parse "${existing_head}^{tree}")' in source
assert "desired_tree_hash=$(git write-tree)" in source
assert "existing_refreshable=false" in source
assert "parseDeliveryRecord" in source
assert "mergeEligibility" in source
assert "status=existing_pr_not_refreshable" in source
assert '[ "$existing_refreshable" = "true" ]' in source
assert '[ "$existing_base" = "$base_sha" ]' in source
assert '[ "$existing_tree" = "$desired_tree_hash" ]' in source
assert "matching_existing=true" in source
commit_push_guard = source.index('if [ "$matching_existing" != "true" ]; then')
assert (
source.index(
'git commit -m "chore: sync workflow templates from Workflows repo', commit_push_guard
)
> commit_push_guard
)
assert (
source.index(
'git push --quiet --force-with-lease="refs/heads/$branch_name:$existing_head"',
commit_push_guard,
)
> commit_push_guard
)
Loading