feat(release): automate approved milestone publication #178
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: Commit Lint | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, edited] | |
| branches: | |
| - main | |
| - next | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| pull-requests: read | |
| contents: read | |
| jobs: | |
| commit-lint: | |
| name: Validate Commits | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: "⤵️ Check out code from GitHub" | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - name: "📝 Lint commit messages" | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| POLICY_BASELINE_SHA: d092066be26d4ec5aedc7a19ed3d85e03a343b35 | |
| DISALLOWED_TRAILER_PATTERN: '^[[:space:]]*Co-authored-by:.*(\[bot\]|noreply@anthropic\.com|noreply@openai\.com|noreply@x\.ai|codex@openai\.com|copilot@github\.com|claude@anthropic\.com)' | |
| run: | | |
| set -euo pipefail | |
| CONVENTIONAL_PATTERN='^(feat|fix|perf|refactor|docs|test|build|ci|style|chore|revert)(\([^)]+\))?!?: .{1,72}$' | |
| errors=0 | |
| checked=0 | |
| while IFS= read -r sha; do | |
| if git show -s --format='%B' "$sha" | grep -qiE "$DISALLOWED_TRAILER_PATTERN"; then | |
| echo "❌ Disallowed trailer found (${sha:0:7}): remove before merging" | |
| errors=$((errors + 1)) | |
| fi | |
| parent_count=$(git cat-file -p "$sha" | grep -c '^parent' || true) | |
| [ "$parent_count" -gt 1 ] && continue | |
| # The repository adopted enforced Conventional Commits at this | |
| # baseline. Preserve older history instead of requiring a rewrite | |
| # when the persistent next branch is promoted. | |
| if git merge-base --is-ancestor "$sha" "$POLICY_BASELINE_SHA"; then | |
| continue | |
| fi | |
| subject=$(git show -s --format='%s' "$sha") | |
| checked=$((checked + 1)) | |
| if ! echo "$subject" | grep -qE "$CONVENTIONAL_PATTERN"; then | |
| echo "❌ Invalid commit message (${sha:0:7}): expected type(scope): description ≤72 chars" | |
| errors=$((errors + 1)) | |
| fi | |
| done < <(git log --format='%H' "${BASE_SHA}..${HEAD_SHA}") | |
| if [ "$errors" -gt 0 ]; then | |
| echo "::error::$errors commit(s) failed validation — run: git rebase -i ${BASE_SHA}" | |
| exit 1 | |
| fi | |
| echo "✅ $checked commit(s) validated" | |
| pr-title: | |
| name: Validate PR Title | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: "📋 Check PR title follows Conventional Commits" | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| run: | | |
| PATTERN='^(feat|fix|perf|refactor|docs|test|build|ci|style|chore|revert)(\([^)]+\))?!?: .{1,72}$' | |
| if ! echo "$PR_TITLE" | grep -qE "$PATTERN"; then | |
| echo "::error::PR title must follow Conventional Commits: type(scope): description" | |
| exit 1 | |
| fi | |
| echo "✅ PR title valid" | |
| branch-naming: | |
| name: Validate Branch Name | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: "🌿 Check branch naming convention" | |
| env: | |
| BRANCH: ${{ github.head_ref }} | |
| run: | | |
| PATTERN='^(feature|bug|hotfix)-[1-9][0-9]*$' | |
| if echo "$BRANCH" | grep -qE '^(dependabot|renovate)/' || \ | |
| [ "$BRANCH" = "next" ]; then | |
| echo "✅ OK" | |
| exit 0 | |
| fi | |
| if ! echo "$BRANCH" | grep -qE "$PATTERN"; then | |
| echo "::error::Branch name must be feature-<id>, bug-<id>, or hotfix-<id>" | |
| exit 1 | |
| fi | |
| echo "✅ Branch name valid" |