chore: import upstream snapshot with attribution
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:23:53 +08:00
commit b3a7f98e5a
3020 changed files with 935484 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
// Attach sandboxed-plugin tarballs to the GitHub releases that
// `changesets/action` creates during a release run.
//
// The decentralized plugin registry (RFC 0001) stores only a *link* to the
// plugin bytes, not the bytes themselves. By bundling each published
// sandboxed plugin and uploading the tarball as a release asset, every
// version automatically gets a stable public URL that an `emdash-plugin
// publish --url ...` step (or a human) can point a registry record at.
//
// Input: the `publishedPackages` output from changesets/action, passed via
// the PUBLISHED_PACKAGES env var as a JSON array of `{ name, version }`.
// Only packages under `packages/plugins/*` that expose a `./sandbox` export
// and are not private are processed; everything else (native plugins, test
// fixtures) is skipped.
//
// Set DRY_RUN=1 to bundle and resolve tarballs without calling `gh`.
import { execFileSync } from "node:child_process";
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
const PLUGINS_DIR = "packages/plugins";
const BUNDLER = "packages/plugin-cli/dist/index.mjs";
const SLASH_RE = /\//g;
const LEADING_AT_RE = /^@/;
const repo = process.env.GITHUB_REPOSITORY;
const dryRun = process.env.DRY_RUN === "1";
if (!dryRun && !repo) {
console.error("GITHUB_REPOSITORY is not set; cannot target `gh release upload`.");
process.exit(1);
}
// `?? "[]"` only catches undefined/null. An unset Actions output arrives as
// an empty string, which would make JSON.parse throw, so coalesce that too.
const raw = process.env.PUBLISHED_PACKAGES?.trim() || "[]";
let published;
try {
published = JSON.parse(raw);
} catch (error) {
console.error(`Could not parse PUBLISHED_PACKAGES as JSON: ${error.message}`);
process.exit(1);
}
if (!Array.isArray(published) || published.length === 0) {
console.log("No published packages to process.");
process.exit(0);
}
// name -> version for the packages that were just published.
const publishedVersions = new Map(published.map((p) => [p.name, p.version]));
/** Slugify a manifest id the same way the bundler names its tarball. */
const slugify = (id) => id.replace(SLASH_RE, "-").replace(LEADING_AT_RE, "");
const failures = [];
let attached = 0;
for (const entry of readdirSync(PLUGINS_DIR, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
const dir = join(PLUGINS_DIR, entry.name);
const pkgPath = join(dir, "package.json");
if (!existsSync(pkgPath)) continue;
// We read every plugin dir, so a single malformed package.json must not
// abort the whole step. Treat an unreadable manifest as a skip.
let pkg;
try {
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
} catch {
console.warn(`Skipping ${entry.name}: could not read/parse package.json`);
continue;
}
// Only published, public, sandboxed plugins.
if (!publishedVersions.has(pkg.name)) continue;
if (pkg.private === true) continue;
if (!pkg.exports?.["./sandbox"]) {
console.log(`Skipping ${pkg.name}: no ./sandbox export (not a sandboxed plugin).`);
continue;
}
const version = publishedVersions.get(pkg.name);
const tag = `${pkg.name}@${version}`;
console.log(`\n=== ${pkg.name}@${version} ===`);
try {
// Bundle. This rebuilds from source and writes dist/<slug>-<version>.tar.gz
// plus dist/manifest.json, so we can read the exact id/version back out
// rather than guessing the filename (stale tarballs may linger in dist/).
execFileSync("node", [BUNDLER, "bundle", "--dir", dir], { stdio: "inherit" });
const manifest = JSON.parse(readFileSync(join(dir, "dist", "manifest.json"), "utf-8"));
const tarball = join(dir, "dist", `${slugify(manifest.id)}-${manifest.version}.tar.gz`);
if (!existsSync(tarball)) {
throw new Error(`Expected tarball not found: ${tarball}`);
}
if (dryRun) {
console.log(`[dry-run] would upload ${tarball} to release ${tag}`);
} else {
// --clobber so re-runs replace the asset instead of failing.
execFileSync("gh", ["release", "upload", tag, tarball, "--clobber", "--repo", repo], {
stdio: "inherit",
});
console.log(`Attached ${tarball} to ${tag}`);
}
attached++;
} catch (error) {
console.error(`Failed to attach tarball for ${pkg.name}: ${error.message}`);
failures.push(pkg.name);
}
}
console.log(`\nAttached ${attached} plugin tarball(s).`);
if (failures.length > 0) {
console.error(`Failed: ${failures.join(", ")}`);
process.exit(1);
}
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
import { readFileSync } from "node:fs";
import { glob } from "node:fs/promises";
const offenders = [];
const seen = [];
for await (const file of glob("**/package.json", {
exclude: (path) =>
path.includes("node_modules") || path.includes("/dist/") || path.includes("/.git/"),
})) {
let pkg;
try {
pkg = JSON.parse(readFileSync(file, "utf8"));
} catch {
continue;
}
if (pkg.private || !pkg.name || !pkg.version) continue;
seen.push(`${pkg.name}@${pkg.version}`);
const major = Number.parseInt(pkg.version.split(".")[0], 10);
if (Number.isFinite(major) && major >= 1) {
offenders.push(`${pkg.name}@${pkg.version} (${file})`);
}
}
if (offenders.length > 0) {
console.error("::error::Non-0.x versions detected. Releases must stay in 0.x while in pre-1.0:");
for (const o of offenders) console.error(` ${o}`);
process.exit(1);
}
console.log(`Checked ${seen.length} non-private packages, all are 0.x.`);
+28
View File
@@ -0,0 +1,28 @@
import { execFileSync } from "node:child_process";
const mode = process.argv[2];
const run = (args) => execFileSync("pnpm", args, { stdio: "inherit" });
// changesets/action runs `git checkout changeset-release/main` + `git reset
// --hard` immediately before this, which can leave the deps state out of
// sync with pnpm-workspace.yaml. The next gated pnpm call
// (verifyDepsBeforeRun: error) would then abort the release. `pnpm install`
// is not gated, so reconcile here, after the action's git work and before
// any `pnpm changeset` call. Do not hoist this into an earlier workflow
// step: the action's git reset runs after workflow steps and undoes it.
// Must be --no-frozen-lockfile, not --prefer-frozen-lockfile: prefer-frozen
// can take the lockfile fast path and skip rewriting the stale deps-state
// hash, which is the exact condition that trips the gate.
run(["install", "--no-frozen-lockfile"]);
if (mode === "version") {
run(["changeset", "version"]);
run(["install", "--no-frozen-lockfile"]);
} else if (mode === "publish") {
run(["changeset", "publish"]);
} else {
throw new Error(
`Unknown release mode: ${JSON.stringify(mode)} (expected "version" or "publish")`,
);
}
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env node
// Resolves the model alias from a /bonk, /review, or @ask-bonk comment body
// and emits the model id and OPENCODE_CONFIG_CONTENT for the ask-bonk action.
//
// Inputs (env):
// BODY — the raw comment / review body
// GITHUB_OUTPUT — path to the workflow step output file
//
// Outputs (to $GITHUB_OUTPUT):
// alias — resolved alias (default if none requested)
// model — full model id passed to ask-bonk's `model:` input
// opencode_config — JSON string for OPENCODE_CONFIG_CONTENT env var
//
// The first word after a trigger ("/bonk", "/review", or "@ask-bonk") selects
// an alias from .github/bonk-models.json. An absent or unknown word falls
// back to the registry's `default`. Only the selected model is registered in
// the opencode provider config; the rest of the registry stays unused at
// runtime to keep the env var small.
import { appendFileSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const registryPath = resolve(here, "..", "bonk-models.json");
const registry = JSON.parse(readFileSync(registryPath, "utf8"));
const body = process.env.BODY ?? "";
// Match a trigger preceded by start-of-string or whitespace, then capture the
// next bare word. The leading-boundary guard avoids matching "/bonk" as a
// substring of unrelated text (e.g. a URL fragment).
const triggerRe = /(?:^|\s)(?:\/bonk|\/review|@ask-bonk)\s+([a-zA-Z][a-zA-Z0-9_-]*)/;
const match = body.match(triggerRe);
const requested = match ? match[1].toLowerCase() : null;
const fallback = registry.default;
const alias = requested && registry.models[requested] ? requested : fallback;
const entry = registry.models[alias];
if (!entry) {
console.error(`bonk-models.json default "${fallback}" missing from models map`);
process.exit(1);
}
const model = `cloudflare-ai-gateway/${entry.id}`;
// OPENCODE_CONFIG_CONTENT bundles two unrelated overrides:
//
// 1. Register the selected model with the pinned opencode version. Without
// this, opencode raises ProviderModelNotFoundError on any model whose
// release_date is after its bundled models.dev snapshot.
//
// 2. Resolve the two opencode permission defaults that ask interactively
// (`external_directory` and `doom_loop`) so a CI run with no TTY can
// never deadlock waiting for approval. external_directory is
// deny-by-default with /tmp/** and ~/** allowed (scratch files,
// home-dir caches); doom_loop is deny so a stuck loop aborts instead
// of prompting. Repro: PR #769 timed out at 30 min waiting for an
// `external_directory` prompt on `git show ... > /tmp/foo`.
const opencodeConfig = {
permission: {
external_directory: {
"*": "deny",
"/tmp/**": "allow",
"~/**": "allow",
},
doom_loop: "deny",
},
provider: {
"cloudflare-ai-gateway": {
models: {
[entry.id]: entry.registration,
},
},
},
};
const out = process.env.GITHUB_OUTPUT;
if (!out) {
console.error("GITHUB_OUTPUT is not set");
process.exit(1);
}
const eof = "OPENCODE_CONFIG_EOF";
appendFileSync(
out,
[
`alias=${alias}`,
`requested=${requested ?? ""}`,
`model=${model}`,
`opencode_config<<${eof}`,
JSON.stringify(opencodeConfig),
eof,
"",
].join("\n"),
);
console.log(`Resolved alias "${alias}" -> ${model}`);
+278
View File
@@ -0,0 +1,278 @@
#!/usr/bin/env node
// review-queue.mjs -- maintainer CLI for the review/* label workflow.
//
// Zero external deps: only node: builtins, shelling out to `gh`.
//
// Usage:
// node .github/scripts/review-queue.mjs [list] List open PRs grouped by review state.
// node .github/scripts/review-queue.mjs request <pr>... Post `/review` on one or more PRs.
// node .github/scripts/review-queue.mjs request --needs-review
// Post `/review` on every PR labeled
// review/needs-review.
// node .github/scripts/review-queue.mjs --help Show this help.
//
// Options:
// --repo <owner/name> Target repo (default: detected via `gh repo view`).
//
// Requires an authenticated `gh` CLI.
import { spawnSync } from "node:child_process";
// --- gh helpers -------------------------------------------------------------
// Run a gh command, returning stdout. On non-zero exit, print stderr and exit 1.
function gh(args) {
const result = spawnSync("gh", args, { encoding: "utf8" });
if (result.error) {
console.error(`Failed to run gh: ${result.error.message}`);
process.exit(1);
}
if (result.status !== 0) {
const stderr = (result.stderr || "").trim();
console.error(stderr || `gh exited with status ${result.status}`);
process.exit(1);
}
return result.stdout;
}
function detectRepo() {
const out = gh(["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]);
return out.trim();
}
// --- review state derivation ------------------------------------------------
const STATE_ORDER = ["needs-rereview", "needs-review", "awaiting-author", "approved", "unlabeled"];
// PR number argument: optional leading "#", then digits.
const PR_NUMBER_RE = /^#?\d+$/;
const PR_HASH_RE = /^#/;
// Derive a PR's review state from its review/* label.
function reviewState(pr) {
const labels = new Set((pr.labels || []).map((l) => l.name));
for (const state of STATE_ORDER) {
if (state === "unlabeled") continue;
if (labels.has(`review/${state}`)) return state;
}
return "unlabeled";
}
// Collect the size/* and area/* labels for display.
function tagLabels(pr) {
return (pr.labels || [])
.map((l) => l.name)
.filter((n) => n.startsWith("size/") || n.startsWith("area/"));
}
// Derive a coarse CI status from the statusCheckRollup array.
function ciStatus(pr) {
const rollup = pr.statusCheckRollup || [];
if (rollup.length === 0) return "none";
let pending = false;
let failed = false;
for (const check of rollup) {
// Check runs use `status` + `conclusion`; status contexts use `state`.
const conclusion = (check.conclusion || "").toUpperCase();
const status = (check.status || "").toUpperCase();
const state = (check.state || "").toUpperCase();
if (status && status !== "COMPLETED") {
// IN_PROGRESS, QUEUED, PENDING, WAITING, etc.
pending = true;
continue;
}
if (
["FAILURE", "TIMED_OUT", "CANCELLED", "ERROR", "ACTION_REQUIRED", "STARTUP_FAILURE"].includes(
conclusion,
)
) {
failed = true;
continue;
}
if (["FAILURE", "ERROR"].includes(state)) {
failed = true;
continue;
}
if (["PENDING", "EXPECTED"].includes(state)) {
pending = true;
continue;
}
// SUCCESS / NEUTRAL / SKIPPED count as pass.
}
if (failed) return "fail";
if (pending) return "pending";
return "pass";
}
function ageInDays(createdAt) {
const created = new Date(createdAt).getTime();
if (Number.isNaN(created)) return 0;
return Math.floor((Date.now() - created) / (24 * 60 * 60 * 1000));
}
function truncate(str, max) {
const s = str || "";
if (s.length <= max) return s;
return s.slice(0, max - 1) + "\u2026";
}
// --- commands ---------------------------------------------------------------
function fetchOpenPrs(repo) {
const out = gh([
"pr",
"list",
"--repo",
repo,
"--state",
"open",
"--limit",
// gh's effective max; avoids silently omitting PRs from list / --needs-review.
"1000",
"--json",
"number,title,labels,createdAt,updatedAt,author,isDraft,mergeable,reviewDecision,statusCheckRollup",
]);
try {
return JSON.parse(out);
} catch (e) {
console.error(`Could not parse gh output: ${e.message}`);
process.exit(1);
}
}
function cmdList(repo) {
const prs = fetchOpenPrs(repo);
// Bucket PRs by derived state.
const groups = new Map(STATE_ORDER.map((s) => [s, []]));
for (const pr of prs) {
groups.get(reviewState(pr)).push(pr);
}
console.log(`Open PRs in ${repo}: ${prs.length}`);
console.log("");
for (const state of STATE_ORDER) {
const bucket = groups.get(state);
console.log(`== ${state} (${bucket.length}) ==`);
if (bucket.length === 0) {
console.log(" (none)");
console.log("");
continue;
}
// Sort oldest-first within a group so the most stale floats to the top.
bucket.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
for (const pr of bucket) {
const num = `#${pr.number}`.padEnd(6);
const title = truncate(pr.title, 60).padEnd(60);
const author = `(${pr.author?.login || "unknown"})`.padEnd(20);
const tags = tagLabels(pr).join(",");
const tagCol = `[${tags}]`.padEnd(24);
const age = `${ageInDays(pr.createdAt)}d`.padEnd(5);
const ci = `CI:${ciStatus(pr)}`.padEnd(11);
const mergeable = (pr.mergeable || "UNKNOWN").toLowerCase();
console.log(` ${num} ${title} ${author} ${tagCol} ${age} ${ci} ${mergeable}`);
}
console.log("");
}
}
function postReview(repo, number) {
gh(["pr", "comment", String(number), "--repo", repo, "--body", "/review"]);
console.log(`Requested review on #${number}`);
}
function cmdRequest(repo, args) {
if (args.includes("--needs-review")) {
const prs = fetchOpenPrs(repo);
const targets = prs.filter((pr) =>
(pr.labels || []).some((l) => l.name === "review/needs-review"),
);
if (targets.length === 0) {
console.log("No PRs labeled review/needs-review.");
return;
}
console.log(
`Requesting review on ${targets.length} PR(s): ${targets.map((p) => `#${p.number}`).join(", ")}`,
);
for (const pr of targets) {
postReview(repo, pr.number);
}
return;
}
const numbers = args.filter((a) => PR_NUMBER_RE.test(a)).map((a) => a.replace(PR_HASH_RE, ""));
if (numbers.length === 0) {
console.error("request: provide one or more PR numbers, or --needs-review");
process.exit(1);
}
for (const number of numbers) {
postReview(repo, number);
}
}
function printHelp() {
console.log(
[
"review-queue.mjs -- maintainer CLI for the review/* label workflow",
"",
"Usage:",
" review-queue.mjs [list] List open PRs grouped by review state",
" review-queue.mjs request <pr>... Post /review on one or more PRs",
" review-queue.mjs request --needs-review Post /review on every review/needs-review PR",
" review-queue.mjs --help Show this help",
"",
"Options:",
" --repo <owner/name> Target repo (default: detected via gh repo view)",
"",
"Requires an authenticated gh CLI.",
].join("\n"),
);
}
// --- arg parsing ------------------------------------------------------------
function main() {
const argv = process.argv.slice(2);
if (argv.includes("--help") || argv.includes("-h")) {
printHelp();
return;
}
// Pull out --repo <value> if present; the rest are positional.
let repoOverride;
const rest = [];
for (let i = 0; i < argv.length; i++) {
if (argv[i] === "--repo") {
const value = argv[i + 1];
if (!value || value.startsWith("-")) {
console.error("--repo requires a value, e.g. --repo owner/name");
process.exit(1);
}
repoOverride = value;
i++;
continue;
}
rest.push(argv[i]);
}
const repo = repoOverride || detectRepo();
const command = rest[0] || "list";
switch (command) {
case "list":
cmdList(repo);
break;
case "request":
cmdRequest(repo, rest.slice(1));
break;
default:
console.error(`Unknown command: ${command}`);
printHelp();
process.exit(1);
}
}
main();