Skip to content

Commit 2f76824

Browse files
committed
ci: propagate rulesets
1 parent 14ad584 commit 2f76824

5 files changed

Lines changed: 286 additions & 0 deletions

File tree

.github/automation-config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
repositories:
2+
- krogerco/Cache-Android
3+
- krogerco/Telemetry-Android
4+
- krogerco/bedrock-android
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Repository ruleset propagation script
5+
# Propagates repository rulesets from ruleset-templates/ to configured repositories
6+
7+
echo "========================================="
8+
echo "Ruleset Propagation Script"
9+
echo "========================================="
10+
11+
# Read the config file and parse repositories
12+
REPOS=$(yq eval '.repositories[]' .github/automation-config.yml)
13+
RULESET_SOURCE_DIR="${RULESET_SOURCE_DIR:-ruleset-templates}"
14+
15+
echo "Source ruleset directory: $RULESET_SOURCE_DIR"
16+
echo ""
17+
18+
# Check if ruleset source directory exists
19+
if [ ! -d "$RULESET_SOURCE_DIR" ]; then
20+
echo "Error: Ruleset source directory '$RULESET_SOURCE_DIR' not found"
21+
exit 1
22+
fi
23+
24+
# Count ruleset files
25+
RULESET_COUNT=$(find "$RULESET_SOURCE_DIR" -name "*.json" | wc -l)
26+
echo "Found $RULESET_COUNT ruleset files to propagate"
27+
echo ""
28+
29+
if [ "$RULESET_COUNT" -eq 0 ]; then
30+
echo "No ruleset files found. Exiting."
31+
exit 0
32+
fi
33+
34+
# Track successful propagations
35+
SUCCESSFUL_REPOS=()
36+
FAILED_REPOS=()
37+
38+
# Process each repository
39+
while IFS= read -r repo; do
40+
[ -z "$repo" ] && continue
41+
42+
echo "========================================="
43+
echo "Processing repository: $repo"
44+
echo "========================================="
45+
46+
REPO_SUCCESS=true
47+
48+
# Process each ruleset file
49+
for ruleset_file in "$RULESET_SOURCE_DIR"/*.json; do
50+
[ -f "$ruleset_file" ] || continue
51+
52+
# Get the ruleset name from the JSON file itself (not the filename)
53+
RULESET_NAME=$(jq -r '.name' "$ruleset_file")
54+
RULESET_FILE_NAME=$(basename "$ruleset_file" .json)
55+
echo "Applying ruleset: $RULESET_FILE_NAME (name: \"$RULESET_NAME\")"
56+
57+
# Check if ruleset already exists in the repository
58+
EXISTING_RULESET_ID=$(gh api \
59+
-H "Accept: application/vnd.github+json" \
60+
-H "X-GitHub-Api-Version: 2022-11-28" \
61+
"/repos/$repo/rulesets" 2>/dev/null | \
62+
jq -r --arg ruleset_name "$RULESET_NAME" '.[] | select(.name == $ruleset_name) | .id' || echo "")
63+
64+
if [ -n "$EXISTING_RULESET_ID" ]; then
65+
echo " Updating existing ruleset (ID: $EXISTING_RULESET_ID)..."
66+
67+
ERROR_OUTPUT=$(gh api \
68+
--method PUT \
69+
-H "Accept: application/vnd.github+json" \
70+
-H "X-GitHub-Api-Version: 2022-11-28" \
71+
"/repos/$repo/rulesets/$EXISTING_RULESET_ID" \
72+
--input "$ruleset_file" 2>&1) || UPDATE_FAILED=true
73+
74+
if [ "$UPDATE_FAILED" = true ]; then
75+
echo " ✗ Failed to update ruleset"
76+
printf ' Error: %s\n' "$ERROR_OUTPUT" | head -5
77+
REPO_SUCCESS=false
78+
UPDATE_FAILED=false
79+
else
80+
echo " ✓ Ruleset updated successfully"
81+
fi
82+
else
83+
echo " Creating new ruleset..."
84+
85+
ERROR_OUTPUT=$(gh api \
86+
--method POST \
87+
-H "Accept: application/vnd.github+json" \
88+
-H "X-GitHub-Api-Version: 2022-11-28" \
89+
"/repos/$repo/rulesets" \
90+
--input "$ruleset_file" 2>&1) || CREATE_FAILED=true
91+
92+
if [ "$CREATE_FAILED" = true ]; then
93+
echo " ✗ Failed to create ruleset"
94+
echo " Error: $ERROR_OUTPUT" | head -5
95+
REPO_SUCCESS=false
96+
CREATE_FAILED=false
97+
else
98+
echo " ✓ Ruleset created successfully"
99+
fi
100+
fi
101+
102+
echo ""
103+
done
104+
105+
if [ "$REPO_SUCCESS" = true ]; then
106+
SUCCESSFUL_REPOS+=("$repo")
107+
else
108+
FAILED_REPOS+=("$repo")
109+
fi
110+
111+
echo ""
112+
done <<< "$REPOS"
113+
114+
echo "========================================="
115+
echo "Propagation complete!"
116+
echo "========================================="
117+
118+
if [ ${#SUCCESSFUL_REPOS[@]} -gt 0 ]; then
119+
echo ""
120+
echo "Successfully updated repositories:"
121+
for repo in "${SUCCESSFUL_REPOS[@]}"; do
122+
echo "$repo"
123+
done
124+
fi
125+
126+
if [ ${#FAILED_REPOS[@]} -gt 0 ]; then
127+
echo ""
128+
echo "Failed repositories:"
129+
for repo in "${FAILED_REPOS[@]}"; do
130+
echo "$repo"
131+
done
132+
exit 1
133+
fi
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Propagate Repository Rulesets
2+
3+
on:
4+
workflow_dispatch:
5+
# Trigger when rulesets are modified in this repository
6+
# Or when the propagation config/script changes
7+
push:
8+
# branches:
9+
# - main
10+
paths:
11+
- 'ruleset-templates/**'
12+
13+
jobs:
14+
propagate:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout source repository
18+
uses: actions/checkout@v7
19+
20+
- name: Propagate rulesets
21+
env:
22+
GH_TOKEN: ${{ secrets.RULESET_PAT }}
23+
run: bash .github/scripts/propagate-rulesets.sh
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "Conventional Commits",
3+
"target": "branch",
4+
"enforcement": "active",
5+
"conditions": {
6+
"ref_name": {
7+
"exclude": [],
8+
"include": [
9+
"~ALL"
10+
]
11+
}
12+
},
13+
"rules": [
14+
{
15+
"type": "commit_message_pattern",
16+
"parameters": {
17+
"operator": "regex",
18+
"pattern": "^(?<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?<scope>\\((?:\\w+|-)+\\))?(?<breaking>!)?(?<subject>:\\s[a-z]+.*)|^(?<merge>Merge \\w+)",
19+
"negate": false,
20+
"name": "Commit messages must be in Conventional Commit format"
21+
}
22+
}
23+
],
24+
"bypass_actors": []
25+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"name": "Default Branch Protections",
3+
"target": "branch",
4+
"enforcement": "active",
5+
"conditions": {
6+
"ref_name": {
7+
"exclude": [],
8+
"include": [
9+
"refs/heads/main",
10+
"refs/heads/alpha",
11+
"refs/heads/beta"
12+
]
13+
}
14+
},
15+
"rules": [
16+
{
17+
"type": "deletion"
18+
},
19+
{
20+
"type": "non_fast_forward"
21+
},
22+
{
23+
"type": "required_linear_history"
24+
},
25+
{
26+
"type": "required_status_checks",
27+
"parameters": {
28+
"strict_required_status_checks_policy": true,
29+
"do_not_enforce_on_create": true,
30+
"required_status_checks": [
31+
{
32+
"context": "push / Build",
33+
"integration_id": 15368
34+
},
35+
{
36+
"context": "push / Code Lint",
37+
"integration_id": 15368
38+
},
39+
{
40+
"context": "push / Commit Lint",
41+
"integration_id": 15368
42+
},
43+
{
44+
"context": "push / Instrumentation Tests",
45+
"integration_id": 15368
46+
},
47+
{
48+
"context": "push / Unit Tests",
49+
"integration_id": 15368
50+
},
51+
{
52+
"context": "push / Version Determination",
53+
"integration_id": 15368
54+
}
55+
]
56+
}
57+
},
58+
{
59+
"type": "pull_request",
60+
"parameters": {
61+
"required_approving_review_count": 1,
62+
"dismiss_stale_reviews_on_push": true,
63+
"required_reviewers": [
64+
{
65+
"minimum_approvals": 1,
66+
"file_patterns": [
67+
"*"
68+
],
69+
"reviewer": {
70+
"id": 6969246,
71+
"type": "Team"
72+
}
73+
}
74+
],
75+
"require_code_owner_review": false,
76+
"dismissal_restriction": {
77+
"enabled": false,
78+
"allowed_actors": []
79+
},
80+
"require_last_push_approval": true,
81+
"required_review_thread_resolution": true,
82+
"allowed_merge_methods": [
83+
"merge",
84+
"squash",
85+
"rebase"
86+
]
87+
}
88+
},
89+
{
90+
"type": "creation"
91+
},
92+
{
93+
"type": "copilot_code_review",
94+
"parameters": {
95+
"review_on_push": true,
96+
"review_draft_pull_requests": true
97+
}
98+
}
99+
],
100+
"bypass_actors": []
101+
}

0 commit comments

Comments
 (0)