
Migrate GitHub Actions to Node.js 24 Before the Deadline
You've got 30 repositories. Maybe 80. Every one of them needs the same lint check, the same security scan, the same code review gate before anything merges to main. The typical approach is branch protection rules plus a workflow file copy-pasted into each repo. It works until someone forgets to update one, or a new team spins up a repo without the standard checks. Then you're chasing drift.
GitHub's answer to this problem is organization-level rulesets with required status checks. Define a workflow once, enforce it everywhere. This guide covers how that mechanism works, how to set it up, and how to wire in an AI code review step so that every PR across your org gets reviewed automatically.
Before organization rulesets existed, platform teams had two options for enforcing CI standards.
Option one: branch protection rules. You'd configure each repository's main branch to require specific status checks before merging. The problem is that the workflow producing those checks still had to exist in each repo. If someone renamed the job, the branch protection rule would silently stop enforcing anything because the check name no longer matched.
Option two: reusable workflows. Better, because the logic lives in one place. But every repo still needs a caller workflow file that references it. New repos start with zero workflow files unless someone remembers to add one, and there's no mechanism preventing a repo admin from deleting the caller.
Both approaches share the same failure mode: they rely on individual repositories being configured correctly. At scale, that's a governance gap.
GitHub originally shipped a feature called "required workflows" as a public beta in January 2023. It let org admins select a workflow file and require it to pass on PRs across selected repos. That feature was short-lived. By October 2023, GitHub migrated the concept into organization rulesets, which provide the same enforcement with more flexibility.
Here's what organization rulesets give you that the old approaches don't:
This is available on GitHub Team and GitHub Enterprise plans.
The setup has two parts: the reusable workflow that contains your CI logic, and the org ruleset that requires its status check to pass.
Most orgs use a dedicated .github repository for shared configuration. Place your reusable workflow there:
# .github/workflows/required-ci.yml
name: Required CI Gate
on:
workflow_call:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run linter
run: echo "Run your org-standard lint here"
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run security scan
run: echo "Run your org-standard security scan here"Each repository that needs to run this workflow gets a caller file. You can template this with a repo creation automation or include it in your org's repo template:
# .github/workflows/call-required-ci.yml
name: Org CI
on:
pull_request:
branches: [main]
jobs:
org-ci:
uses: your-org/.github/.github/workflows/required-ci.yml@mainGo to your org settings, then Repository > Rulesets > New branch ruleset. Configure it like this:
lint, security-scan).Once created, this ruleset immediately applies to every targeted repository. PRs can't merge until the named checks pass. Repo admins can see the ruleset but can't disable it.
When your caller workflow references the central reusable workflow with @main, you're trusting that the main branch of your .github repo won't be tampered with. For most internal orgs that's fine. But if you want stronger guarantees, pin to a commit SHA:
jobs:
org-ci:
uses: your-org/.github/.github/workflows/required-ci.yml@a1b2c3d4e5f6This guarantees that the exact version of the workflow you tested is the version that runs. Tags and branches can be moved or overwritten; a SHA can't. GitHub's own org settings also support enabling "Require actions to be pinned to a full-length commit SHA" at the org level, which enforces this practice across all workflows.
The tradeoff is maintenance overhead. Every time you update the central workflow, you need to update the SHA reference in all caller files. A Dependabot or Renovate config watching your .github repo can automate SHA bumps across your repos.
One of the most practical uses for a required workflow is making sure every PR gets an AI code review, regardless of which repo it's in. Tenki's code reviewer works as a GitHub App that triggers on PR events, so integrating it into a required workflow is straightforward.
Since Tenki operates as a GitHub App rather than a workflow action, the integration pattern is slightly different. You install the Tenki GitHub App at the org level, which gives it access to all repositories (or selected ones). Then in your org ruleset, you add the Tenki review check name as a required status check.
The result: every PR across every targeted repo in your org gets reviewed by Tenki before it can merge. No per-repo opt-in. No workflow file to copy. The platform team sets it once, and it's done.
At $0.50 per review and 10 free reviews to start, it's a low-risk way to add coverage. Compare that to the cost of a missed bug reaching production because a PR merged without any review at all.
Not every repo in your org should have the same rules. Documentation repos, archived projects, and experimental sandboxes might not need a full CI gate. Organization rulesets give you several ways to handle this.
GitHub lets you define custom properties for repositories at the org level. You could create a property like ci-tier with values like standard, strict, and exempt. Then your ruleset filter targets only repos where props.ci-tier:standard or props.ci-tier:strict.
This is cleaner than managing exclusion lists because the property travels with the repo. When someone asks "why doesn't this repo have CI enforcement?" the answer is visible right in the repo's settings.
If your org uses naming conventions like svc-* for services, lib-* for libraries, and docs-* for documentation, you can use fnmatch patterns to include svc-* and lib-* while excluding docs-*.
Sometimes you need a specific team or bot to push without clearing the gate. Rulesets let you add bypass permissions for roles (admin, maintain, write), specific teams, and GitHub Apps. Dependabot is explicitly supported as a bypass actor.
Keep the bypass list tight. Every bypass is a potential gap in your enforcement. If you need to grant temporary bypasses for incident response, consider creating a separate ruleset with a narrower scope rather than loosening the primary one.
Required status checks from org rulesets show up in the same place as any other check: the PR's checks tab. Developers see the check name, whether it passed or failed, and can click through to the workflow run. There's no mystery about why a PR is blocked.
From the platform team's side, you can see all rulesets and their status in the org settings. The rulesets page shows which repos are targeted, what rules are active, and what enforcement status each ruleset has. If you're on GitHub Enterprise, the audit log also captures ruleset creation, modification, and bypass events.
Repo admins can view which org-level rulesets apply to their repo, but they can't edit or disable them. They can only add additional, stricter rules on top.
If your org currently relies on branch protection rules with required status checks configured repo by repo, here's a migration path that avoids disruption.
.github repo and add caller workflow files to a pilot group of repos.The whole migration can happen incrementally. There's no big-bang cutover required.
A few practical gotchas from teams that have rolled this out:
A production setup for an org with 30+ repos looks something like this:
.github repo containing lint, test, and security scan jobs.The platform team manages the workflow and the ruleset. Individual teams don't need to think about CI governance at all. They just open PRs and see the checks. If a new repo gets created, it's automatically covered as long as it matches the targeting criteria. No tickets, no manual setup, no drift.
Tags
Recommended for you
What's next in your stack.