Open App CI/CD & GitHub Rulesets Setup Guide (bizapps-common Standard)
This guide documents how to set up the standard MemberJunction Open App CI/CD pipeline and GitHub branch rulesets, modeled directly on MemberJunction/bizapps-common (with the human reviewer requirement removed so PRs are gated by automated checks without blocking on manual review approval).
1. Pipeline Architecture Overview
Section titled “1. Pipeline Architecture Overview”Every Open App uses a two-branch (next → main) Changesets + npm OIDC release pipeline where no human or workflow ever pushes directly to next or main:
flowchart LR
subgraph Dev["1. Feature Work (into next)"]
F["Feature Branch<br/>+ changeset"] -->|PR to next| NextGates["Door Gates:<br/>• changes.yml<br/>• build.yml<br/>• repo-checks.yml<br/>• clean-room-gate.yml<br/>• schema-consistency.yml<br/>• caret-pins.yml (advisory)"]
NextGates -->|Merge| Next["next<br/>(Default Branch)"]
end
subgraph Rel["2. Automated Release PR (into main)"]
Next -->|Triggers version.yml<br/>(GitHub App token)| VP["Version Packages PR<br/>(changeset-release/main → main)<br/>• changeset version<br/>• sync mj-app.json<br/>• refresh pnpm-lock.yaml"]
VP -->|Runs on PR| MainGates["Release Gates:<br/>• release-readiness.yml (rr:*)<br/>• build.yml (build)<br/>• repo: release seed<br/>• clean-room-gate.yml<br/>• schema-consistency.yml<br/>• caret-pins.yml (advisory)"]
end
subgraph Pub["3. Publish & Back-Merge"]
MainGates -->|release.yml dispatch<br/>(Cut the Release)<br/>merges the PR = Release| Main["main<br/>(Release Branch)"]
Main -->|Triggers publish.yml| OIDC["npm OIDC Publish<br/>+ git tag vX.Y.Z"]
OIDC -->|GitHub App opens &<br/>auto-merges PR| BM["release-back-merge/vX.Y.Z → next"]
BM --> Next
end
Why this design?
Section titled “Why this design?”- Zero direct pushes (
GH013-proof): GitHub rulesets with required status checks reject direct branch pushes (GH013) because a newly pushed SHA has no check runs attached to it yet. Every write tonextandmaingoes through a Pull Request. - GitHub App token (
blue-cypress-ci-bot), notGITHUB_TOKEN: PRs opened withGITHUB_TOKENdo not trigger GitHub Actions check runs. Using the org’sblue-cypress-ci-botApp token ensuresversion.yml’s Version Packages PR andpublish.yml’s back-merge PR trigger all required status checks automatically. - Version numbers never skip:
version.ymlbases the Version Packages PR onmain(changeset-release/main→main) while resetting its tree tonext+ the version bump. Merging that single PR is the release, and that merge is aworkflow_dispatch(release.yml, “Cut the Release”) run with the App token, never a hand click or an auto-merge:version.ymlruns on every push tonext, so auto-merging its PR would turn every merge into a publish. Releasing stays a recorded decision. - OIDC Trusted Publishing: Packages publish to npm using GitHub OIDC (
id-token: write) with provenance — noNPM_TOKENsecret to rotate.
2. Files to Copy from bizapps-common
Section titled “2. Files to Copy from bizapps-common”From a sibling checkout of bizapps-common, copy the following four directories into your Open App repository:
# Run from the root of your target Open App repository (assuming ../bizapps-common exists)# Make sure that checkout is on an up-to-date `next` first (`git -C ../bizapps-common fetch origin next:next`);# a stale feature branch is missing workflows that `origin/next` already carries.mkdir -p .github/workflows .github/scripts/__tests__ ci/lib scripts .changeset
# 1. Workflows (copy all except mutants.yml unless your repo already has mutation harnesses)cp ../bizapps-common/.github/workflows/{build,caret-pins,changes,clean-room-gate,publish,release,release-readiness,repo-checks,schema-consistency,verify-release-app-token,version}.yml .github/workflows/
# 2. Shared .github validator scripts + their self-testscp ../bizapps-common/.github/scripts/*.{sh,mjs} .github/scripts/cp ../bizapps-common/.github/scripts/__tests__/* .github/scripts/__tests__/
# 3. Release-readiness & manifest sync scriptscp ../bizapps-common/ci/*.{sh,mjs} ci/cp ../bizapps-common/ci/lib/*.sh ci/lib/
# 4. Repo guard & release-seed scripts + their specscp ../bizapps-common/scripts/{sync-app-version,check-release-seed-coverage,check-release-seed-cadence,check-release-pushes,check-paths-touched,check-against-published-mj}*.mjs scripts/What each workflow does
Section titled “What each workflow does”| Workflow File | Trigger | Purpose & Status Check Contexts |
|---|---|---|
.github/workflows/changes.yml | PR → next | Door migration & script checks: migration naming, migration changeset, migration immutability, script references, Parse migrations (T-SQL syntax check). The migration changeset job needs pull-requests: read + issues: read and passes GH_TOKEN/PR_NUMBER: check-migration-changeset.sh only honours the migration-changeset-exempt label when whoever applied it holds the Maintain or Admin role (fails closed otherwise). |
.github/workflows/build.yml | Push → next, PR → next, main | Package & build gate (build): validates repository.url, files: ["dist"], lockfile case-sensitivity self-tests, script specs, pnpm install --frozen-lockfile, pnpm run build:packages, pnpm run test. |
.github/workflows/repo-checks.yml | PR → next, main | Repo guards & seed checks: repo: guards (enforces caret peerDependencies on @memberjunction/* and sibling @mj-biz-apps/*, script self-tests, no direct-push gate) and repo: release seed (on PRs to main: verifies check:release-seed and check:seed-cadence). |
.github/workflows/schema-consistency.yml | Push & PR → next, main | FK index coverage (Foreign Key Index Coverage (MC4)): check-fk-indexes.mjs asserts every FOREIGN KEY in migrations/ has its CodeGen-standard IDX_AUTO_MJ_FKEY_{Table}_{Column} index. Not a required context in the rulesets, but it runs on every PR. |
.github/workflows/caret-pins.yml | PR → next, main (path-filtered to package.json files) | Advisory (caret pins (advisory)): check-caret-pins.mjs flags ~ or exact ranges outside devDependencies/overrides. Deliberately not required: a red result is fixed, or allow-listed in .github/caret-pins-allow.json with a reason. The file is copied byte-for-byte into every bizapps repo. |
.github/workflows/clean-room-gate.yml | Push & PR → next, main | Clean-room DB gate (Clean-room EntityField drift): boots SQL Server 2022, installs MJ core at the mjVersionRange floor, runs the app’s migrations from scratch, and verifies zero EntityField drift between SQL views and metadata. |
.github/workflows/version.yml | Push → next | Maintains the Version Packages PR: uses blue-cypress-ci-bot to open/update changeset-release/main → main running pnpm run version:prepare. |
.github/workflows/release-readiness.yml | PR → main | Release gates (rr:*): rr: changesets, rr: minor bump, rr: metadata shipped, rr: pg counterparts, rr: release base current, rr: migration immutability, rr: packages on npm. |
.github/workflows/release.yml | workflow_dispatch | Cut the Release: finds the single open changeset-release/main → main PR (refuses on 0 or >1), waits up to 10 min for its checks to settle, then merges it with a merge commit using the App token and --match-head-commit. Takes no inputs on purpose: the App’s pull_request-mode bypass on protect-main must only ever merge the branch the changesets action owns. GitHub still enforces protect-main-checks on the merge. |
.github/workflows/publish.yml | Push → main | Builds, publishes, tags, and back-merges: publishes to npm over OIDC, creates and pushes vX.Y.Z tag, and opens + auto-merges release-back-merge/vX.Y.Z → next. |
.github/workflows/verify-release-app-token.yml | workflow_dispatch | Read-only diagnostic: verifies vars.APP_CLIENT_ID and secrets.APP_PRIVATE_KEY can mint an installation token for this repo with contents: write and pull-requests: write. |
3. Customizing Repo-Specific Values
Section titled “3. Customizing Repo-Specific Values”The 9 core workflows (changes.yml, build.yml, caret-pins.yml, schema-consistency.yml, version.yml, release.yml, release-readiness.yml, publish.yml, verify-release-app-token.yml) and all ci/* scripts are 100% repo-agnostic (they read packages/Entities/package.json and mj-app.json dynamically).
Only the following places reference bizapps-common’s schema name (__mj_BizAppsCommon) or optional test harnesses and must be updated for your app:
3.1 Replace __mj_BizAppsCommon with your app’s schema name
Section titled “3.1 Replace __mj_BizAppsCommon with your app’s schema name”Replace __mj_BizAppsCommon (and bizapps_common_cleanroom) with your app’s schema (e.g., __mj_BizAppsMyApp) in:
.github/workflows/clean-room-gate.yml(lines 58, 137, 139).github/scripts/check-entityfield-drift.mjs(line 62:const APP_SCHEMA = argValue('--schema') || '__mj_BizAppsMyApp';).github/scripts/parse-migrations.mjs(line 117: default--schemafallback).github/scripts/__tests__/parse-migrations.spec.mjs(line 37)
# Quick replacement example (replace __mj_BizAppsMyApp with your schema name from mj-app.json):APP_SCHEMA="__mj_BizAppsMyApp"sed -i '' "s/__mj_BizAppsCommon/${APP_SCHEMA}/g" \ .github/workflows/clean-room-gate.yml \ .github/scripts/check-entityfield-drift.mjs \ .github/scripts/parse-migrations.mjs \ .github/scripts/__tests__/parse-migrations.spec.mjs3.2 If your Open App depends on bizapps-common (or another upstream Open App)
Section titled “3.2 If your Open App depends on bizapps-common (or another upstream Open App)”In .github/workflows/clean-room-gate.yml, if your app’s SQL migrations reference tables or views in __mj_BizAppsCommon, add a step in clean-room-gate.yml right after Install MJ core (step pnpm exec mj migrate --tag ${{ steps.mj.outputs.tag }}) to migrate bizapps-common before running your app’s migration chain:
- name: Install MJ core run: pnpm exec mj migrate --tag ${{ steps.mj.outputs.tag }} --verbose
# Add this step if your app depends on bizapps-common in mj-app.json: - name: Install upstream Open App (bizapps-common) run: | DEP_RANGE=$(jq -r '.dependencies["mj-bizapps-common"] // empty' mj-app.json) if [ -n "$DEP_RANGE" ]; then DEP_VER=$(echo "$DEP_RANGE" | sed -E 's/^[^0-9]*//') git clone --depth 1 --branch "v${DEP_VER}" https://github.com/MemberJunction/bizapps-common.git /tmp/bizapps-common pnpm exec mj migrate --schema __mj_BizAppsCommon --dir /tmp/bizapps-common/migrations --verbose fi
- name: Apply this repo's migration chain run: pnpm exec mj migrate --schema __mj_BizAppsMyApp --dir ./migrations --verbose3.3 Adjust .github/workflows/repo-checks.yml if you don’t use Mutation / Integration Harnesses yet
Section titled “3.3 Adjust .github/workflows/repo-checks.yml if you don’t use Mutation / Integration Harnesses yet”In .github/workflows/repo-checks.yml, the guards job runs two steps that assume bizapps-common’s mutation and integration harnesses exist:
Mutation anchors(pnpm run test:mutants:anchors)Harness self-tests(node --test 'packages/*/test-harnesses/*.spec.mjs' 'test-harnesses/*.spec.mjs')
If your Open App does not have mutation or integration harness .spec.mjs files yet, remove those two steps from .github/workflows/repo-checks.yml (keep Dependency model, Script self-tests, and Release-push gate, plus the entire release-seed job).
4. Required Repository Configuration Files
Section titled “4. Required Repository Configuration Files”4.1 .changeset/config.json
Section titled “4.1 .changeset/config.json”Ensure .changeset/config.json uses baseBranch: "next" and lockstep (fixed) versioning across your @mj-biz-apps/* packages:
{ "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [["@mj-biz-apps/*"]], "linked": [], "access": "public", "baseBranch": "next", "updateInternalDependencies": "patch"}4.2 Root package.json Scripts & devDependencies
Section titled “4.2 Root package.json Scripts & devDependencies”Your root package.json must define the scripts invoked by the workflows and guards (replace myapp and __mj_BizAppsMyApp with your app’s package prefix and schema):
{ "name": "bizapps-myapp", "private": true, "version": "1.0.0", "license": "BUSL-1.1", "workspaces": [ "packages/*" ], "scripts": { "prebuild": "node -e \"try { require.resolve('turbo') } catch (e) { console.error('\\n❌ Dependencies not installed. Run: pnpm install\\n'); process.exit(1); }\"", "build": "pnpm run build:stream", "build:stream": "turbo --log-order=stream build --filter=\"@mj-biz-apps/*\"", "build:generated": "turbo --log-order=stream build --filter=\"@mj-biz-apps/myapp-entities\" --filter=\"@mj-biz-apps/myapp-actions\"", "build:packages": "turbo --log-order=stream build --filter=\"@mj-biz-apps/myapp-*\"", "test": "turbo --log-order=stream test --filter=\"@mj-biz-apps/myapp-*\"", "mj:migrate": "mj migrate --schema __mj_BizAppsMyApp --dir ./migrations", "mj:codegen": "mj codegen", "mj:migrate:convert": "mj migrate convert --source-dir ./migrations --output-dir ./migrations-pg --schema __mj_BizAppsMyApp", "change": "changeset", "version": "changeset version && node scripts/sync-app-version.mjs", "version:prepare": "changeset version && node ci/sync-mj-app-version.mjs && pnpm install --lockfile-only", "lint:entityfield-drift": "node .github/scripts/check-entityfield-drift.mjs", "lint:entityfield-drift:test": "node .github/scripts/check-entityfield-drift.mjs --self-test", "check:published-mj": "node scripts/check-against-published-mj.mjs", "check:release-seed": "node scripts/check-release-seed-coverage.mjs", "check:release-seed:test": "node --test scripts/check-release-seed-coverage.spec.mjs", "check:seed-cadence": "node scripts/check-release-seed-cadence.mjs", "check:seed-cadence:test": "node --test scripts/check-release-seed-cadence.spec.mjs", "lint:paths-touched:test": "node --test scripts/check-paths-touched.spec.mjs", "sync-app-version:test": "node --test scripts/sync-app-version.spec.mjs", "lint:release-pushes": "node scripts/check-release-pushes.mjs", "lint:release-pushes:test": "node --test scripts/check-release-pushes.spec.mjs" }, "repository": { "type": "git", "url": "https://github.com/MemberJunction/bizapps-myapp" }, "packageManager": "pnpm@10.33.0", "devDependencies": { "@changesets/cli": "^2.29.8", "@memberjunction/cli": "^6.1.6", "@types/node": "^22.10.0", "simple-git": "^3.31.1", "tsc-alias": "^1.8.16", "turbo": "^2.7.3", "typescript": "^5.9.3", "zod": "^3.25.76" }}[!IMPORTANT] Keep
@changesets/clion^2.29.8:version.ymluseschangesets/action@v1(v1.9.0), which requires Changesets CLI v2 and supports thebranch: maininput that creates the release PR directly againstmain.
4.3 pnpm-workspace.yaml and .npmrc
Section titled “4.3 pnpm-workspace.yaml and .npmrc”Ensure pnpm-workspace.yaml at the repo root has linkWorkspacePackages: true and the standard onlyBuiltDependencies list:
linkWorkspacePackages: true
onlyBuiltDependencies: - '@apollo/protobufjs' - '@google/genai' - '@parcel/watcher' - '@zoom/rtms' - browser-tabs-lock - core-js - core-js-pure - esbuild - isolated-vm - lmdb - msgpackr-extract - oracledb - protobufjs - rete - sharp - tesseract.js
packages: - 'packages/*'And .npmrc:
package-manager-strict=falsestrict-peer-dependencies=falseauto-install-peers=true4.4 Per-Package package.json Requirements Enforced by CI
Section titled “4.4 Per-Package package.json Requirements Enforced by CI”Every publishable package under packages/* ("private": true packages like IntegrationTests are exempt) is checked by CI for:
packages/Entities/package.jsonis the version & MJ floor anchor:ci/sync-mj-app-version.mjsandscripts/sync-app-version.mjsread.versionand.peerDependencies["@memberjunction/core"]frompackages/Entities/package.jsonto derivemj-app.json.filesand entrypoint (validate-package-files.sh): Every publishable package must declare"files": ["dist"](or"dist/**") and"main"/"types"pointing into./dist/....repository.url(validate-package-repository.sh): Every publishable package must have"repository": { "type": "git", "url": "https://github.com/MemberJunction/<repo-name>", "directory": "packages/<Dir>" }for npm OIDC provenance verification.- Caret
peerDependencies(check-dependency-model.mjs):@memberjunction/*and other apps’@mj-biz-apps/*packages must be declared inpeerDependencies(anddevDependencies) using a caret range (^6.1.6), never independencies. Intra-repo sibling packages (@mj-biz-apps/<this-app>-*) go independencieswith exact versions.
5. GitHub App & npm OIDC Setup (One-Time Prerequisite)
Section titled “5. GitHub App & npm OIDC Setup (One-Time Prerequisite)”5.1 Grant the Repo Access to blue-cypress-ci-bot
Section titled “5.1 Grant the Repo Access to blue-cypress-ci-bot”The workflows authenticate using the MemberJunction org’s GitHub App (blue-cypress-ci-bot, Integration ID 4705146):
- In the
MemberJunctionGitHub Organization settings:- Under Settings → GitHub Apps →
blue-cypress-ci-bot, ensure the new repository is included in the App’s repository access list. - Under Settings → Actions → Variables / Secrets, ensure
APP_CLIENT_ID(org variable) andAPP_PRIVATE_KEY(org secret) are shared with the repository.
- Under Settings → GitHub Apps →
- Once
.github/workflows/verify-release-app-token.ymlis pushed to the repo, go to Actions → Verify the release App token → Run workflow. Confirm it turns green and outputs:app: blue-cypress-ci-botgrants requested: contents: write, pull-requests: write
- Releases are cut from Actions → Cut the Release → Run workflow once a Version Packages PR is open and green. The same App token does that merge, so if step 2 is green this works too.
5.2 Bootstrap Packages on npm & Enable OIDC Trusted Publishing
Section titled “5.2 Bootstrap Packages on npm & Enable OIDC Trusted Publishing”Because npm OIDC cannot create a brand-new package on first publish (and rr: packages on npm blocks the release PR if any non-private package is missing from npm):
- For each publishable
@mj-biz-apps/*package inpackages/*/package.jsonthat does not exist on npm yet, an@mj-biz-appsnpm org owner runs once:Terminal window npx setup-npm-trusted-publish @mj-biz-apps/<package-name> - On npmjs.com, open
https://www.npmjs.com/package/@mj-biz-apps/<package-name>/access→ Trusted Publisher and configure:- Organization / User:
MemberJunction - Repository:
<repo-name>(e.g.,bizapps-myapp) - Workflow filename:
publish.yml
- Organization / User:
6. Exact GitHub Repository Rulesets (Minus Required Reviewer)
Section titled “6. Exact GitHub Repository Rulesets (Minus Required Reviewer)”First, ensure next is set as the repository’s default branch (Settings → General → Default branch → next).
bizapps-common uses three active branch rulesets (protect-next, protect-main, protect-main-checks). Below is the exact same configuration minus the required human reviewer (required_approving_review_count: 0, require_extra_approval_for_unattributed_changes: false, require_code_owner_review: false, required_reviewers: []):
6.1 Summary of the 3 Rulesets
Section titled “6.1 Summary of the 3 Rulesets”| Ruleset Name | Target Branch | Bypass Actors | Rules Enforced | Required Status Check Contexts (GitHub Actions 15368) |
|---|---|---|---|---|
protect-next | ~DEFAULT_BRANCH (next) | • blue-cypress-ci-bot App (pull_request mode)• Org Admin Teams ( always) | • Block branch deletion • Block force pushes ( non_fast_forward)• Require Pull Request ( 0 approvals required, dismiss stale reviews on push)• Require status checks ( strict: true — branch must be up-to-date) | • migration naming• migration changeset• script references• Parse migrations (T-SQL syntax check) |
protect-main | refs/heads/main | • Repository Admin role (pull_request mode)• blue-cypress-ci-bot App (pull_request mode)• Org Admin Teams ( always) | • Require Pull Request (0 approvals required, dismiss stale reviews on push) | (Handled in protect-main-checks) |
protect-main-checks | refs/heads/main | • Repository Admin role (pull_request mode)• Org Admin Teams ( always) | • Block branch deletion • Block force pushes ( non_fast_forward)• Require status checks ( strict: false) | • rr: changesets• rr: minor bump• rr: metadata shipped• rr: pg counterparts• rr: release base current• rr: migration immutability• rr: packages on npm• build |
[!NOTE] Why
blue-cypress-ci-botis apull_request-mode bypass actor onprotect-main:release.yml(“Cut the Release”) merges the Version Packages PR with the App token. Inbizapps-commonthat bypass is what lets the App skip the one-approval rule; with0approvals here it is kept so the rulesets stay identical to upstream and still work if a reviewer requirement is added later. Bypass is per-ruleset, which is whyprotect-mainholds only thepull_requestrule andprotect-main-checksholdsdeletion,non_fast_forwardand the required checks: the App is deliberately not a bypass actor onprotect-main-checks, so a dispatch can never merge a release that fails the gate, nor delete or force-pushmain.Why
blue-cypress-ci-botis apull_request-mode bypass actor onprotect-next: Whenpublish.ymlfinishes publishing to npm onmain, it uses the App token to openrelease-back-merge/vX.Y.Z→nextand immediately runsgh pr merge "$PR_URL" --merge --delete-branch. Givingblue-cypress-ci-botpull_request-mode bypass onprotect-nextallows it to merge that back-merge PR immediately without waiting fornextstatus checks to finish, while still forbidding direct pushes tonext.
6.2 One-Shot CLI Commands to Create All 3 Rulesets (gh api)
Section titled “6.2 One-Shot CLI Commands to Create All 3 Rulesets (gh api)”You can create the exact three rulesets on any MemberJunction/<repo> using the GitHub CLI (gh).
# Set your target repository:REPO="MemberJunction/bizapps-myapp"
# 0. Ensure `next` is the default branchgh api -X PATCH "repos/$REPO" -f default_branch="next"1. Create protect-next (targets ~DEFAULT_BRANCH, 0 required reviewers)
Section titled “1. Create protect-next (targets ~DEFAULT_BRANCH, 0 required reviewers)”gh api -X POST "repos/$REPO/rulesets" --input - <<'EOF'{ "name": "protect-next", "target": "branch", "enforcement": "active", "conditions": { "ref_name": { "exclude": [], "include": [ "~DEFAULT_BRANCH" ] } }, "bypass_actors": [ { "actor_id": 4705146, "actor_type": "Integration", "bypass_mode": "pull_request" }, { "actor_id": 19336784, "actor_type": "Team", "bypass_mode": "always" }, { "actor_id": 20104768, "actor_type": "Team", "bypass_mode": "always" } ], "rules": [ { "type": "deletion" }, { "type": "non_fast_forward" }, { "type": "pull_request", "parameters": { "required_approving_review_count": 0, "dismiss_stale_reviews_on_push": true, "required_reviewers": [], "require_code_owner_review": false, "dismissal_restriction": { "enabled": false, "allowed_actors": [] }, "require_last_push_approval": false, "required_review_thread_resolution": false, "require_extra_approval_for_unattributed_changes": false, "allowed_merge_methods": [ "merge", "squash", "rebase" ] } }, { "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": true, "do_not_enforce_on_create": false, "required_status_checks": [ { "context": "migration naming", "integration_id": 15368 }, { "context": "migration changeset", "integration_id": 15368 }, { "context": "script references", "integration_id": 15368 }, { "context": "Parse migrations (T-SQL syntax check)", "integration_id": 15368 } ] } } ]}EOF2. Create protect-main (targets refs/heads/main, 0 required reviewers)
Section titled “2. Create protect-main (targets refs/heads/main, 0 required reviewers)”gh api -X POST "repos/$REPO/rulesets" --input - <<'EOF'{ "name": "protect-main", "target": "branch", "enforcement": "active", "conditions": { "ref_name": { "exclude": [], "include": [ "refs/heads/main" ] } }, "bypass_actors": [ { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "pull_request" }, { "actor_id": 4705146, "actor_type": "Integration", "bypass_mode": "pull_request" }, { "actor_id": 19336784, "actor_type": "Team", "bypass_mode": "always" }, { "actor_id": 20104768, "actor_type": "Team", "bypass_mode": "always" } ], "rules": [ { "type": "pull_request", "parameters": { "required_approving_review_count": 0, "dismiss_stale_reviews_on_push": true, "required_reviewers": [], "require_code_owner_review": false, "dismissal_restriction": { "enabled": false, "allowed_actors": [] }, "require_last_push_approval": false, "required_review_thread_resolution": false, "require_extra_approval_for_unattributed_changes": false, "allowed_merge_methods": [ "merge", "squash", "rebase" ] } } ]}EOF3. Create protect-main-checks (targets refs/heads/main, enforces rr:* + build)
Section titled “3. Create protect-main-checks (targets refs/heads/main, enforces rr:* + build)”gh api -X POST "repos/$REPO/rulesets" --input - <<'EOF'{ "name": "protect-main-checks", "target": "branch", "enforcement": "active", "conditions": { "ref_name": { "exclude": [], "include": [ "refs/heads/main" ] } }, "bypass_actors": [ { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "pull_request" }, { "actor_id": 19336784, "actor_type": "Team", "bypass_mode": "always" }, { "actor_id": 20104768, "actor_type": "Team", "bypass_mode": "always" } ], "rules": [ { "type": "deletion" }, { "type": "non_fast_forward" }, { "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": false, "do_not_enforce_on_create": false, "required_status_checks": [ { "context": "rr: changesets", "integration_id": 15368 }, { "context": "rr: minor bump", "integration_id": 15368 }, { "context": "rr: metadata shipped", "integration_id": 15368 }, { "context": "rr: pg counterparts", "integration_id": 15368 }, { "context": "rr: release base current", "integration_id": 15368 }, { "context": "rr: migration immutability", "integration_id": 15368 }, { "context": "rr: packages on npm", "integration_id": 15368 }, { "context": "build", "integration_id": 15368 } ] } } ]}EOF[!TIP] Actor & Integration IDs Reference (
MemberJunctionGitHub Org):
integration_id: 15368— GitHub Actions (global across GitHub)actor_id: 4705146(Integration) —blue-cypress-ci-botGitHub Appactor_id: 5(RepositoryRole) — Repository Admin roleactor_id: 19336784&20104768(Team) — MemberJunction admin teams
7. Verification Checklist After Setup
Section titled “7. Verification Checklist After Setup”- Run local self-tests before opening the setup PR:
Terminal window node .github/scripts/check-dependency-model.mjs --self-testnode .github/scripts/check-dependency-model.mjsnode .github/scripts/check-caret-pins.mjs --self-testnode .github/scripts/check-fk-indexes.mjs --self-testnode .github/scripts/check-fk-indexes.mjs --dir ./migrations./.github/scripts/check-script-references.sh./.github/scripts/validate-package-repository.sh./.github/scripts/validate-package-files.shnode --test .github/scripts/__tests__/*.spec.mjsnode --test scripts/*.spec.mjspnpm run lint:release-pushes - Merge the CI/CD setup PR to
nextandmainBEFORE activating the rulesets (or use admin bypass for the initial bootstrap PR), because required status checks onnextandmaincan only report once the workflow files exist on the target branch. - Run
Verify the release App tokenfrom the Actions tab (workflow_dispatch) and confirm it succeeds. - Apply the 3 rulesets via the
gh apicommands in §6.2 and verify them with:Terminal window gh api "repos/$REPO/rulesets" --jq '.[] | {id, name, enforcement}' - Confirm every PR context reports: open a throwaway PR to
nextand check thatmigration naming,migration changeset,migration immutability,script references,Parse migrations (T-SQL syntax check),build,repo: guards,Clean-room EntityField drift,Foreign Key Index Coverage (MC4)and (when apackage.jsonchanged)caret pins (advisory)all appear. - First release: merge a changeset to
next, wait forversion.ymlto open the Version Packages PR, then run Actions → Cut the Release.publish.ymlthen publishes, tags, and opens the back-merge intonext.