Skip to content
54 changes: 42 additions & 12 deletions .github/workflows/handle-baseline-result.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ on:
workflow_run:
workflows: ["Baseline verification"]
types: [completed]
workflow_dispatch:
inputs:
baseline_run_id:
description: "Completed Baseline workflow run to process"
required: true
type: string

permissions:
actions: read
contents: read
pull-requests: read

concurrency:
group: handle-baseline-${{ github.event.workflow_run.id }}
group: handle-baseline-${{ github.event.workflow_run.id || github.event.inputs.baseline_run_id || github.run_id }}
cancel-in-progress: false

jobs:
Expand All @@ -20,9 +26,12 @@ jobs:
contents: write
pull-requests: write
if: >-
github.event.workflow_run.conclusion == 'success' &&
(github.event.workflow_run.event == 'pull_request' ||
github.event.workflow_run.event == 'workflow_dispatch')
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
(github.event.workflow_run.event == 'pull_request' ||
github.event.workflow_run.event == 'workflow_dispatch')) ||
(github.event_name == 'workflow_dispatch' &&
github.event.inputs.baseline_run_id != '')
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
Expand All @@ -38,17 +47,28 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
script: |
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
const {
isEligibleAgentPullRequest,
resolveBaselineRun,
isPullRequestGateBlocked,
resolvePullRequestForRun,
waitForPullRequestGate,
} = require(
`${process.env.GITHUB_WORKSPACE}/scripts/github/exact-head-pr-gates.cjs`,
);

const run = await resolveBaselineRun({
github,
owner,
repo,
event: context.payload,
});
if (!run || run.conclusion !== "success") {
core.info("The requested Baseline run did not complete successfully.");
return;
}

const resolved = await resolvePullRequestForRun({
github,
owner,
Expand Down Expand Up @@ -90,9 +110,12 @@ jobs:
core.notice(`Merged PR #${pr.number} immediately after successful final-head Baseline verification.`);
qualify:
if: >-
github.event.workflow_run.conclusion == 'failure' &&
(github.event.workflow_run.event == 'pull_request' ||
github.event.workflow_run.event == 'workflow_dispatch')
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'failure' &&
(github.event.workflow_run.event == 'pull_request' ||
github.event.workflow_run.event == 'workflow_dispatch')) ||
(github.event_name == 'workflow_dispatch' &&
github.event.inputs.baseline_run_id != '')
runs-on: ubuntu-latest
outputs:
eligible: ${{ steps.gate.outputs.eligible }}
Expand All @@ -114,9 +137,8 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
with:
script: |
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
const { resolvePullRequestForRun } = require(
const { resolveBaselineRun, resolvePullRequestForRun } = require(
`${process.env.GITHUB_WORKSPACE}/scripts/github/exact-head-pr-gates.cjs`,
);
const deny = (reason) => {
Expand All @@ -125,6 +147,14 @@ jobs:
core.notice(`Automatic repair skipped: ${reason}`);
};

const run = await resolveBaselineRun({
github,
owner,
repo,
event: context.payload,
});
if (!run || run.conclusion !== 'failure') return deny('the requested Baseline run did not fail');

const resolved = await resolvePullRequestForRun({
github,
owner,
Expand Down Expand Up @@ -207,7 +237,7 @@ jobs:
- name: Capture failed Baseline logs
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id }}
RUN_ID: ${{ github.event.workflow_run.id || github.event.inputs.baseline_run_id }}
run: |
mkdir -p .ross-autofix
gh run view "$RUN_ID" --log-failed > .ross-autofix/baseline-failure.log
Expand Down Expand Up @@ -370,7 +400,7 @@ jobs:
if: steps.parse.outputs.apply == 'true'
env:
HEAD_REF: ${{ needs.qualify.outputs.head_ref }}
FAILED_RUN: ${{ github.event.workflow_run.id }}
FAILED_RUN: ${{ github.event.workflow_run.id || github.event.inputs.baseline_run_id }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
Expand Down
31 changes: 11 additions & 20 deletions .github/workflows/sync-upstream-mike-escalated.yml
Original file line number Diff line number Diff line change
Expand Up @@ -639,34 +639,25 @@ jobs:
pr_url="$(gh pr create --base main --head "$branch" --title "$title" --body-file /tmp/pr-body.md)"
fi
pr_number="$(gh pr view "$pr_url" --json number --jq .number)"
if [ "$RISK" != "high" ]; then gh workflow run baseline.yml --ref "$branch"; fi
echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT"
echo "branch=$branch" >> "$GITHUB_OUTPUT"

- name: Wait for automatic merge and continue the escalated queue
- name: Hand the Baseline result to the existing gate
id: settle
if: steps.finalize_full.outputs.risk != 'high'
env:
GH_TOKEN: ${{ github.token }}
RECONSIDER_ALL_DEFERRED: ${{ needs.scan.outputs.reconsider_all_deferred }}
BRANCH: ${{ steps.publish.outputs.branch }}
PR_NUMBER: ${{ steps.publish.outputs.pr_number }}
run: bash scripts/github/settle-baseline-run.sh "$BRANCH" "$PR_NUMBER"

- name: Continue the escalated queue
if: steps.settle.outputs.merged == 'true'
env:
GH_TOKEN: ${{ github.token }}
RECONSIDER_ALL_DEFERRED: ${{ needs.scan.outputs.reconsider_all_deferred }}
run: |
set -euo pipefail
deadline=$((SECONDS + 7200))
merged_at=""
while [ "$SECONDS" -lt "$deadline" ]; do
pr="$(gh pr view "$PR_NUMBER" --json state,mergedAt)"
merged_at="$(jq -r '.mergedAt // empty' <<<"$pr")"
state="$(jq -r '.state' <<<"$pr")"
if [ -n "$merged_at" ]; then
echo "Escalated synchronization PR #${PR_NUMBER} merged at ${merged_at}."
break
fi
if [ "$state" != "OPEN" ]; then
echo "Escalated synchronization PR #${PR_NUMBER} closed without merging." >&2
exit 1
fi
sleep 20
done
test -n "$merged_at"
if [ "$RECONSIDER_ALL_DEFERRED" = "true" ]; then
gh workflow run sync-upstream-mike-escalated.yml --ref main -f reconsider_all_deferred=true
else
Expand Down
44 changes: 23 additions & 21 deletions .github/workflows/sync-upstream-mike.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
script: |
const fs = require("fs");
const { execFileSync } = require("child_process");
const { findExistingUnmergedMikeProposal } = await import("./scripts/lib/mike-sync.mjs");
const state = JSON.parse(
fs.readFileSync("docs/upstream-mike-sync-state.json", "utf8"),
);
Expand Down Expand Up @@ -81,11 +82,28 @@ jobs:
const time = new Date(left.merged_at).getTime() - new Date(right.merged_at).getTime();
return time || left.number - right.number;
});
const window = candidates.slice(0, limit);
const proposals = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: "all",
base: "main",
per_page: 100,
});
const boundedCandidates = candidates.slice(0, limit);
const blockedIndex = boundedCandidates.findIndex((candidate) =>
findExistingUnmergedMikeProposal(proposals, candidate.number),
);
const window = blockedIndex >= 0
? boundedCandidates.slice(0, blockedIndex)
: boundedCandidates;
core.setOutput("base_sha", execFileSync("git", ["rev-parse", "HEAD"], { encoding: "utf8" }).trim());
if (window.length === 0) {
core.setOutput("has_window", "false");
core.notice("No unprocessed merged upstream Mike PRs are newer than the sync cursor.");
if (blockedIndex >= 0) {
core.notice("An existing unmerged automated proposal is already the next low-risk queue item; waiting for reconciliation.");
} else {
core.notice("No unprocessed merged upstream Mike PRs are newer than the sync cursor.");
}
return;
}

Expand Down Expand Up @@ -357,6 +375,7 @@ jobs:
git add docs/upstream-mike-sync-state.json

- name: Publish one capability proposal and settle its PR
id: publish
env:
GH_TOKEN: ${{ github.token }}
PR_TITLE: ${{ steps.parse.outputs.title }}
Expand All @@ -382,27 +401,10 @@ jobs:

pr_url="$(gh pr create --base main --head "$branch" --title "$PR_TITLE" --body-file /tmp/pr-body.md)"
pr_number="$(gh pr view "$pr_url" --json number --jq .number)"
gh workflow run baseline.yml --ref "$branch"

deadline=$((SECONDS + 7200))
merged_at=""
while [ "$SECONDS" -lt "$deadline" ]; do
pr="$(gh pr view "$pr_number" --json state,mergedAt)"
merged_at="$(jq -r '.mergedAt // empty' <<<"$pr")"
state="$(jq -r '.state' <<<"$pr")"
if [ -n "$merged_at" ]; then
echo "Synchronization PR #${pr_number} merged at ${merged_at}."
break
fi
if [ "$state" != "OPEN" ]; then
echo "Synchronization PR #${pr_number} closed without merging." >&2
exit 1
fi
sleep 20
done
test -n "$merged_at"
bash scripts/github/settle-baseline-run.sh "$branch" "$pr_number"

- name: Continue Mike capability queues
if: steps.publish.outputs.merged == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
Expand Down
15 changes: 15 additions & 0 deletions scripts/github/exact-head-pr-gates.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ const isEligibleAgentPullRequest = (
(allowMikeSyncBot && isMarkedMikeSyncBot(pr))) &&
(verifiedHead === null || pr.head.sha === verifiedHead);

const resolveBaselineRun = async ({ github, owner, repo, event }) => {
if (event?.workflow_run) return event.workflow_run;

const runId = Number(event?.inputs?.baseline_run_id);
if (!Number.isSafeInteger(runId) || runId <= 0) return null;

const { data: run } = await github.rest.actions.getWorkflowRun({
owner,
repo,
run_id: runId,
});
return run.name === "Baseline verification" ? run : null;
};

const resolvePullRequestForRun = async ({ github, owner, repo, run }) => {
const linked = run.pull_requests?.[0];
if (run.event === "pull_request" && linked) {
Expand Down Expand Up @@ -129,6 +143,7 @@ const isPullRequestGateBlocked = ({ gate, expectedHead }) => {
module.exports = {
isEligibleAgentPullRequest,
isPullRequestGateBlocked,
resolveBaselineRun,
resolvePullRequestForRun,
waitForPullRequestGate,
};
72 changes: 72 additions & 0 deletions scripts/github/settle-baseline-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -euo pipefail

branch="${1:?branch is required}"
pr_number="${2:?pull request number is required}"
head_sha="$(git rev-parse HEAD)"
output_file="${GITHUB_OUTPUT:-}"

set_output() {
if [ -n "$output_file" ]; then
printf '%s=%s\n' "$1" "$2" >> "$output_file"
fi
}

set_output merged false

echo "Dispatching exact-head Baseline for ${branch} (${head_sha})."
gh workflow run baseline.yml --ref "$branch"

baseline_run_id=""
for _ in $(seq 1 30); do
baseline_run_id="$(gh run list --workflow baseline.yml --branch "$branch" --limit 50 --json databaseId,headSha | jq -r --arg sha "$head_sha" 'map(select(.headSha == $sha)) | first | .databaseId // empty')"
if [ -n "$baseline_run_id" ]; then break; fi
sleep 2
done

if [ -z "$baseline_run_id" ]; then
echo "::notice::Baseline dispatch was accepted but its exact run ID was not observable; leaving PR #${pr_number} pending for reconciliation."
exit 0
fi

echo "Baseline run ${baseline_run_id} is attached to exact head ${head_sha}."
baseline_deadline=$((SECONDS + 900))
baseline_status=""
baseline_conclusion=""
while [ "$SECONDS" -lt "$baseline_deadline" ]; do
run="$(gh run view "$baseline_run_id" --json status,conclusion,headSha)"
baseline_status="$(jq -r '.status' <<<"$run")"
baseline_conclusion="$(jq -r '.conclusion // empty' <<<"$run")"
if [ "$baseline_status" = "completed" ]; then break; fi
sleep 10
done

if [ "$baseline_status" != "completed" ]; then
echo "::notice::Baseline run ${baseline_run_id} did not complete within the bounded handoff window; leaving PR #${pr_number} pending."
exit 0
fi

echo "Baseline run ${baseline_run_id} completed with conclusion ${baseline_conclusion}."
if ! gh workflow run handle-baseline-result.yml --ref main -f baseline_run_id="$baseline_run_id"; then
echo "::notice::Could not dispatch the existing Baseline-result gate for run ${baseline_run_id}; leaving PR #${pr_number} pending."
exit 0
fi

merge_deadline=$((SECONDS + 600))
while [ "$SECONDS" -lt "$merge_deadline" ]; do
pr="$(gh pr view "$pr_number" --json state,mergedAt)"
merged_at="$(jq -r '.mergedAt // empty' <<<"$pr")"
state="$(jq -r '.state' <<<"$pr")"
if [ -n "$merged_at" ]; then
echo "Synchronization PR #${pr_number} merged at ${merged_at}."
set_output merged true
exit 0
fi
if [ "$state" != "OPEN" ]; then
echo "::notice::Synchronization PR #${pr_number} closed without merging; reconciliation remains authoritative."
exit 0
fi
sleep 10
done

echo "::notice::Synchronization PR #${pr_number} is still pending after the bounded merge handoff window; reconciliation remains authoritative."
Loading