Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions .github/scripts/verify-version-pr-scope.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,26 @@
# mechanical `changeset version` output before an unattended merge.
#
# Two layers, both fail-closed:
# 1. Path allowlist: consumed changesets (.changeset/*.md) and per-package
# package.json / CHANGELOG.md / src/version.ts. No root package.json
# (private, unversioned), no lockfile (workspace:* deps don't touch it).
# src/version.ts is the committed gen:version output (see publish.yaml's
# `pnpm run version` note) — a Version PR bumping @openrouter/mcp always
# carries it, so excluding it would fail every such train run.
# 1. Path allowlist: consumed changesets (.changeset/*.md), per-package
# package.json / CHANGELOG.md, and the committed gen:version outputs
# listed in GENERATED_VERSION_FILES below (see publish.yaml's
# `pnpm run version` note). No root package.json (private, unversioned),
# no lockfile (workspace:* deps don't touch it). A Version PR bumping
# @openrouter/agent always carries packages/agent/src/mcp/version.ts, so
# leaving it off the list fails every such train run (it moved there from
# packages/mcp/src/version.ts in #102, which stalled the train).
# 2. Content vet for package.json: paths alone are not enough — a smuggled
# `"postinstall"` script in packages/*/package.json passes a path check,
# survives `pnpm install --frozen-lockfile` (script-only edits don't
# desync the lockfile), and executes inside the publish job where the
# npm OIDC id-token is in scope. So every changed line in a package.json
# must be a version bump or an internal @openrouter/* dependency range
# bump — exactly what `changeset version` emits (cf. Version PR #57).
# src/version.ts gets the same treatment: every changed line must match
# the fixed gen-version.mjs output shape (comments or the PACKAGE_VERSION
# constant), so a smuggled statement can't ride the auto-merge either.
# Generated version files get the same treatment: every changed line must
# match the fixed gen-version.mjs output shape (comments or the
# PACKAGE_VERSION constant), so a smuggled statement can't ride the
# auto-merge either. Path allowlist and content vet share one list, so a
# file can never be allowed without also being vetted.
#
# File list is paginated via the REST API: `gh pr view --json files` caps at
# 100 entries and sorts .changeset/ first, so a padded PR could hide an
Expand Down Expand Up @@ -70,7 +74,12 @@ fi
printf '%s\n' "$FILES_NDJSON" | python3 -c '
import json, re, sys

ALLOW = re.compile(r"^(\.changeset/[^/]+\.md|packages/[^/]+/(package\.json|CHANGELOG\.md|src/version\.ts))$")
# Exact paths of committed gen:version outputs (packages/*/scripts/gen-version.mjs).
# Add a path here when a package starts committing one.
GENERATED_VERSION_FILES = frozenset({
"packages/agent/src/mcp/version.ts",
})
ALLOW = re.compile(r"^(\.changeset/[^/]+\.md|packages/[^/]+/(package\.json|CHANGELOG\.md))$")
# The only lines `changeset version` changes in a package.json: the version
# field, and internal dependency ranges when updateInternalDependencies fires.
OK_LINE = re.compile(
Expand All @@ -91,11 +100,12 @@ for raw in sys.stdin:
continue
f = json.loads(raw)
name = f["filename"]
if not ALLOW.match(name):
is_version_file = name in GENERATED_VERSION_FILES
if not (is_version_file or ALLOW.match(name)):
bad.append(f"path outside allowlist: {name}")
continue
if name.endswith("package.json") or name.endswith("src/version.ts"):
ok_line = OK_VERSION_TS_LINE if name.endswith(".ts") else OK_LINE
if is_version_file or name.endswith("package.json"):
ok_line = OK_VERSION_TS_LINE if is_version_file else OK_LINE
patch = f.get("patch")
if patch is None:
# No inline patch (file too large / binary flag) — cannot vet.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ jobs:
commit: 'chore: version packages'
# `pnpm run version`, not `changeset version` directly: the bump has
# to be followed by `turbo run gen:version` so committed generated
# files (packages/mcp/src/version.ts) are regenerated in the same
# files (packages/agent/src/mcp/version.ts) are regenerated in the same
# commit. Otherwise the Version PR ships a package.json at the new
# version and a version.ts at the old one, and version.test.ts fails
# on it — turbo's `test` task uses `dependsOn: ["^build"]`, which
Expand Down
Loading