From 2f76824ee65cdd76df873676486c2579bb39044e Mon Sep 17 00:00:00 2001 From: Chris Crowell <79209554+ccrowell-kr@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:07:00 -0400 Subject: [PATCH 1/4] ci: propagate rulesets --- .github/automation-config.yml | 4 + .github/scripts/propagate-rulesets.sh | 133 ++++++++++++++++++ .github/workflows/propagate_rulesets.yml | 23 +++ ruleset-templates/conventional_commits.json | 25 ++++ .../default_branch_protections.json | 101 +++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 .github/automation-config.yml create mode 100755 .github/scripts/propagate-rulesets.sh create mode 100644 .github/workflows/propagate_rulesets.yml create mode 100644 ruleset-templates/conventional_commits.json create mode 100644 ruleset-templates/default_branch_protections.json diff --git a/.github/automation-config.yml b/.github/automation-config.yml new file mode 100644 index 0000000..65ccf87 --- /dev/null +++ b/.github/automation-config.yml @@ -0,0 +1,4 @@ +repositories: + - krogerco/Cache-Android + - krogerco/Telemetry-Android + - krogerco/bedrock-android diff --git a/.github/scripts/propagate-rulesets.sh b/.github/scripts/propagate-rulesets.sh new file mode 100755 index 0000000..d47c0f2 --- /dev/null +++ b/.github/scripts/propagate-rulesets.sh @@ -0,0 +1,133 @@ +#!/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 \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/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 \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/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 \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "/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 diff --git a/.github/workflows/propagate_rulesets.yml b/.github/workflows/propagate_rulesets.yml new file mode 100644 index 0000000..e1a2e3b --- /dev/null +++ b/.github/workflows/propagate_rulesets.yml @@ -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_PAT }} + run: bash .github/scripts/propagate-rulesets.sh diff --git a/ruleset-templates/conventional_commits.json b/ruleset-templates/conventional_commits.json new file mode 100644 index 0000000..339a82c --- /dev/null +++ b/ruleset-templates/conventional_commits.json @@ -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": "^(?build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(?\\((?:\\w+|-)+\\))?(?!)?(?:\\s[a-z]+.*)|^(?Merge \\w+)", + "negate": false, + "name": "Commit messages must be in Conventional Commit format" + } + } + ], + "bypass_actors": [] +} \ No newline at end of file diff --git a/ruleset-templates/default_branch_protections.json b/ruleset-templates/default_branch_protections.json new file mode 100644 index 0000000..1c6455d --- /dev/null +++ b/ruleset-templates/default_branch_protections.json @@ -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": [] +} From a94499c34636dbbd024121cad1867a5eb7593e7c Mon Sep 17 00:00:00 2001 From: Chris Crowell <79209554+ccrowell-kr@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:11:03 -0400 Subject: [PATCH 2/4] ci: fix typo --- .github/workflows/propagate_rulesets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/propagate_rulesets.yml b/.github/workflows/propagate_rulesets.yml index e1a2e3b..6821170 100644 --- a/.github/workflows/propagate_rulesets.yml +++ b/.github/workflows/propagate_rulesets.yml @@ -19,5 +19,5 @@ jobs: - name: Propagate rulesets env: - GH_TOKEN: ${{ secrets.RULESET_PAT }} + GH_TOKEN: ${{ secrets.RULESET_TOKEN }} run: bash .github/scripts/propagate-rulesets.sh From e7067f0e7d525a90369c7212d92de961c89ebf9f Mon Sep 17 00:00:00 2001 From: Chris Crowell <79209554+ccrowell-kr@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:14:33 -0400 Subject: [PATCH 3/4] ci: cleanup --- .github/scripts/propagate-rulesets.sh | 6 ------ ruleset-templates/default_branch_protections.json | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/scripts/propagate-rulesets.sh b/.github/scripts/propagate-rulesets.sh index d47c0f2..1b5193c 100755 --- a/.github/scripts/propagate-rulesets.sh +++ b/.github/scripts/propagate-rulesets.sh @@ -56,8 +56,6 @@ while IFS= read -r repo; do # Check if ruleset already exists in the repository EXISTING_RULESET_ID=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/$repo/rulesets" 2>/dev/null | \ jq -r --arg ruleset_name "$RULESET_NAME" '.[] | select(.name == $ruleset_name) | .id' || echo "") @@ -66,8 +64,6 @@ while IFS= read -r repo; do ERROR_OUTPUT=$(gh api \ --method PUT \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/$repo/rulesets/$EXISTING_RULESET_ID" \ --input "$ruleset_file" 2>&1) || UPDATE_FAILED=true @@ -84,8 +80,6 @@ while IFS= read -r repo; do ERROR_OUTPUT=$(gh api \ --method POST \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ "/repos/$repo/rulesets" \ --input "$ruleset_file" 2>&1) || CREATE_FAILED=true diff --git a/ruleset-templates/default_branch_protections.json b/ruleset-templates/default_branch_protections.json index 1c6455d..f3ad0bc 100644 --- a/ruleset-templates/default_branch_protections.json +++ b/ruleset-templates/default_branch_protections.json @@ -98,4 +98,4 @@ } ], "bypass_actors": [] -} +} \ No newline at end of file From 763db17f8558fda21c388c0583f23043def9666d Mon Sep 17 00:00:00 2001 From: Chris Crowell <79209554+ccrowell-kr@users.noreply.github.com> Date: Fri, 14 Aug 2026 15:33:49 -0400 Subject: [PATCH 4/4] ci: put back branch trigger --- .github/workflows/propagate_rulesets.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/propagate_rulesets.yml b/.github/workflows/propagate_rulesets.yml index 6821170..7c70864 100644 --- a/.github/workflows/propagate_rulesets.yml +++ b/.github/workflows/propagate_rulesets.yml @@ -5,8 +5,8 @@ on: # Trigger when rulesets are modified in this repository # Or when the propagation config/script changes push: - # branches: - # - main + branches: + - main paths: - 'ruleset-templates/**'