ci(upstream-merge): fix no-op outputs bug and split into precheck+merge jobs; gate heavy work at job level

This commit is contained in:
James Peter
2025-09-09 14:56:45 +10:00
parent 9193353f67
commit a9bb2b6add
+98 -18
View File
@@ -38,7 +38,89 @@ env:
JQ_VERSION: '1.7.1'
jobs:
precheck:
name: Precheck (no-op gate)
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
skip: ${{ steps.check.outputs.skip }}
skip_reason: ${{ steps.check.outputs.skip_reason }}
mirror_on_skip: ${{ steps.check.outputs.mirror_on_skip }}
steps:
- name: Check out repository (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Quick no-op: upstream ancestor check
id: check
shell: bash
env:
UPSTREAM_REPO: ${{ env.UPSTREAM_REPO }}
UPSTREAM_BRANCH: ${{ env.UPSTREAM_BRANCH }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch || 'main' }}
MERGE_BRANCH: ${{ env.MERGE_BRANCH }}
run: |
set -euo pipefail
git remote add upstream "https://github.com/${UPSTREAM_REPO}.git" 2>/dev/null || true
git fetch --no-tags --prune --filter=blob:none origin "${DEFAULT_BRANCH}"
HAS_MERGE_BRANCH=false
if git ls-remote --exit-code --heads origin "${MERGE_BRANCH}" >/dev/null 2>&1; then
git fetch --no-tags --prune --filter=blob:none origin "${MERGE_BRANCH}"
HAS_MERGE_BRANCH=true
fi
git fetch --no-tags --prune --filter=blob:none upstream \
"+refs/heads/${UPSTREAM_BRANCH}:refs/remotes/upstream/${UPSTREAM_BRANCH}"
if git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${DEFAULT_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_default" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=true" >> "$GITHUB_OUTPUT"
elif [ "$HAS_MERGE_BRANCH" = true ] && git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${MERGE_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_merge_branch" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_has_new_commits" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Close stale upstream-merge PR on skip when zero diff (opt-in)
if: steps.check.outputs.skip == 'true' && steps.check.outputs.mirror_on_skip == 'true' && env.CLOSE_ZERO_DIFF_ON_SKIP == 'true'
uses: actions/github-script@v7
env:
MERGE_BRANCH: ${{ env.MERGE_BRANCH }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch || 'main' }}
with:
github-token: ${{ secrets.CODE_GH_PAT || github.token }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const head = process.env.MERGE_BRANCH;
const base = process.env.DEFAULT_BRANCH;
const headRef = `${owner}:${head}`;
const prs = await github.rest.pulls.list({ owner, repo, state: 'open', head: headRef });
if (!prs.data.length) { return; }
let zeroDiff = false;
try {
const cmp = await github.rest.repos.compareCommitsWithBasehead({ owner, repo, basehead: `${base}...${head}` });
zeroDiff = (cmp.data.files || []).length === 0;
} catch (e) {
core.warning(`Compare failed (${base}...${head}): ${e.message}. Skipping.`);
return;
}
if (zeroDiff) {
const pr = prs.data[0];
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: 'Closing: upstream merge has no net file changes vs base.' });
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: 'closed' });
core.notice(`Closed PR #${pr.number} due to zero diff (skip path).`);
}
merge:
needs: [precheck]
if: needs.precheck.outputs.skip != 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
@@ -84,6 +166,22 @@ jobs:
git fetch --no-tags --prune --filter=blob:none origin "${MERGE_BRANCH}"
HAS_MERGE_BRANCH=true
fi
git fetch --no-tags --prune --filter=blob:none upstream \
"+refs/heads/${UPSTREAM_BRANCH}:refs/remotes/upstream/${UPSTREAM_BRANCH}"
# Evaluate ancestor relationships and expose results as step outputs
if git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${DEFAULT_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_default" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=true" >> "$GITHUB_OUTPUT"
elif [ "$HAS_MERGE_BRANCH" = true ] && git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${MERGE_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_merge_branch" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_has_new_commits" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Summarize quick check
if: always()
@@ -94,24 +192,6 @@ jobs:
echo "- reason: ${{ steps.check_upstream.outputs.skip_reason || 'n/a' }}";
echo "- mirror_on_skip: ${{ steps.check_upstream.outputs.mirror_on_skip || 'false' }}";
} >> "$GITHUB_STEP_SUMMARY"
git fetch --no-tags --prune --filter=blob:none upstream \
"+refs/heads/${UPSTREAM_BRANCH}:refs/remotes/upstream/${UPSTREAM_BRANCH}"
# Evaluate ancestor relationships
if git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${DEFAULT_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_default" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=true" >> "$GITHUB_OUTPUT"
echo "No new upstream commits beyond default; skipping.";
elif [ "$HAS_MERGE_BRANCH" = true ] && git merge-base --is-ancestor "upstream/${UPSTREAM_BRANCH}" "origin/${MERGE_BRANCH}"; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_ancestor_of_merge_branch" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
echo "Upstream already contained in merge branch '${MERGE_BRANCH}'; skipping to avoid duplicate runs.";
else
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "skip_reason=upstream_has_new_commits" >> "$GITHUB_OUTPUT"
echo "mirror_on_skip=false" >> "$GITHUB_OUTPUT"
fi
# Expensive setup only runs if we are not skipping.
- name: Setup Rust toolchain (match repo)