From c7bf545eafec7660401284ac9c4d5f1ef777184e Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Fri, 29 May 2026 17:35:15 +0530 Subject: [PATCH 1/3] feat: add branch protection workflow for main Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/branch-protection.yml | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 .github/workflows/branch-protection.yml diff --git a/.github/workflows/branch-protection.yml b/.github/workflows/branch-protection.yml new file mode 100644 index 0000000..21dcd4d --- /dev/null +++ b/.github/workflows/branch-protection.yml @@ -0,0 +1,96 @@ +name: Branch Protection Rules + +on: + push: + branches: + - main + +jobs: + revert-and-notify: + runs-on: ubuntu-latest + if: github.actor != 'github-actions[bot]' + environment: prod + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Check if merge commit + id: merge_check + run: | + PARENT_COUNT=$(git log -1 --pretty=format:%P | wc -w) + if [ "$PARENT_COUNT" -gt 1 ]; then + echo "is_merge=true" >> "$GITHUB_OUTPUT" + else + echo "is_merge=false" >> "$GITHUB_OUTPUT" + fi + + - name: Get actor real name + if: steps.merge_check.outputs.is_merge == 'false' + id: actor_info + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + REAL_NAME=$(gh api users/${{ github.actor }} --jq '.name') + if [ -z "$REAL_NAME" ] || [ "$REAL_NAME" = "null" ]; then + REAL_NAME="${{ github.actor }}" + fi + echo "name=$REAL_NAME" >> $GITHUB_OUTPUT + + - name: Get commit title + if: steps.merge_check.outputs.is_merge == 'false' + id: commit_info + run: | + COMMIT_TITLE=$(git log -1 --pretty=format:%s | sed 's/\\/\\\\/g' | sed 's/"/\\"/g') + echo "title=$COMMIT_TITLE" >> "$GITHUB_OUTPUT" + + - name: Revert push to ${{ github.ref_name }} + if: steps.merge_check.outputs.is_merge == 'false' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git revert HEAD --no-edit + git push origin ${{ github.ref_name }} + + - name: Notify Slack + if: steps.merge_check.outputs.is_merge == 'false' + uses: slackapi/slack-github-action@v2.1.1 + with: + webhook-type: incoming-webhook + payload: | + { + "attachments": [ + { + "color": "#ff0000", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": ":x: *Direct Commit to `${{ github.ref_name }}` Branch Not Allowed*\n\n*Pushed By:* `${{ steps.actor_info.outputs.name }}`\n*Repository:* `${{ github.event.repository.name }}`\n*Commit Message:* <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ steps.commit_info.outputs.title }}>\n\n> :memo: *Note:* Please use a feature branch and open a Pull Request instead." + } + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "View Pipeline Run" + }, + "style": "danger", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + } + ] + } + ] + } + ] + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} From 7d5f45b573816f56f238bf2e105096eef255d009 Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Fri, 29 May 2026 17:39:48 +0530 Subject: [PATCH 2/3] chore: add githooks and Makefile setup for local branch protection Co-Authored-By: Claude Sonnet 4.6 --- .githooks/commit-msg | 27 +++++++++++++++++++++++++++ .githooks/pre-push | 31 +++++++++++++++++++++++++++++++ Makefile | 7 +++++++ 3 files changed, 65 insertions(+) create mode 100644 .githooks/commit-msg create mode 100644 .githooks/pre-push create mode 100644 Makefile diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100644 index 0000000..8f80c31 --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,27 @@ +#!/bin/bash + +commit_msg_file=$1 +commit_msg=$(cat "$commit_msg_file") + +# Allow merge commits +if echo "$commit_msg" | grep -qE "^Merge "; then + exit 0 +fi + +# Pattern: [()][!]: +if ! echo "$commit_msg" | grep -qE "^(feat|fix|chore|docs|ci|refactor|test|perf)(\([a-z0-9/-]+\))?(!)?: .+$"; then + echo "" + echo "❌ BLOCKED: Commit message does not follow convention." + echo "" + echo "✅ Format: [()]: " + echo "" + echo "📌 Types: feat | fix | chore | docs | ci | refactor | test | perf" + echo "" + echo "📌 Examples:" + echo " feat(deploy): add Cloud Run job support" + echo " fix(build): resolve duplicate step names" + echo " chore: update action pin hashes" + echo " docs: update README with new inputs" + echo "" + exit 1 +fi diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100644 index 0000000..c3f5566 --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,31 @@ +#!/bin/bash + +branch=$(git symbolic-ref --short HEAD 2>/dev/null) + +if [[ -z "$branch" ]]; then + echo "⚠️ Could not determine branch name (detached HEAD?). Skipping check." + exit 0 +fi + +if [[ "$branch" == "main" ]]; then + echo "❌ BLOCKED: Direct pushes to 'main' are not allowed." + echo "" + echo "✅ Create a feature/fix branch and open a PR instead:" + echo " git checkout -b feat/" + echo " git push origin feat/" + echo " # Then open a Pull Request on GitHub" + exit 1 +fi + +if ! [[ "$branch" =~ ^(main|(feat|fix|chore|hotfix|ci|docs|refactor)/.+)$ ]]; then + echo "❌ BLOCKED: Branch '$branch' does not follow naming convention." + echo "" + echo "✅ Allowed formats:" + echo " main | /" + echo " where ∈ feat | fix | chore | hotfix | ci | docs | refactor" + echo "" + echo "📌 Examples:" + echo " git checkout -b feat/docker-build-cloudrun-actions" + echo " git checkout -b fix/scheduler-reconcile" + exit 1 +fi diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..023bfa3 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: setup + +## setup : Configure Git hooks from .githooks/ +setup: + @echo "Configuring Git hooks..." + @git config core.hooksPath .githooks + @echo "Done. Hooks active." From 633b56981cfec3fa96f6583608f35eb864a12cef Mon Sep 17 00:00:00 2001 From: chinmay jain Date: Fri, 29 May 2026 17:41:05 +0530 Subject: [PATCH 3/3] chore: restrict commit and branch types to feat fix chore hotfix docs Co-Authored-By: Claude Sonnet 4.6 --- .githooks/commit-msg | 4 ++-- .githooks/pre-push | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.githooks/commit-msg b/.githooks/commit-msg index 8f80c31..409f51d 100644 --- a/.githooks/commit-msg +++ b/.githooks/commit-msg @@ -9,13 +9,13 @@ if echo "$commit_msg" | grep -qE "^Merge "; then fi # Pattern: [()][!]: -if ! echo "$commit_msg" | grep -qE "^(feat|fix|chore|docs|ci|refactor|test|perf)(\([a-z0-9/-]+\))?(!)?: .+$"; then +if ! echo "$commit_msg" | grep -qE "^(feat|fix|chore|hotfix|docs)(\([a-z0-9/-]+\))?(!)?: .+$"; then echo "" echo "❌ BLOCKED: Commit message does not follow convention." echo "" echo "✅ Format: [()]: " echo "" - echo "📌 Types: feat | fix | chore | docs | ci | refactor | test | perf" + echo "📌 Types: feat | fix | chore | hotfix | docs" echo "" echo "📌 Examples:" echo " feat(deploy): add Cloud Run job support" diff --git a/.githooks/pre-push b/.githooks/pre-push index c3f5566..0cda404 100644 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -17,12 +17,12 @@ if [[ "$branch" == "main" ]]; then exit 1 fi -if ! [[ "$branch" =~ ^(main|(feat|fix|chore|hotfix|ci|docs|refactor)/.+)$ ]]; then +if ! [[ "$branch" =~ ^(main|(feat|fix|chore|hotfix|docs)/.+)$ ]]; then echo "❌ BLOCKED: Branch '$branch' does not follow naming convention." echo "" echo "✅ Allowed formats:" echo " main | /" - echo " where ∈ feat | fix | chore | hotfix | ci | docs | refactor" + echo " where ∈ feat | fix | chore | hotfix | docs" echo "" echo "📌 Examples:" echo " git checkout -b feat/docker-build-cloudrun-actions"