Skip to content

Commit 5cfca69

Browse files
committed
add workflow sync-oscal-cac
Signed-off-by: Sophia Wang <huiwang@redhat.com>
1 parent 90fa6a9 commit 5cfca69

1 file changed

Lines changed: 159 additions & 0 deletions

File tree

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
name: Sync OSCAL content to CAC content
2+
permissions:
3+
contents: write
4+
pull-requests: read
5+
on:
6+
push:
7+
branches:
8+
- main
9+
10+
jobs:
11+
sync-oscal-content-update-to-cac:
12+
runs-on: ubuntu-latest
13+
steps:
14+
# Step 1: Set up Python 3
15+
- name: Set up Python 3
16+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
17+
with:
18+
python-version: '3.9'
19+
# Step 2: Install Git
20+
- name: Install Git
21+
run: sudo apt-get update && sudo apt-get install -y git
22+
# Step 3: Checkout OSCAL repo
23+
- name: Checkout OSCAL repo
24+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
25+
with:
26+
repository: ${{ github.repository }}
27+
path: oscal-content
28+
# Step 4: Get the commit message and PR number
29+
- name: Get the commit message and PR number
30+
run: |
31+
cd oscal-content
32+
# Get the latest commit message
33+
COMMIT_MSG=$(git log -1 --pretty=%B)
34+
# Extract the PR number from the commit message (if it's a merge commit)
35+
PR_NUMBER=$(echo "$COMMIT_MSG" | grep -oP '#\K\d+')
36+
if [ -n "$PR_NUMBER" ]; then
37+
echo "Found PR number: $PR_NUMBER"
38+
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
39+
echo "SKIP=false" >> $GITHUB_ENV
40+
PR_INFO=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${PR_NUMBER}")
41+
# Extract PR title from the response
42+
PR_TITLE=$(echo "$PR_INFO" | jq -r .title)
43+
echo "PR Title: $PR_TITLE"
44+
if [[ "$PR_TITLE" == *"Auto-generated PR from CAC"* ]]; then
45+
echo "The PR comes from CAC content. The workflow of Sync OSCAL content to CAC will exit."
46+
echo "Skipping further checks."
47+
echo "SKIP=true" >> $GITHUB_ENV
48+
fi
49+
fi
50+
# Step 5: Get the access token for content write permission to CAC content
51+
- name: Get GitHub app token
52+
if: ${{ env.SKIP == 'false' }}
53+
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
54+
id: app-token
55+
with:
56+
app-id: ${{ secrets.APP_ID }}
57+
private-key: ${{ secrets.PRIVATE_KEY }}
58+
owner: ${{ github.repository_owner }}
59+
repositories: |
60+
content
61+
oscal-content
62+
# Step 6: Dectect the updates of OSCAL content
63+
- name: Detect the changed files of the PR
64+
id: changed-files
65+
run: |
66+
repo=${{ github.repository }}
67+
# Fetch all pages of the files for the pull request
68+
url="repos/$repo/pulls/${{ env.PR_NUMBER }}/files"
69+
response=$(gh api "$url" --paginate)
70+
echo "$response" | jq -r '.[].filename' > filenames.txt
71+
echo "CHANGE_FOUND=false" >> $GITHUB_ENV
72+
if grep -E "catalogs/|profiles/|component-definitions/" filenames.txt ; then
73+
echo "CHANGE_FOUND=true" >> $GITHUB_ENV
74+
fi
75+
cat filenames.txt
76+
env:
77+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
78+
# Step 7: Setup complyscribe
79+
- name: Checkout CAC repo
80+
if: ${{ env.CHANGE_FOUND == 'true' }}
81+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
82+
with:
83+
repository: ComplianceAsCode/content
84+
path: cac-content
85+
token: ${{ steps.app-token.outputs.token }}
86+
fetch-depth: 0
87+
- name: Checkout complyscribe repo
88+
if: ${{ env.CHANGE_FOUND == 'true' }}
89+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
90+
with:
91+
repository: complytime/complyscribe
92+
path: complyscribe
93+
- name: Setup complyscribe
94+
if: ${{ env.CHANGE_FOUND == 'true' }}
95+
run: |
96+
cd complyscribe && python3 -m venv venv && source venv/bin/activate
97+
python3 -m pip install --no-cache-dir "poetry==1.7.1"
98+
poetry install
99+
# Step 8: Sync OSCAL content to CAC content
100+
- name: Sync OSCAL content to CAC content
101+
if: ${{ env.CHANGE_FOUND == 'true' }}
102+
run: |
103+
pr_number="${{ github.event.pull_request.number }}"
104+
cat filenames.txt
105+
cd complyscribe && source venv/bin/activate
106+
while IFS= read -r line; do
107+
if [[ "$line" == *catalogs* ]]; then
108+
echo "sync oscal catalogs according to update of $line ..."
109+
policy_id=$(echo "$line" | cut -f2 -d"/")
110+
echo "The policy_id is $policy_id"
111+
poetry run complyscribe sync-oscal-content catalog --repo-path ../oscal-content --committer-email "openscap-ci@gmail.com" --committer-name "openscap-ci" --branch "sync_oscal_pr$pr_number" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --cac-policy-id "$policy_id"
112+
elif [[ "$line" == "profiles"* ]]; then
113+
echo "sync oscal profiles according to update of $line ..."
114+
policy_id=$(echo $line | cut -f2 -d"/" | cut -f2 -d"-")
115+
product=$(echo "$line" | cut -f2 -d"/" | cut -f1 -d"-")
116+
echo "The policy_id is $policy_id, the product is $product"
117+
poetry run complyscribe sync-oscal-content profile --repo-path ../oscal-content --committer-email "openscap-ci@gmail.com" --committer-name "openscap-ci" --branch "sync_oscal_pr$pr_number" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --cac-policy-id "$policy_id" --product "$product"
118+
elif [[ "$line" == "component-definitions"* ]]; then
119+
echo "sync oscal component-definitions according to update of $line ..."
120+
product=$(echo "$line" | cut -f2 -d"/")
121+
profile=$(echo "$line" | cut -f3 -d"/")
122+
echo "The product is $product, the profile is $profile"
123+
poetry run complyscribe sync-oscal-content component-definition --repo-path ../oscal-content --committer-email "openscap-ci@gmail.com" --committer-name "openscap-ci" --branch "sync_oscal_pr$pr_number" --cac-content-root "$GITHUB_WORKSPACE/cac-content" --product "$product" --oscal-profile "$profile"
124+
fi
125+
done < ../filenames.txt
126+
# Step 9: Create PR to CAC content
127+
- name: Create a Pull Request to OSCAL content
128+
if: ${{ env.CHANGE_FOUND == 'true' }}
129+
run: |
130+
cd cac-content
131+
BRANCH_NAME="sync_oscal_pr${{ env.PR_NUMBER }}"
132+
OWNER="ComplianceAsCode"
133+
REPO="content"
134+
if [[ "$(git branch --show-current)" == "$BRANCH_NAME" ]]; then
135+
# Check if the PR exists
136+
PR_EXISTS=$(gh pr list --repo $OWNER/$REPO \
137+
--head $BRANCH_NAME --state open --json id \
138+
| jq length)
139+
# Get commits between main and branch
140+
commits=$(git log master..$BRANCH_NAME --oneline)
141+
# If the PR does not exist and there are commits in the branch,
142+
# then create a PR for this branch.
143+
if [ "$PR_EXISTS" -gt 0 ]; then
144+
echo "PR $BRANCH_NAME already exists. Skipping PR creation."
145+
elif [ -z "$commits" ]; then
146+
echo "No commits between main and $BRANCH_NAME. Skipping PR creation."
147+
else
148+
echo "Creating PR for new branch: $BRANCH_NAME"
149+
gh pr create --repo $OWNER/$REPO \
150+
--title "Auto-generated PR from OSCAL ${{ env.PR_NUMBER }}" \
151+
--head "$BRANCH_NAME" \
152+
--base "master" \
153+
--body "This is an auto-generated PR from OSCAL ${{ env.PR_NUMBER }} updates"
154+
fi
155+
else
156+
echo "No branch $BRANCH_NAME. Skipping PR creation."
157+
fi
158+
env:
159+
GH_TOKEN: ${{ steps.app-token.outputs.token }}

0 commit comments

Comments
 (0)