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..1b5193c --- /dev/null +++ b/.github/scripts/propagate-rulesets.sh @@ -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 diff --git a/.github/workflows/propagate_rulesets.yml b/.github/workflows/propagate_rulesets.yml new file mode 100644 index 0000000..7c70864 --- /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_TOKEN }} + 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..f3ad0bc --- /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": [] +} \ No newline at end of file