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
3 changes: 2 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,6 @@ Relates to #

### Merge Checklist

- [ ] Squash commits with clear commit message
- [ ] PR **title** is a valid Conventional Commits subject (enforced by the "Validate PR Title" check) - it becomes the squash commit subject that drives the release
- [ ] Squash-merge only (direct pushes to master are blocked; the squash subject = the PR title)
- [ ] Release (CHANGELOG, version bump, tag) is automated from conventional commits on master - no manual step
59 changes: 59 additions & 0 deletions .github/workflows/pr-title-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Lint PR Title

# Enforce Conventional Commits on the PR TITLE.
#
# Why the title (not the commits): PRs are squash-merged and the repo's
# `squash_merge_commit_title` is `PR_TITLE`, so the PR title becomes the commit
# subject on master. That subject is what `automated-release.yml`
# (TriPSs/conventional-changelog-action, angular preset) reads to decide the
# version bump, changelog group, and whether a release is cut. A
# non-conventional title => no release / wrong bump.
#
# This is a required status check on master (ruleset). It must run on EVERY PR,
# so there is no `paths:` filter. `pull_request_target` (not `pull_request`) so
# the check also runs - and can post its status - on PRs from forks, the normal
# contributor path. The action only reads the title from the event payload; it
# never checks out PR/fork code, so running in the base-repo context is safe.

on:
pull_request_target:
types:
- opened
- edited
- synchronize
- reopened

permissions:
pull-requests: read
statuses: write

concurrency:
group: pr-title-lint-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
validate-pr-title:
name: Validate PR Title
runs-on: ubuntu-latest
steps:
- name: Lint PR title against Conventional Commits
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Angular preset types - must match the changelog action so a title
# that lints here also produces the intended release behavior.
# Breaking changes (`feat!:`, `fix!:`) are handled natively.
types: |
feat
fix
docs
style
refactor
perf
test
build
ci
chore
revert
requireScope: false
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ Releases are **fully automated** from conventional commits on `master`:
| `fix:` | Patch |
| Other | Patch (default) |

`master` is protected by a ruleset: **no direct pushes** (all changes land via
PR) and two required checks - `Validate Skill Files` and `Validate PR Title`.
PRs are **squash-merged**, so the **PR title** becomes the master commit
subject that drives the bump above; `pr-title-lint.yml` enforces that the title
is a valid conventional subject. The release flow (`automated-release.yml`)
opens its own `chore(release): vX.Y.Z` PR and auto-merges it.

The release workflow automatically:
- Bumps the version in `CHANGELOG.md`
- Syncs `skills/terraform-skill/SKILL.md` YAML frontmatter
Expand Down
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,18 @@ git commit -m "chore: update workflow dependencies"

Commit type determines the version bump, the changelog group, and whether a release is cut on merge to master. The release workflow updates the version in SKILL.md frontmatter (the single version source) and tags the release.

### The PR title is what counts

PRs are **squash-merged**, and the squash commit subject is the **PR title**. That title is the subject the release workflow reads on master - so the **PR title must be a valid Conventional Commits subject** (e.g. `feat: add native test mocking guidance`), not just your individual commits.

- ✅ Title the PR with a conventional `type: description` (use `feat!:` or a `BREAKING CHANGE:` footer for breaking changes).
- A required CI check, **"Validate PR Title"**, lints the title on every PR and blocks merge until it conforms.
- Your in-progress commit messages are squashed away, so they need not be conventional - only the PR title.

### No direct pushes to master

`master` is protected: **all changes land through a pull request** (direct pushes are rejected). Releases also go through an automated PR - see the Release Process below.

## Submitting Changes

### Pull Request Process
Expand Down
Loading