Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions .github/automation-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
repositories:
- krogerco/Cache-Android
- krogerco/Telemetry-Android
- krogerco/bedrock-android
127 changes: 127 additions & 0 deletions .github/scripts/propagate-rulesets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/bin/bash
set -e

# Repository ruleset propagation script
# Propagates repository rulesets from ruleset-templates/ to configured repositories

echo "========================================="
echo "Ruleset Propagation Script"
echo "========================================="

# Read the config file and parse repositories
REPOS=$(yq eval '.repositories[]' .github/automation-config.yml)
RULESET_SOURCE_DIR="${RULESET_SOURCE_DIR:-ruleset-templates}"

echo "Source ruleset directory: $RULESET_SOURCE_DIR"
echo ""

# Check if ruleset source directory exists
if [ ! -d "$RULESET_SOURCE_DIR" ]; then
echo "Error: Ruleset source directory '$RULESET_SOURCE_DIR' not found"
exit 1
fi

# Count ruleset files
RULESET_COUNT=$(find "$RULESET_SOURCE_DIR" -name "*.json" | wc -l)
echo "Found $RULESET_COUNT ruleset files to propagate"
echo ""

if [ "$RULESET_COUNT" -eq 0 ]; then
echo "No ruleset files found. Exiting."
exit 0
fi

# Track successful propagations
SUCCESSFUL_REPOS=()
FAILED_REPOS=()

# Process each repository
while IFS= read -r repo; do
[ -z "$repo" ] && continue

echo "========================================="
echo "Processing repository: $repo"
echo "========================================="

REPO_SUCCESS=true

# Process each ruleset file
for ruleset_file in "$RULESET_SOURCE_DIR"/*.json; do
[ -f "$ruleset_file" ] || continue

# Get the ruleset name from the JSON file itself (not the filename)
RULESET_NAME=$(jq -r '.name' "$ruleset_file")
RULESET_FILE_NAME=$(basename "$ruleset_file" .json)
echo "Applying ruleset: $RULESET_FILE_NAME (name: \"$RULESET_NAME\")"

# Check if ruleset already exists in the repository
EXISTING_RULESET_ID=$(gh api \
"/repos/$repo/rulesets" 2>/dev/null | \
jq -r --arg ruleset_name "$RULESET_NAME" '.[] | select(.name == $ruleset_name) | .id' || echo "")

if [ -n "$EXISTING_RULESET_ID" ]; then
echo " Updating existing ruleset (ID: $EXISTING_RULESET_ID)..."

ERROR_OUTPUT=$(gh api \
--method PUT \
"/repos/$repo/rulesets/$EXISTING_RULESET_ID" \
--input "$ruleset_file" 2>&1) || UPDATE_FAILED=true

if [ "$UPDATE_FAILED" = true ]; then
echo " ✗ Failed to update ruleset"
printf ' Error: %s\n' "$ERROR_OUTPUT" | head -5
REPO_SUCCESS=false
UPDATE_FAILED=false
else
echo " ✓ Ruleset updated successfully"
fi
else
echo " Creating new ruleset..."

ERROR_OUTPUT=$(gh api \
--method POST \
"/repos/$repo/rulesets" \
--input "$ruleset_file" 2>&1) || CREATE_FAILED=true

if [ "$CREATE_FAILED" = true ]; then
echo " ✗ Failed to create ruleset"
echo " Error: $ERROR_OUTPUT" | head -5
REPO_SUCCESS=false
CREATE_FAILED=false
else
echo " ✓ Ruleset created successfully"
fi
fi

echo ""
done

if [ "$REPO_SUCCESS" = true ]; then
SUCCESSFUL_REPOS+=("$repo")
else
FAILED_REPOS+=("$repo")
fi

echo ""
done <<< "$REPOS"

echo "========================================="
echo "Propagation complete!"
echo "========================================="

if [ ${#SUCCESSFUL_REPOS[@]} -gt 0 ]; then
echo ""
echo "Successfully updated repositories:"
for repo in "${SUCCESSFUL_REPOS[@]}"; do
echo " ✓ $repo"
done
fi

if [ ${#FAILED_REPOS[@]} -gt 0 ]; then
echo ""
echo "Failed repositories:"
for repo in "${FAILED_REPOS[@]}"; do
echo " ✗ $repo"
done
exit 1
fi
23 changes: 23 additions & 0 deletions .github/workflows/propagate_rulesets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Propagate Repository Rulesets

on:
workflow_dispatch:
# Trigger when rulesets are modified in this repository
# Or when the propagation config/script changes
push:
branches:
- main
paths:
- 'ruleset-templates/**'

jobs:
propagate:
runs-on: ubuntu-latest
steps:
- name: Checkout source repository
uses: actions/checkout@v7

- name: Propagate rulesets
env:
GH_TOKEN: ${{ secrets.RULESET_TOKEN }}
run: bash .github/scripts/propagate-rulesets.sh
25 changes: 25 additions & 0 deletions ruleset-templates/conventional_commits.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "Conventional Commits",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"exclude": [],
"include": [
"~ALL"
]
}
},
"rules": [
{
"type": "commit_message_pattern",
"parameters": {
"operator": "regex",
"pattern": "^(?<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?<scope>\\((?:\\w+|-)+\\))?(?<breaking>!)?(?<subject>:\\s[a-z]+.*)|^(?<merge>Merge \\w+)",
"negate": false,
"name": "Commit messages must be in Conventional Commit format"
}
}
],
"bypass_actors": []
}
101 changes: 101 additions & 0 deletions ruleset-templates/default_branch_protections.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"name": "Default Branch Protections",
"target": "branch",
"enforcement": "active",
"conditions": {
"ref_name": {
"exclude": [],
"include": [
"refs/heads/main",
"refs/heads/alpha",
"refs/heads/beta"
]
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "required_linear_history"
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": true,
"do_not_enforce_on_create": true,
"required_status_checks": [
{
"context": "push / Build",
"integration_id": 15368
},
{
"context": "push / Code Lint",
"integration_id": 15368
},
{
"context": "push / Commit Lint",
"integration_id": 15368
},
{
"context": "push / Instrumentation Tests",
"integration_id": 15368
},
{
"context": "push / Unit Tests",
"integration_id": 15368
},
{
"context": "push / Version Determination",
"integration_id": 15368
}
]
}
},
{
"type": "pull_request",
"parameters": {
"required_approving_review_count": 1,
"dismiss_stale_reviews_on_push": true,
"required_reviewers": [
{
"minimum_approvals": 1,
"file_patterns": [
"*"
],
"reviewer": {
"id": 6969246,
"type": "Team"
}
}
],
"require_code_owner_review": false,
"dismissal_restriction": {
"enabled": false,
"allowed_actors": []
},
"require_last_push_approval": true,
"required_review_thread_resolution": true,
"allowed_merge_methods": [
"merge",
"squash",
"rebase"
]
}
},
{
"type": "creation"
},
{
"type": "copilot_code_review",
"parameters": {
"review_on_push": true,
"review_draft_pull_requests": true
}
}
],
"bypass_actors": []
}
Loading