Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions .github/workflows/sync-oscal-cac.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
name: Sync OSCAL content to CAC content
permissions:
contents: write
pull-requests: read
on:
# push:
# branches:
# - main
# The sync-oscal-content CLI could change the format of controls and profiles.
# It's hard to review for CAC reviewers. The story CPLYTM-652 could help to
# improve the pain point. To aviod the noise in the early stage, the trigger
# event is changed to workflow_dispatch.
# trigger.
# https://github.com/ComplianceAsCode/content/pull/13617#issuecomment-3000489965
workflow_dispatch:

jobs:
sync-oscal-content-update-to-cac:
runs-on: ubuntu-latest
steps:
# Step 1: Set up Python 3
- name: Set up Python 3
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: '3.9'
# Step 2: Install Git
- name: Install Git
run: sudo apt-get update && sudo apt-get install -y git
# Step 3: Checkout OSCAL repo
- name: Checkout OSCAL repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ${{ github.repository }}
path: oscal-content
# Step 4: Get the commit message and PR number
- name: Get the commit message and PR number
run: |
cd oscal-content
# Get the latest commit message
COMMIT_MSG=$(git log -1 --pretty=%B)
# Extract the PR number from the commit message (if it's a merge commit)
PR_NUMBER=$(echo "$COMMIT_MSG" | grep -oP '#\K\d+')
if [ -n "$PR_NUMBER" ]; then
echo "Found PR number: $PR_NUMBER"
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
echo "SKIP=false" >> $GITHUB_ENV
PR_INFO=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${PR_NUMBER}")
# Extract PR title from the response
PR_TITLE=$(echo "$PR_INFO" | jq -r .title)
echo "PR Title: $PR_TITLE"
if [[ "$PR_TITLE" == *"Auto-generated PR from CAC"* ]]; then
echo "The PR comes from CAC content. The workflow of Sync OSCAL content to CAC will exit."
echo "Skipping further checks."
echo "SKIP=true" >> $GITHUB_ENV
fi
fi
# Step 5: Get the access token for content write permission to CAC content
- name: Get GitHub app token
if: ${{ env.SKIP == 'false' }}
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: |
content
oscal-content
# Step 6: Dectect the updates of OSCAL content
- name: Detect the changed files of the PR
id: changed-files
run: |
repo=${{ github.repository }}
# Fetch all pages of the files for the pull request
url="repos/$repo/pulls/${{ env.PR_NUMBER }}/files"
response=$(gh api "$url" --paginate)
echo "$response" | jq -r '.[].filename' > filenames.txt
echo "CHANGE_FOUND=false" >> $GITHUB_ENV
if grep -E "catalogs/|profiles/|component-definitions/" filenames.txt ; then
echo "CHANGE_FOUND=true" >> $GITHUB_ENV
fi
cat filenames.txt
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
# Step 7: Setup complyscribe
- name: Checkout CAC repo
if: ${{ env.CHANGE_FOUND == 'true' }}
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: ComplianceAsCode/content
path: cac-content
token: ${{ steps.app-token.outputs.token }}
fetch-depth: 0
- name: Checkout complyscribe repo
if: ${{ env.CHANGE_FOUND == 'true' }}
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: complytime/complyscribe
path: complyscribe
- name: Setup complyscribe
if: ${{ env.CHANGE_FOUND == 'true' }}
run: |
cd complyscribe && python3 -m venv venv && source venv/bin/activate
python3 -m pip install --no-cache-dir "poetry==1.7.1"
poetry install
# Step 8: Check if the CAC content branch exists
- name: Check if the CAC content branch exists
if: ${{ env.CHANGE_FOUND == 'true' }}
run: |
BRANCH_NAME="sync_oscal_pr${{ env.PR_NUMBER }}"
cd cac-content
git fetch --all
if git show-ref --verify --quiet refs/remotes/origin/"$BRANCH_NAME"; then
git checkout -b "sync_oscal_pr${{ env.PR_NUMBER }}" origin/sync_oscal_pr${{ env.PR_NUMBER }}
else
echo "CAC content branch $BRANCH_NAME doesn't exist"
fi
# Step 9: Sync OSCAL content to CAC content
- name: Sync OSCAL content to CAC content
if: ${{ env.CHANGE_FOUND == 'true' }}
run: |
cat filenames.txt
cd complyscribe && source venv/bin/activate
while IFS= read -r line; do
if [[ "$line" == *catalogs* ]]; then
echo "sync oscal catalogs according to update of $line ..."
policy_id=$(echo "$line" | cut -f2 -d"/")
echo "The policy_id is $policy_id"
poetry run complyscribe sync-oscal-content catalog --repo-path ../oscal-content --committer-email "[email protected]" --committer-name "openscap-ci" --branch "sync_oscal_pr${{ env.PR_NUMBER }}" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --cac-policy-id "$policy_id"
elif [[ "$line" == "profiles"* ]]; then
echo "sync oscal profiles according to update of $line ..."
policy_id=$(echo $line | cut -f2 -d"/" | cut -f2 -d"-")
product=$(echo "$line" | cut -f2 -d"/" | cut -f1 -d"-")
echo "The policy_id is $policy_id, the product is $product"
poetry run complyscribe sync-oscal-content profile --repo-path ../oscal-content --committer-email "[email protected]" --committer-name "openscap-ci" --branch "sync_oscal_pr${{ env.PR_NUMBER }}" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --cac-policy-id "$policy_id" --product "$product"
elif [[ "$line" == "component-definitions"* ]]; then
echo "sync oscal component-definitions according to update of $line ..."
product=$(echo "$line" | cut -f2 -d"/")
profile=$(echo "$line" | cut -f3 -d"/")
echo "The product is $product, the profile is $profile"
poetry run complyscribe sync-oscal-content component-definition --repo-path ../oscal-content --committer-email "[email protected]" --committer-name "openscap-ci" --branch "sync_oscal_pr${{ env.PR_NUMBER }}" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --product "$product" --oscal-profile "$profile"
fi
done < ../filenames.txt
# Step 10: Create PR in CAC content
- name: Create a Pull Request in CAC content
if: ${{ env.CHANGE_FOUND == 'true' }}
run: |
cd cac-content
BRANCH_NAME="sync_oscal_pr${{ env.PR_NUMBER }}"
OWNER="ComplianceAsCode"
REPO="content"
OSCAL_PR_URL="https://github.com/$OWNER/oscal-content/pull/${{ env.PR_NUMBER }}"
if [[ "$(git branch --show-current)" == "$BRANCH_NAME" ]]; then
# Check if the PR exists
PR_EXISTS=$(gh pr list --repo $OWNER/$REPO \
--head $BRANCH_NAME --state open --json id \
| jq length)
# Get commits between main and branch
commits=$(git log master..$BRANCH_NAME --oneline)
# If the PR does not exist and there are commits in the branch,
# then create a PR for this branch.
if [ "$PR_EXISTS" -gt 0 ]; then
echo "PR $BRANCH_NAME already exists. Skipping PR creation."
elif [ -z "$commits" ]; then
echo "No commits between main and $BRANCH_NAME. Skipping PR creation."
else
echo "Creating PR for new branch: $BRANCH_NAME"
PR_BODY="This is an auto-generated PR from OSCAL PR [${{ env.PR_NUMBER }}]("$OSCAL_PR_URL")"
gh pr create --repo $OWNER/$REPO \
--title "Auto-generated PR from OSCAL ${{ env.PR_NUMBER }}" \
--head "$BRANCH_NAME" \
--base "master" \
--body "${PR_BODY}"
fi
else
echo "No branch $BRANCH_NAME. Skipping PR creation."
fi
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}