Skip to content

SCF API v2026.1 — automated pipeline with 249 framework crosswalks #12

SCF API v2026.1 — automated pipeline with 249 framework crosswalks

SCF API v2026.1 — automated pipeline with 249 framework crosswalks #12

Workflow file for this run

name: Update SCF
on:
schedule:
# Weekly check: Monday 9:00 UTC
- cron: "0 9 * * 1"
workflow_dispatch:
inputs:
tag:
description: "SCF release tag to update to (leave empty for latest)"
required: false
type: string
force:
description: "Force update even if version matches"
required: false
type: boolean
default: false
permissions:
contents: write
pull-requests: write
jobs:
check-and-update:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Determine target version
id: version
env:
GH_TOKEN: ${{ github.token }}
INPUT_TAG: ${{ inputs.tag }}
run: |
if [ -n "$INPUT_TAG" ]; then
NEW_TAG="$INPUT_TAG"
else
NEW_TAG=$(gh api repos/securecontrolsframework/securecontrolsframework/releases/latest --jq '.tag_name')
fi
CURRENT=$(cat .scf-version)
echo "new_tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "Current: $CURRENT → Target: $NEW_TAG"
- name: Check if update needed
id: check
env:
NEW_TAG: ${{ steps.version.outputs.new_tag }}
CURRENT: ${{ steps.version.outputs.current }}
FORCE: ${{ inputs.force }}
run: |
if [ "$NEW_TAG" = "$CURRENT" ] && [ "$FORCE" != "true" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Already at version $CURRENT, skipping."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Parse SCF Excel
if: steps.check.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
SCF_TAG: ${{ steps.version.outputs.new_tag }}
run: node scripts/parse-scf-excel.mjs --tag "$SCF_TAG"
- name: Build static API
if: steps.check.outputs.skip != 'true'
run: npm run build
- name: Generate change summary
if: steps.check.outputs.skip != 'true'
id: summary
env:
NEW_TAG: ${{ steps.version.outputs.new_tag }}
CURRENT: ${{ steps.version.outputs.current }}
run: |
CONTROLS=$(node -e "console.log(require('./docs/api/summary.json').total_controls)")
FAMILIES=$(node -e "console.log(require('./docs/api/summary.json').total_families)")
FRAMEWORKS=$(node -e "console.log(require('./docs/api/summary.json').crosswalk_frameworks.length)")
cat > /tmp/pr-body.md <<EOF
## SCF Update: ${CURRENT} → ${NEW_TAG}
### Stats
- **Controls:** ${CONTROLS}

Check failure on line 95 in .github/workflows/update-scf.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/update-scf.yml

Invalid workflow file

You have an error in your yaml syntax on line 95
- **Families:** ${FAMILIES}
- **Framework crosswalks:** ${FRAMEWORKS}
### Source
- [SCF ${NEW_TAG} Release](https://github.com/securecontrolsframework/securecontrolsframework/releases/tag/${NEW_TAG})
### Generated by
Automated workflow — [update-scf.yml](.github/workflows/update-scf.yml)
EOF
- name: Remove old data files
if: steps.check.outputs.skip != 'true'
run: |
NEW_SLUG=$(cat .scf-version | tr '.' '-')
# Remove any scf-*.json that isn't the current version or crosswalks
for f in data/scf-*.json; do
base=$(basename "$f")
if [ "$base" != "scf-${NEW_SLUG}.json" ] && [ "$base" != "scf-crosswalks.json" ]; then
echo "Removing old file: $f"
rm "$f"
fi
done
# Remove defunct individual crosswalk files
for f in data/hipaa-scf-crosswalk.json data/gdpr-scf-crosswalk.json data/ccpa-scf-crosswalk.json data/nis2-scf-crosswalk.json; do
[ -f "$f" ] && echo "Removing: $f" && rm "$f"
done
- name: Create Pull Request
if: steps.check.outputs.skip != 'true'
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update-scf/${{ steps.version.outputs.new_tag }}
title: "Update SCF to ${{ steps.version.outputs.new_tag }}"
body-path: /tmp/pr-body.md
commit-message: "Update SCF to ${{ steps.version.outputs.new_tag }}"
labels: automated,scf-update
delete-branch: true