mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
ci(release): split channel pipelines and automate version transitions (#345)
* ci(release): split channel pipelines and automate version transitions Introduce dedicated next/release orchestration workflows backed by a reusable publish pipeline with RC/final package version updates and post-release main dev bump PRs. Add a strict release process SOP with RC freeze, hotfix override, and mandatory backport rules. * ci(release): gate main-to-next promotion on green CI Add a dedicated workflow that validates main branch checks/statuses and block the promote-main-to-next flow unless main is green. Update the release SOP to reflect the enforced pre-promotion validation. * ci(release): fix channel promotion edge cases and policy gaps Switch channel promotions to reset-based snapshots with force-with-lease pushes, stop auto-merging channel nix-refresh PRs, and guard main dev bump against version downgrades. Add RC changelog fallback, require AUR release updates in SOP, and extend npmDepsHash sync to main/next/release pushes. * docs(release): clarify backport and nix refresh survivability rules Require RC fix backports to reach main before the next main-to-next promotion, and document that channel-local nix refresh PRs are advisory unless equivalent changes are merged into main.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
name: Validate Main Green CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "Branch to validate"
|
||||
required: false
|
||||
default: main
|
||||
type: string
|
||||
workflow_call:
|
||||
inputs:
|
||||
branch:
|
||||
required: false
|
||||
default: main
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
checks: read
|
||||
statuses: read
|
||||
steps:
|
||||
- name: validate branch checks are green
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
TARGET_BRANCH: ${{ inputs.branch || github.event.inputs.branch || 'main' }}
|
||||
with:
|
||||
script: |
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const branch = process.env.TARGET_BRANCH || "main";
|
||||
|
||||
const branchResp = await github.rest.repos.getBranch({ owner, repo, branch });
|
||||
const sha = branchResp.data.commit.sha;
|
||||
core.info(`Validating checks for ${branch} @ ${sha}`);
|
||||
|
||||
const combined = await github.rest.repos.getCombinedStatusForRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: sha,
|
||||
});
|
||||
const statusState = combined.data.state;
|
||||
const statusCount = combined.data.statuses.length;
|
||||
core.info(`Combined status state: ${statusState} (${statusCount} status contexts)`);
|
||||
|
||||
const checks = await github.paginate(github.rest.checks.listForRef, {
|
||||
owner,
|
||||
repo,
|
||||
ref: sha,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
if (checks.length === 0 && statusCount === 0) {
|
||||
core.setFailed(`No checks/statuses found for ${branch}. Refusing promotion.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const badChecks = checks.filter((c) => {
|
||||
if (c.status !== "completed") return true;
|
||||
return !["success", "neutral", "skipped"].includes(c.conclusion || "");
|
||||
});
|
||||
|
||||
if (badChecks.length > 0) {
|
||||
const details = badChecks.map((c) => `${c.name}: status=${c.status}, conclusion=${c.conclusion}`).join("\n");
|
||||
core.setFailed(`Branch ${branch} is not green.\n${details}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!["success", "neutral"].includes(statusState)) {
|
||||
core.setFailed(`Combined status is ${statusState} for ${branch}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(`Branch ${branch} is green.`);
|
||||
Reference in New Issue
Block a user