From 413e8015f17bcf4085e087af43c1d0e5346fce53 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:47:45 -0300 Subject: [PATCH] =?UTF-8?q?feat(ci):=20continuous=20release-green=20?= =?UTF-8?q?=E2=80=94=20on-push=20quick=20gate=20+=203x/day=20full=20sweep?= =?UTF-8?q?=20(WS5.1)=20(#7089)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The v3.8.49 cycle started with what looked like a shared base-red because the tip had NO gate between pushes and the nightly (24h MTTD): the captain's sync-back is a direct push, and merged PR combinations are never validated together. nightly-release-green.yml becomes 'Release-Green (continuous)': - push to release/v* (code paths) → validate-release-green --quick (~5-8min) against exactly the pushed ref, with per-branch concurrency so merge storms collapse to the newest commit. The failure issue now names the offending push range (before..after, one merge per push in the normal queue — direct attribution without bisect). SHAs enter the shell via env (injection-safe); commit subjects go to the issue body through a file, never interpolated. - schedule → full --with-build --full-ci, now 3x/day (05:23/12:23/18:23 UTC). Workflow-only change (no production code); YAML parse validated. --- .github/workflows/nightly-release-green.yml | 78 +++++++++++++++---- .../maintenance/release-green-continuous.md | 1 + 2 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 changelog.d/maintenance/release-green-continuous.md diff --git a/.github/workflows/nightly-release-green.yml b/.github/workflows/nightly-release-green.yml index 0ba8e9301d..9d1d20b565 100644 --- a/.github/workflows/nightly-release-green.yml +++ b/.github/workflows/nightly-release-green.yml @@ -1,11 +1,18 @@ -name: Nightly Release-Green +name: Release-Green (continuous) # Solution D — continuous, NON-BLOCKING drift signal for the active release branch. # # WHY: the full gate (ci.yml) only runs on the release PR (PR → main), so reds # accrue silently on release/** and explode — in layers — at release time. This -# nightly reproduces the release-equivalent validation on the active release branch -# HEAD and, when there are HARD failures, opens/updates a single tracking issue. +# workflow reproduces the release-equivalent validation on the release branch and, +# when there are HARD failures, opens/updates a single tracking issue. +# +# WS5.1 (v3.8.49 quality plan) — two modes: +# push to release/v* (code paths) → --quick (fast HARD gates, ~5-8min). Catches the +# captain's direct pushes (sync-back — the one ungated write path) AND the merged +# COMBINATION right after every PR merge, attributing the offending push range in +# the issue. Base-red MTTD drops from ≤24h to ≤~15min after the offending push. +# schedule (3×/day) → full --with-build --full-ci (the deep sweep incl. build+suites). # # It is NOT a required status check and never touches a contributor PR — it only # reports. Ratchet drift (eslint warnings / cognitive-complexity / file-size) is @@ -14,8 +21,23 @@ name: Nightly Release-Green # package-artifact) flip the issue open. on: + push: + branches: ["release/v*"] + paths: + - "src/**" + - "open-sse/**" + - "bin/**" + - "electron/**" + - "scripts/**" + - "tests/**" + - "config/**" + - "package.json" + - "package-lock.json" + - "tsconfig*.json" schedule: - - cron: "23 5 * * *" # 05:23 UTC daily — off-peak, distinct from other nightlies + - cron: "23 5 * * *" # full sweep — off-peak, distinct from other nightlies + - cron: "23 12 * * *" # full sweep — midday (WS5.1: 3×/day instead of 1×) + - cron: "23 18 * * *" # full sweep — evening workflow_dispatch: inputs: branch: @@ -28,7 +50,9 @@ permissions: issues: write concurrency: - group: nightly-release-green + # push storms during merge campaigns collapse to the newest commit per branch; + # scheduled full sweeps keep their own single lane. + group: release-green-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true env: @@ -56,10 +80,15 @@ jobs: id: branch env: INPUT_BRANCH: ${{ github.event.inputs.branch }} + EVENT_NAME: ${{ github.event_name }} + PUSHED_REF: ${{ github.ref_name }} run: | set -euo pipefail if [ -n "${INPUT_BRANCH:-}" ]; then TARGET="$INPUT_BRANCH" + elif [ "$EVENT_NAME" = "push" ]; then + # validate exactly what was pushed, not the highest branch + TARGET="$PUSHED_REF" else # highest release/vX.Y.Z by semver among remote branches TARGET=$(git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/release/v*' \ @@ -93,16 +122,26 @@ jobs: - name: Release-green validation (full) id: validate + env: + EVENT_NAME: ${{ github.event_name }} run: | set +e # --hermetic: scrub live-test trigger vars (self-hosted runner may carry # operator env; hosted ignores the unknown flag before #6300 lands). - # --full-ci: ALSO run every static gate from ci.yml's gate jobs (lint, - # quality-gate, quality-extended, docs-sync-strict, pr-test-policy). PRs into - # release/** only get the fast-gates, so these accrue silently and explode in - # layers on the release PR (v3.8.46: 11 static base-reds leaked). Running them - # nightly opens the tracking issue the moment one lands, not at release time. - node scripts/quality/validate-release-green.mjs --json --with-build --hermetic --full-ci \ + # push → --quick: fast HARD gates only (~5-8min), per-merge signal. + # schedule/dispatch → --with-build --full-ci: ALSO run every static gate from + # ci.yml's gate jobs (lint, quality-gate, quality-extended, docs-sync-strict, + # pr-test-policy) + build + full suites. PRs into release/** only get the + # fast-gates, so these accrue silently and explode in layers on the release PR + # (v3.8.46: 11 static base-reds leaked). + if [ "$EVENT_NAME" = "push" ]; then + MODE="--quick" + else + MODE="--with-build --full-ci" + fi + echo "[release-green] mode: $MODE (event: $EVENT_NAME)" + # shellcheck disable=SC2086 — MODE is an intentional flag list + node scripts/quality/validate-release-green.mjs --json --hermetic $MODE \ 1> release-green.json 2> release-green.log echo "exit=$?" >> "$GITHUB_OUTPUT" echo "------- report -------" @@ -114,15 +153,28 @@ jobs: GH_TOKEN: ${{ github.token }} TARGET: ${{ steps.branch.outputs.target }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + EVENT_NAME: ${{ github.event_name }} + BEFORE_SHA: ${{ github.event.before }} + AFTER_SHA: ${{ github.event.after }} run: | set -euo pipefail TITLE="🔴 Release branch not green: ${TARGET}" { - echo "The nightly **release-green** validation found HARD failures on \`${TARGET}\`." + echo "The **release-green** validation found HARD failures on \`${TARGET}\`." echo "These are real defects that would block the release PR — fix them in the" echo "originating PR branch (via co-authorship), not by demanding it from contributors." echo "" - echo "**Run:** ${RUN_URL}" + echo "**Run:** ${RUN_URL} (mode: ${EVENT_NAME})" + # WS5.1 attribution: on push events the offending change IS this push's range + # (one merge per push in the normal queue), so name it — no bisect needed. + if [ "$EVENT_NAME" = "push" ] && [ -n "${BEFORE_SHA:-}" ] && \ + git cat-file -e "$BEFORE_SHA" 2>/dev/null; then + echo "" + echo "**Offending push range** (\`${BEFORE_SHA:0:9}..${AFTER_SHA:0:9}\`):" + echo '```' + git log --no-decorate --oneline "${BEFORE_SHA}..${AFTER_SHA}" | head -20 + echo '```' + fi echo "" echo '```' sed -n '/──────── verdict ────────/,$p' release-green.log || tail -40 release-green.log diff --git a/changelog.d/maintenance/release-green-continuous.md b/changelog.d/maintenance/release-green-continuous.md new file mode 100644 index 0000000000..b3c181510d --- /dev/null +++ b/changelog.d/maintenance/release-green-continuous.md @@ -0,0 +1 @@ +- **CI**: release-green validation is now continuous — every code push to `release/v*` (including the captain's direct sync-back pushes, the one previously ungated write path) triggers a `--quick` HARD-gate run with per-branch superseded-run cancellation, and the tracking issue names the offending push range; the deep `--with-build --full-ci` sweep now runs 3×/day instead of nightly-only (base-red MTTD: ≤24h → ~15min after the offending push)