Skip to content

Build master @ bc50cd5bunreleased — 31 commits past v1.282.0Last reviewed Last changed Grade fresh (aging >90d, stale >180d)Source / History

Release Content Gate ​

A BoB release is a tag of this repository, and every client installs from it. Whatever is tracked at the release commit ships to every tenant. The release content gate refuses a release whose tree carries operator data, a client identifier or a credential (#2290, part of #2215).

Where it runs ​

Entry pointWhat it does
bob release (stable, nightly, promote)Runs the gate after every other gate and before any bump, tag or push. A hit refuses with exit 3: one line with the hit count and the command that lists the hits. No tag is created. --dry-run runs the gate too.
make check-release-content [REF=<sha>]The same check without releasing. It is not part of make check, because it needs an operator profile, and CI has none.
node scripts/checks/release-content.js [--ref <sha>] [--root DIR] [--allow FILE] [--json]The engine. --ref defaults to HEAD. --allow grades against an uncommitted allow-list.

BOB_RELEASE_CONTENT=<script> replaces the engine inside release.sh, the same seam as BOB_RELEASE_DOCS.

The three rules ​

RuleWhat it refusesInput
pathA tracked path that must not exist in a release treescripts/checks/release-content.json paths[], plus the session_state and machine_local paths of scripts/lib/derived-paths.json
tenantA path or a line carrying a client identifier: repo names, org names, domains and product namesThe operator profile's tenants[] only (operator.json, schemas/operator.schema.json)
credentialA line matching a credential pattern that is not a placeholderregistry/skills/secrets-management/references/secret-patterns.json, the catalogue the commit secret scan reads too (#3662)

Path patterns are anchored at the repo root. * matches within one path segment, a leading **/ matches at any depth, and a trailing / matches the directory and everything under it. except lists exact paths a pattern does not cover. So apps/intake/config/ matches the operator's intake config but not apps/intake/config.example/. config/cloudflare-accounts.json does not match the operator fixtures under tests/fixtures/operator/sample/. Operator data (#2270), projects.json, and any secrets/ or memory/ directory are covered. The rules file holds paths only, never a client name.

Tenant literals are matched case-insensitively as substrings of every tracked path (a file named after the client ships its name; the hit has no line) and of every tracked text line, including docs/, *.md, journal/, requirements/ and CHANGELOG.md. Binary files' contents are skipped. The product repo never names a client in order to keep that client out: the names live only in the operator's profile.

json
"tenants": [
  { "id": "client-a", "label": "Globex Corporation", "literals": ["globex", "globex-corp.example"] }
]

A tenant id is opaque. The allow-list in the product repo names tenants by id, so an id that overlaps one of its own literals ("id": "globex") is a config error.

Credential hits never reproduce the secret. A hit names the file, the line and the pattern. The match is masked to its first four characters (ghp_****), and the line itself is never kept. A line matching the catalogue's placeholder regex (REPLACE_ME, EXAMPLE, ${VAR}, …) is an example, not a hit.

Output and exit codes ​

text
release-content — /home/me/projects/bigbrain at HEAD (44203325)
  operator: example-co (via default)   tenants: client-a
  graded: 2145 tracked file(s) · 39 path rule(s) · 10 credential pattern(s)
  allow-listed: path 1 · tenant 289 · credential 24   (scripts/checks/release-content-allow.json)
  PASS  no operator data, client identifiers or credentials in the release tree

--json emits release-content/1: root, ref, sha, verdict (clean / hits), operator, tenants (ids), rules, hits[] (one per file, line, rule and tenant id; pattern and the masked match for a credential), allowed (count per rule) and unused_allow[]. On exit 2 or 3 the document carries verdict config-error / no-operator and error.

ExitMeaning
0Clean: every hit, if any, is allow-listed
1Hits: a release of this tree would ship them
2Invalid config: the rules, the catalogue, the profile's tenants[] or the allow-list
3No operator resolved: the gate cannot know which clients to keep out, so it fails closed

The allow-list ​

scripts/checks/release-content-allow.json is read from the graded tree at <ref>, so a release carries its own baseline. Entries are {path, rule, tenant, why, retires}:

  • path is a file or a directory prefix (trailing /).
  • rule is path, tenant or credential.
  • tenant is required for rule tenant. It is an id the operator profile declares, never a literal. An undeclared id is a config error.
  • why and retires are required. retires names the requirement that removes the entry.
  • The allow-list is graded like any other file, so it must not name a client either. A file whose name carries the client is allow-listed by its directory, not by its own path.

The allow-list is loaded by abs-path-lint.js's loader, not by a second parser. Every run prints the allow-listed count per rule and lists the entries that match nothing, so the burn-down stays visible.

The baseline at #2290 allow-lists four groups, each against a follow-on requirement:

GroupRetired by
The eloqua-dev skill's as-built client material#3430 — retired: the client notes live in the operator repo's skills/eloqua-dev/
Tenant literals in docs, briefings, tests, scripts, journal and CHANGELOG#3431
projects.json, the derived registry (path and tenant)#3432 — retired: the registry is operator data in the operator repo
Credential-shaped samples in tests and doc examples#3433
  • Credential Inventory covers credentials by name. This gate covers the tree.
  • Versioning explains the release channels and the other gates.
  • Operator describes the profile that declares tenants[].
  • check-abs-paths (#2268) keeps the operator's own identity literals out of synced files during development. This gate grades the whole tree at the release commit, docs included, for every declared tenant.