Skip to content

Feature/userinfo

Feature/userinfo #649

name: Development Merge Check
# Advisory, NON-BLOCKING check for the team convention that branches merge into
# Development before main.
#
# It is a single check on the PR - this job's own pass/fail:
# green when the PR head commit is already in Development
# red when it is not (a reminder, not a hard failure; it does not block a
# merge unless someone marks it a required check)
#
# "In Development" is detected by ancestry, because the team merges into
# Development with a direct `git merge` + push (`npm run merge`), not a PR, so the
# PR head becomes an ancestor of Development. A merged-PR lookup is kept as a
# squash-merge fallback.
#
# Triggers:
# pull_request - evaluate this PR's head on every commit (this is the verdict).
# push: Development - a branch can land in Development without a new PR commit,
# so re-run each still-red PR's check to flip it green.
# workflow_dispatch - manual refresh.
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
branches: [main]
push:
branches: [Development]
workflow_dispatch:
permissions:
contents: read
pull-requests: read
actions: write
concurrency:
group: dev-merge-check-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
development-merge-check:
name: Development merge check (advisory)
runs-on: ubuntu-latest
steps:
- name: Check Development merge status
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const DEV_BRANCH = 'Development';
const WORKFLOW = 'development-merge-check.yml';
const { owner, repo } = context.repo;
// Detail string if the head is in Development, else null.
async function mergedDetail(headRef, headSha, headOwner) {
// Direct merge: npm run merge makes a real merge commit, so the head
// is contained in Development ("behind" or "identical").
try {
const cmp = await github.rest.repos.compareCommitsWithBasehead({
owner, repo, basehead: `${DEV_BRANCH}...${headSha}`,
});
if (cmp.data.status === 'behind' || cmp.data.status === 'identical') {
return `Branch "${headRef}" is on ${DEV_BRANCH}: its commits (through ${headSha.slice(0, 7)}) are already in ${DEV_BRANCH}.`;
}
} catch (e) {
core.info(`compare ${DEV_BRANCH}...${headSha} failed: ${e.message}`);
}
// Fallback: a PR/squash merge into Development.
const devPulls = await github.paginate(github.rest.pulls.list, {
owner, repo, state: 'closed', base: DEV_BRANCH,
head: `${headOwner}:${headRef}`, per_page: 100,
});
const mergedPr = devPulls.find((p) => p.merged_at);
return mergedPr
? `Branch "${headRef}" is on ${DEV_BRANCH} (merged via PR #${mergedPr.number}).`
: null;
}
// pull_request: this job's pass/fail IS the advisory check for the PR.
if (context.eventName === 'pull_request') {
const pr = context.payload.pull_request;
const headOwner = pr.head.repo ? pr.head.repo.owner.login : owner;
const detail = await mergedDetail(pr.head.ref, pr.head.sha, headOwner);
if (detail) {
core.info(detail);
} else {
core.setFailed(
`Branch "${pr.head.ref}" is not on ${DEV_BRANCH} yet. Team workflow ` +
`routes branches through ${DEV_BRANCH} before main. Advisory only; ` +
`it does not block merging.`,
);
}
return;
}
// push to Development / manual dispatch: a branch may have just landed
// in Development without a new PR commit. Re-run each still-red PR's
// check so its verdict refreshes against the updated Development.
const prs = await github.paginate(github.rest.pulls.list, {
owner, repo, state: 'open', base: 'main', per_page: 100,
});
const runs = await github.paginate(github.rest.actions.listWorkflowRuns, {
owner, repo, workflow_id: WORKFLOW, event: 'pull_request', per_page: 100,
});
const latestBySha = new Map();
for (const r of runs) {
if (!latestBySha.has(r.head_sha)) latestBySha.set(r.head_sha, r);
}
for (const pr of prs) {
const run = latestBySha.get(pr.head.sha);
if (run?.conclusion === 'failure') {
try {
await github.rest.actions.reRunWorkflowFailedJobs({
owner, repo, run_id: run.id,
});
core.info(`PR #${pr.number}: re-running advisory check (was red).`);
} catch (e) {
core.warning(`PR #${pr.number}: re-run failed: ${e.message}`);
}
}
}