Skip to content

Commit 2133fec

Browse files
committed
merge: reconcile context telemetry with append-only persistence
Preserve effective input-token analytics from the context-budget branch while adopting mainline batched append-only message event persistence. Generated-By: looper 0.11.8 (runner=fixer, agent=codex)
2 parents 11d8104 + eee0377 commit 2133fec

760 files changed

Lines changed: 57506 additions & 8421 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/setup-playwright/action.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,28 @@ runs:
9999
package_dir="${PACKAGE_JSON_PATH%/*}"
100100
pnpm -C "$package_dir" exec playwright install chromium
101101
102+
- name: Pin Blacksmith apt mirror to the canonical archive
103+
if: ${{ steps.preinstalled-playwright.outputs.enabled != 'true' }}
104+
shell: bash
105+
env:
106+
RUNNER_LABELS_JSON: ${{ inputs.runner-labels }}
107+
run: |
108+
# Blacksmith VMs resolve apt through a mirrorlist of third-party
109+
# university mirrors. When one goes down, apt retries it for every
110+
# index file before falling back, so the `apt-get update` inside
111+
# `playwright install --with-deps` hangs until the job timeout.
112+
# Browser OS deps are NOT fully preinstalled on the Blacksmith image
113+
# (xvfb, fonts, libasound are missing), so apt must stay in the path;
114+
# pin the canonical archive so it stays deterministic instead.
115+
case "$RUNNER_LABELS_JSON" in
116+
*'"blacksmith-'*) ;;
117+
*) exit 0 ;;
118+
esac
119+
mirrorlist=/etc/apt/blacksmith-ubuntu-mirrors.txt
120+
if [ -f "$mirrorlist" ]; then
121+
printf 'http://archive.ubuntu.com/ubuntu\tpriority:1\n' | sudo tee "$mirrorlist"
122+
fi
123+
102124
- name: Install Playwright browsers
103125
if: ${{ steps.preinstalled-playwright.outputs.enabled != 'true' }}
104126
shell: bash
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#!/usr/bin/env node
2+
3+
// Purge Cloudflare edge cache for the marketing host(s) by hostname.
4+
//
5+
// Used by `landing-page-production` after `pages deploy` so locale HTML
6+
// (`/`, `/zh/pricing/`, …) does not keep serving pre-deploy objects when
7+
// CF Pages' deploy invalidation is incomplete for the custom domain, and by
8+
// `landing-edge-cache-purge` for manual incident recovery / token checks.
9+
//
10+
// Inputs (all via env):
11+
// CLOUDFLARE_API_TOKEN (required) token with Zone → Cache Purge on the zone
12+
// CLOUDFLARE_ZONE_ID (optional) defaults to the open-design.ai zone id
13+
// CLOUDFLARE_PURGE_HOSTS (optional) comma-separated hostnames, default
14+
// `open-design.ai`
15+
//
16+
// Scope: hosts purge is intentional — one host, all paths/locales, without
17+
// touching other hostnames on the same zone (e.g. download.open-design.ai).
18+
//
19+
// @see https://developers.cloudflare.com/api/resources/cache/methods/purge/
20+
// @see https://developers.cloudflare.com/cache/how-to/purge-cache/purge-by-hostname/
21+
22+
type PurgeResponse = {
23+
success?: boolean;
24+
errors?: unknown;
25+
result?: unknown;
26+
};
27+
28+
const DEFAULT_ZONE_ID = "84ed4658186179c7eba52659b6ef48ad";
29+
30+
const token = process.env.CLOUDFLARE_API_TOKEN?.trim();
31+
const zoneId = process.env.CLOUDFLARE_ZONE_ID?.trim() || DEFAULT_ZONE_ID;
32+
const hosts = (process.env.CLOUDFLARE_PURGE_HOSTS || "open-design.ai")
33+
.split(",")
34+
.map((host) => host.trim())
35+
.filter(Boolean);
36+
37+
if (!token) {
38+
console.error("CLOUDFLARE_API_TOKEN is required");
39+
process.exit(1);
40+
}
41+
if (hosts.length === 0) {
42+
console.error("CLOUDFLARE_PURGE_HOSTS resolved to an empty host list");
43+
process.exit(1);
44+
}
45+
46+
const response = await fetch(
47+
`https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,
48+
{
49+
method: "POST",
50+
headers: {
51+
Authorization: `Bearer ${token}`,
52+
"Content-Type": "application/json",
53+
},
54+
body: JSON.stringify({ hosts }),
55+
},
56+
);
57+
58+
const payload = (await response
59+
.json()
60+
.catch(() => null)) as PurgeResponse | null;
61+
const ok = response.ok && payload?.success === true;
62+
63+
console.log(
64+
JSON.stringify(
65+
{
66+
httpStatus: response.status,
67+
zoneId,
68+
hosts,
69+
success: ok,
70+
errors: payload?.errors ?? null,
71+
result: payload?.result ?? null,
72+
},
73+
null,
74+
2,
75+
),
76+
);
77+
78+
if (!ok) {
79+
console.error(
80+
`Cloudflare host purge failed. Ensure CLOUDFLARE_API_TOKEN has Zone.Cache Purge on ${hosts.join(", ")}.`,
81+
);
82+
process.exit(1);
83+
}

.github/scripts/rerun_infra_cancel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import urllib.error
3131
import urllib.parse
3232
import urllib.request
33-
from typing import Any, Callable
33+
from typing import Any, Callable, Optional
3434

3535
DEFAULT_MAX_ATTEMPT = 2
3636
ALLOWED_EVENTS = frozenset({"pull_request", "merge_group"})
@@ -63,7 +63,7 @@
6363
ANNOTATIONS_PER_PAGE = 100
6464

6565

66-
GhRequest = Callable[[str, str, dict[str, str] | None, str | None], Any]
66+
GhRequest = Callable[[str, str, Optional[dict[str, str]], Optional[str]], Any]
6767

6868

6969
class Skip(Exception):

.github/workflows/ci.yml

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ jobs:
669669
timeout-minutes: 5
670670

671671
steps:
672-
# The needs-validation gate below produces a `handoff/comment` artifact when it ejects a
672+
# The merge-blocking label gate below produces a `handoff/comment` artifact when it ejects a
673673
# queued PR, and that production goes through `.github/scripts/handoff.py` (the only
674674
# sanctioned source of handoff names and layout). Only the merge_group context can eject,
675675
# so pull_request runs skip the checkout entirely.
@@ -721,12 +721,12 @@ jobs:
721721
exit 1
722722
fi
723723
724-
- name: Block merge while the needs-validation label is present
725-
id: needs_validation_gate
726-
# Hard gate: a PR that still carries `needs-validation` must not merge. Enforced ONLY in
727-
# the merge_group (merge-queue) context — never on pull_request. main requires the merge
728-
# queue, so every merge passes through merge_group; a needs-validation entry fails this
729-
# step there and is ejected from the queue, so it can never land.
724+
- name: Block merge while a merge-blocking label is present
725+
id: merge_blocking_label_gate
726+
# Hard gate: a PR that still carries `needs-validation` or `needs-maintainer-check` must
727+
# not merge. Enforced ONLY in the merge_group (merge-queue) context — never on
728+
# pull_request. main requires the merge queue, so every merge passes through merge_group;
729+
# a labeled entry fails this step there and is ejected from the queue, so it can never land.
730730
#
731731
# Why not also fail on pull_request: that turns the PR's own required `Validate workspace`
732732
# check red, and a deliberately-red required check is indistinguishable from a real CI
@@ -736,7 +736,8 @@ jobs:
736736
# forever). The merge_group run executes on the queue's transient ref, so this failure does
737737
# NOT appear in the PR head's status rollup; the PR stays green until the label is cleared.
738738
# Fails closed: any label-lookup error blocks rather than silently waving the merge through.
739-
# Respects skip-validation implicitly (that override means needs-validation is never added).
739+
# `skip-validation` continues to override only the `needs-validation` producer; it has no
740+
# effect on the independent maintainer-check policy.
740741
env:
741742
GH_TOKEN: ${{ github.token }}
742743
EVENT_NAME: ${{ github.event_name }}
@@ -752,7 +753,7 @@ jobs:
752753
run: |
753754
set -euo pipefail
754755
if [ "$EVENT_NAME" != "merge_group" ]; then
755-
echo "needs-validation is gated at merge time in the merge_group context; nothing to enforce on $EVENT_NAME (PR check stays green)."
756+
echo "Merge-blocking labels are gated in the merge_group context; nothing to enforce on $EVENT_NAME (PR check stays green)."
756757
else
757758
# An ejection is invisible from the PR: this failure runs on the queue's transient
758759
# ref, the PR's own checks stay green, and `mergeQueueEntry` just goes null. Leave a
@@ -761,25 +762,41 @@ jobs:
761762
# completes. Best-effort: a handoff failure must never soften the block itself.
762763
emit_ejection_notice() {
763764
local pr="$1"
764-
local pr_json pr_head pr_base handoff_id handoff_root handoff_dir marker
765+
local blocking_label="$2"
766+
local pr_json pr_head pr_base handoff_id handoff_root handoff_dir marker next_step
765767
pr_json="$(gh api "repos/$REPO/pulls/$pr")"
766768
pr_head="$(jq -r '.head.sha' <<< "$pr_json")"
767769
pr_base="$(jq -r '.base.sha' <<< "$pr_json")"
768-
handoff_id="needs-validation-pr-$pr"
770+
case "$blocking_label" in
771+
needs-validation)
772+
handoff_id="needs-validation-pr-$pr"
773+
marker="<!-- merge-queue-needs-validation -->"
774+
# Markdown code spans are intentional literal text.
775+
# shellcheck disable=SC2016
776+
next_step='To land this PR: complete the QA pass the label is tracking, remove the `needs-validation` label, then add the PR back to the merge queue.'
777+
;;
778+
needs-maintainer-check)
779+
handoff_id="needs-maintainer-check-pr-$pr"
780+
marker="<!-- merge-queue-needs-maintainer-check -->"
781+
# Markdown code spans are intentional literal text.
782+
# shellcheck disable=SC2016
783+
next_step='To land this PR: have a maintainer complete the check, remove the `needs-maintainer-check` label, then add the PR back to the merge queue.'
784+
;;
785+
*)
786+
echo "Unsupported merge-blocking label: $blocking_label" >&2
787+
return 1
788+
;;
789+
esac
769790
handoff_root="$RUNNER_TEMP/handoff-comment-$handoff_id"
770791
handoff_dir="$(python3 .github/scripts/handoff.py dir comment "$handoff_id" --root "$handoff_root")"
771792
mkdir -p "$handoff_dir"
772-
marker="<!-- merge-queue-needs-validation -->"
773793
# Markdown code spans are intentionally literal in these single-quoted strings.
774794
# shellcheck disable=SC2016
775795
{
776796
printf '%s\n' "$marker"
777-
# Markdown code spans are intentional literal text.
778-
# shellcheck disable=SC2016
779-
printf 'Ejected from the merge queue: this PR still carries the `needs-validation` label.\n\n'
797+
printf 'Ejected from the merge queue: this PR still carries the `%s` label.\n\n' "$blocking_label"
780798
printf 'The merge queue gate ([run %s](%s/%s/actions/runs/%s)) blocked the queued group because of the label. That failure runs on the queue'"'"'s transient ref, so it never appears in this PR'"'"'s own checks — they stay green, and this notice is the only visible trace on the PR.\n\n' "$RUN_ID" "$GITHUB_SERVER_URL" "$REPO" "$RUN_ID"
781-
# shellcheck disable=SC2016
782-
printf 'To land this PR: complete the QA pass the label is tracking, remove the `needs-validation` label, then add the PR back to the merge queue.\n'
799+
printf '%s\n' "$next_step"
783800
} > "$handoff_dir/body.md"
784801
jq -n \
785802
--arg kind "comment" \
@@ -843,31 +860,37 @@ jobs:
843860
fi
844861
covered=1
845862
case " $checked " in *" $pr "*) ;; *) checked="$checked $pr";; esac
846-
if printf '%s\n' "$labels" | grep -qx 'needs-validation'; then
847-
echo "::error::PR #$pr still has 'needs-validation' — blocking merge."
863+
blocking_label=""
864+
if printf '%s\n' "$labels" | grep -qx 'needs-maintainer-check'; then
865+
blocking_label="needs-maintainer-check"
866+
elif printf '%s\n' "$labels" | grep -qx 'needs-validation'; then
867+
blocking_label="needs-validation"
868+
fi
869+
if [ -n "$blocking_label" ]; then
870+
echo "::error::PR #$pr still has '$blocking_label' — blocking merge."
848871
# Fail-fast keeps this per-run notice on the FIRST labeled PR found; every
849872
# labeled entry also fails its own queue run, so each still gets its own notice.
850-
emit_ejection_notice "$pr" || echo "::warning::could not produce the ejection-notice handoff for PR #$pr; the block itself still stands."
873+
emit_ejection_notice "$pr" "$blocking_label" || echo "::warning::could not produce the ejection-notice handoff for PR #$pr; the block itself still stands."
851874
exit 1
852875
fi
853-
echo "PR #$pr: no needs-validation label."
876+
echo "PR #$pr: no merge-blocking label."
854877
done
855878
if [ "$covered" -eq 0 ]; then
856-
echo "::error::merge-group commit $sha resolved to no pull request (candidates: ${candidates:-none}) — cannot verify needs-validation; blocking merge (fail closed)."
879+
echo "::error::merge-group commit $sha resolved to no pull request (candidates: ${candidates:-none}) — cannot verify merge-blocking labels; blocking merge (fail closed)."
857880
exit 1
858881
fi
859882
done
860-
echo "No 'needs-validation' label in the queued group ($checked ) — clear to merge."
883+
echo "No merge-blocking label in the queued group ($checked ) — clear to merge."
861884
fi
862885
863886
# `failure()` is required: the gate exits 1 on the very path that produces the handoff,
864887
# and the default `success()` condition would skip this upload exactly when it matters.
865888
- name: Upload merge-queue ejection notice handoff
866-
if: ${{ failure() && steps.needs_validation_gate.outputs.comment_created == 'true' }}
889+
if: ${{ failure() && steps.merge_blocking_label_gate.outputs.comment_created == 'true' }}
867890
uses: actions/upload-artifact@v4
868891
with:
869-
name: ${{ steps.needs_validation_gate.outputs.comment_name }}
870-
path: ${{ steps.needs_validation_gate.outputs.comment_path }}
892+
name: ${{ steps.merge_blocking_label_gate.outputs.comment_name }}
893+
path: ${{ steps.merge_blocking_label_gate.outputs.comment_path }}
871894

872895
runtime_summary:
873896
name: Runtime summary
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Contributor maintainer check
2+
3+
on:
4+
pull_request_target:
5+
types: [opened, reopened, synchronize]
6+
7+
permissions:
8+
issues: write
9+
10+
concurrency:
11+
group: contributor-maintainer-check-${{ github.event.pull_request.number }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
route:
16+
if: github.repository == 'nexu-io/open-design'
17+
runs-on: ubuntu-24.04
18+
timeout-minutes: 5
19+
20+
steps:
21+
- name: Require maintainer check for configured contributors
22+
uses: actions/github-script@v8
23+
env:
24+
NEEDS_MAINTAINER_CHECK_USER_IDS: ${{ secrets.NEEDS_MAINTAINER_CHECK_USER_IDS }}
25+
with:
26+
script: |
27+
const rawIds = process.env.NEEDS_MAINTAINER_CHECK_USER_IDS?.trim();
28+
const configuredIds = rawIds ? JSON.parse(rawIds) : [];
29+
if (!Array.isArray(configuredIds) || !configuredIds.every((id) => Number.isSafeInteger(id) && id > 0)) {
30+
core.setFailed('NEEDS_MAINTAINER_CHECK_USER_IDS must be a JSON array of positive integer GitHub user IDs.');
31+
return;
32+
}
33+
34+
const authorId = context.payload.pull_request?.user?.id;
35+
if (!Number.isSafeInteger(authorId) || !configuredIds.includes(authorId)) {
36+
return;
37+
}
38+
39+
await github.rest.issues.addLabels({
40+
owner: context.repo.owner,
41+
repo: context.repo.repo,
42+
issue_number: context.payload.pull_request.number,
43+
labels: ['needs-maintainer-check'],
44+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: landing-edge-cache-purge
2+
3+
# Manual / test entry to host-purge open-design.ai (or staging) without a full
4+
# production rebuild. Production deploy also purges after pages deploy; this
5+
# workflow is for incident recovery and permission checks.
6+
#
7+
# Requires secrets.CLOUDFLARE_API_TOKEN with Zone → Cache Purge on the target
8+
# zone. Optional: vars.CLOUDFLARE_ZONE_ID.
9+
10+
on:
11+
workflow_dispatch:
12+
inputs:
13+
hosts:
14+
description: 'Comma-separated hostnames to purge (no scheme)'
15+
required: true
16+
default: 'open-design.ai'
17+
18+
permissions:
19+
contents: read
20+
21+
jobs:
22+
purge:
23+
name: Purge Cloudflare edge cache by hostname
24+
if: github.repository == 'nexu-io/open-design'
25+
runs-on: ubuntu-latest
26+
timeout-minutes: 5
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v6.0.2
30+
31+
- name: Setup Node.js
32+
uses: actions/setup-node@v6
33+
with:
34+
node-version: 24
35+
36+
- name: Purge hosts
37+
env:
38+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
39+
CLOUDFLARE_ZONE_ID: ${{ vars.CLOUDFLARE_ZONE_ID }}
40+
CLOUDFLARE_PURGE_HOSTS: ${{ inputs.hosts }}
41+
run: node --experimental-strip-types .github/scripts/landing-page-purge-edge-cache.ts

0 commit comments

Comments
 (0)