CI Requirements for Pull Requests
All repositories in the integritystl GitHub org are required to have a build-test status check pass before any pull request can be merged. This is enforced by the org-level “PR-Review” ruleset and satisfies SOC2 CC8.1 (Change Management).
How it works
The org-level ruleset requires a GitHub Actions job named build-test to pass on every PR targeting a trunk branch (main, develop, dev, development, staging). If no build-test check runs on a PR, the merge button is blocked.
This means every repo needs a GitHub Actions workflow that:
- Triggers on
pull_request - Contains a job named
build-test - Performs meaningful validation (not a no-op pass-through)
Existing repos
All active repos already have this set up. You don’t need to do anything for repos that are already producing a build-test check on PRs.
New repos
When creating a new repository, you must add a workflow file at .github/workflows/pr-check.yml (or any filename) that includes a build-test job triggered on pull requests. The job should run validation appropriate to the project’s stack.
WordPress repos
For WordPress projects, use PHP linting on custom code. This catches syntax errors before they reach production.
name: PR Check
on:
pull_request:
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
- name: Lint PHP files in custom code
run: |
errors=0
total=0
files=()
for dir in wp-content/themes wp-content/mu-plugins; do
if [ -d "$dir" ]; then
while IFS= read -r file; do
files+=("$file")
done < <(find "$dir" -name '*.php' \
-not -path '*/vendor/*' \
-not -path '*/node_modules/*')
fi
done
total=${#files[@]}
if [ "$total" -eq 0 ]; then
echo "No PHP files found to lint."
exit 0
fi
for file in "${files[@]}"; do
output=$(php -l "$file" 2>&1) || true
if echo "$output" | grep -q "No syntax errors"; then
echo " pass $file"
else
errors=$((errors + 1))
echo " FAIL $file"
echo "$output" | grep -v "^Errors parsing" | sed 's/^/ /'
fi
done
echo ""
echo "$total files linted"
if [ "$errors" -gt 0 ]; then
echo "$errors failed, $((total - errors)) passed"
exit 1
fi
echo "0 failed, $total passed"Adjust php-version to match your project’s hosting environment. The || true on php -l prevents a non-zero exit code (from either a syntax error or a PHP binary crash) from aborting the entire lint step under GitHub Actions’ default bash -e behavior.
Next.js / Node repos
For Node-based projects, run your existing lint and/or test suite under a build-test job:
name: PR Check
on:
pull_request:
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn lint
- run: yarn buildReplace the steps with whatever validation is appropriate — yarn test, next lint, type checking, etc. The only requirement is the job is named build-test.
Repos with multiple CI jobs
If your workflow has multiple jobs that run conditionally (e.g., based on path filters), don’t rename one of them. Instead, add a build-test gate job that depends on all of them and reports a single pass/fail result.
name: CI
on:
pull_request:
jobs:
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "frontend checks here"
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "backend checks here"
build-test:
needs: [frontend, backend]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check results
run: |
results=("${{ needs.frontend.result }}" "${{ needs.backend.result }}")
for r in "${results[@]}"; do
if [[ "$r" == "failure" || "$r" == "cancelled" ]]; then
echo "One or more checks failed"
exit 1
fi
doneThe if: always() ensures the gate job runs even if an upstream job is skipped. Skipped jobs are treated as passing — only failure or cancelled results cause the gate to fail. Add each upstream job to the needs array and results list.
If all upstream jobs are skipped (e.g., no files matched path filters), the gate job will still pass. This is expected — it means the PR didn’t change code that requires validation by those jobs.
What not to do
- No rubber-stamp CI. A job that always passes with no real validation defeats the purpose. The check must perform meaningful work.
- Don’t skip the job name. The org-level ruleset specifically requires a check named
build-test. A workflow with a differently-named job won’t satisfy the requirement. - Don’t use
required_status_checksat the repo level. The org-level ruleset handles this. Adding repo-level rules creates confusion about where the requirement is enforced.