From e98bbc5978b5515637c94920ca4c270efbbafcff Mon Sep 17 00:00:00 2001 From: Dylan Ravel Date: Mon, 20 Jul 2026 21:22:24 -0600 Subject: [PATCH 1/2] Use GitHub release notes and PR type labels --- .github/dependabot.yml | 5 +-- .github/release.yml | 32 ++++++++++++++++ .github/workflows/label-prs.yml | 66 +++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 15 ++++---- cliff.toml | 52 -------------------------- 5 files changed, 107 insertions(+), 63 deletions(-) create mode 100644 .github/release.yml create mode 100644 .github/workflows/label-prs.yml delete mode 100644 cliff.toml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3112716..cd4e03c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,7 @@ updates: schedule: interval: "weekly" labels: - - "dependencies" + - "type: dependencies" # Maintain GitHub Actions in your workflows - package-ecosystem: "github-actions" @@ -14,5 +14,4 @@ updates: schedule: interval: "weekly" labels: - - "dependencies" - - "ci" + - "type: dependencies" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..7bc1d15 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,32 @@ +changelog: + exclude: + labels: + - ignore-for-release + authors: + - github-actions[bot] + categories: + - title: โœจ Features + labels: + - "type: feature" + - title: ๐Ÿ› Bug Fixes + labels: + - "type: fix" + - title: ๐Ÿš€ Performance Improvements + labels: + - "type: performance" + - title: ๐Ÿ› ๏ธ Refactoring + labels: + - "type: refactor" + - title: ๐Ÿ“š Documentation + labels: + - "type: docs" + - title: ๐Ÿ”ง Build & Dependencies + labels: + - "type: build" + - "type: dependencies" + - title: ๐Ÿงน Maintenance + labels: + - "type: chore" + - title: Other Changes + labels: + - "*" diff --git a/.github/workflows/label-prs.yml b/.github/workflows/label-prs.yml new file mode 100644 index 0000000..80e686b --- /dev/null +++ b/.github/workflows/label-prs.yml @@ -0,0 +1,66 @@ +name: Label PRs + +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: label-prs-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + label: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.type != 'Bot' }} + steps: + - name: Apply category label from conventional-commit title + uses: actions/github-script@v7 + with: + script: | + const TYPE_TO_LABEL = { + feat: "type: feature", + fix: "type: fix", + perf: "type: performance", + refactor: "type: refactor", + docs: "type: docs", + build: "type: build", + ci: "type: build", + chore: "type: chore", + }; + + function labelFor(title) { + const m = (title || "").match(/^([a-zA-Z]+)(?:\([^)]*\))?!?:/); + return m ? (TYPE_TO_LABEL[m[1].toLowerCase()] || null) : null; + } + + const pr = context.payload.pull_request; + const label = labelFor(pr.title); + if (!label) { + core.info(`No category mapping for title "${pr.title}" โ€” leaving labels unchanged.`); + return; + } + + const owner = context.repo.owner; + const repo = context.repo.repo; + const issue_number = pr.number; + const managed = new Set(Object.values(TYPE_TO_LABEL)); + const current = (pr.labels || []).map((l) => l.name); + + for (const name of current) { + if (managed.has(name) && name !== label) { + await github.rest.issues + .removeLabel({ owner, repo, issue_number, name }) + .catch(() => {}); + } + } + + if (current.includes(label)) { + core.info(`"${label}" already present.`); + } else { + await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [label] }); + core.info(`Applied "${label}".`); + } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd08d2f..c233b3c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,21 +22,20 @@ jobs: with: go-version: "1.25" - - name: Generate Release Notes - uses: orhun/git-cliff-action@v4 - with: - config: cliff.toml - args: --latest --strip header --github-repo ${{ github.repository }} + - name: Generate release notes env: - OUTPUT: ${{ runner.temp }}/release-notes.md - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api --method POST "repos/${GITHUB_REPOSITORY}/releases/generate-notes" \ + -f tag_name="${GITHUB_REF_NAME}" --jq .body > "${RUNNER_TEMP}/notes.md" + sed -E -i 's/^(\* )(feat|fix|perf|refactor|docs|build|ci|chore)(\([^)]+\))?!?: /\1/' "${RUNNER_TEMP}/notes.md" - name: Run GoReleaser uses: goreleaser/goreleaser-action@v7 with: distribution: goreleaser version: "~> v2" - args: release --clean --release-notes=${{ runner.temp }}/release-notes.md + args: release --clean --release-notes=${{ runner.temp }}/notes.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} diff --git a/cliff.toml b/cliff.toml deleted file mode 100644 index ba5026e..0000000 --- a/cliff.toml +++ /dev/null @@ -1,52 +0,0 @@ -[remote.github] -owner = "DylanDevelops" -repo = "tmpo" - -[changelog] -body = """ -## What's Changed - -{% for group, commits in commits | group_by(attribute="group") %} -### {{ group | striptags | trim }} -{% for commit in commits -%} -{% set commit_title = commit.message | split(pat="\\n") | first -%} -{% if commit.github.pr_number -%} - {% set pr_str = " (#" ~ commit.github.pr_number ~ ")" -%} - {% set commit_title = commit_title | replace(from=pr_str, to="") -%} -{% endif -%} -- {% if commit.breaking %}[breaking] {% endif %}{{ commit_title | upper_first }}\ -{% if commit.github.username %} by @{{ commit.github.username }}{% endif %}\ -{% if commit.github.pr_number %} in [#{{ commit.github.pr_number }}](https://github.com/DylanDevelops/tmpo/pull/{{ commit.github.pr_number }}){% endif %} -{% endfor %} - -{% endfor %} -{%- set new_contributors = github.contributors | filter(attribute="is_first_time", value=true) %} -{% if new_contributors | length > 0 %} -## New Contributors -{% for contributor in new_contributors -%} -- @{{ contributor.username }} made their first contribution! -{% endfor %} -{% endif %} - -{% if previous.version %} -**Full Changelog**: https://github.com/DylanDevelops/tmpo/compare/{{ previous.version }}...{{ version }} -{% else %} -**Full Changelog**: https://github.com/DylanDevelops/tmpo/commits/{{ version }} -{% endif %} -""" -trim = true - -[git] -conventional_commits = true -filter_unconventional = true -filter_commits = true -commit_parsers = [ - { message = "^feat", group = "โœจ Features" }, - { message = "^fix", group = "๐Ÿ› Bug Fixes" }, - { message = "^perf", group = "๐Ÿš€ Performance Improvements" }, - { message = "^refactor", group = "๐Ÿ› ๏ธ Refactoring" }, - { message = "^docs?", group = "๐Ÿ“š Documentation" }, - { message = "^(build|ci)", group = "๐Ÿ”ง Build & Dependencies" }, - { message = "^chore", group = "๐Ÿงน Maintenance" }, - { message = "^Merge ", skip = true }, -] \ No newline at end of file From b2889b8ab82f51c4d50bf552d1c10d7eff02e32f Mon Sep 17 00:00:00 2001 From: Dylan Ravel Date: Mon, 20 Jul 2026 21:27:49 -0600 Subject: [PATCH 2/2] Document PR title and release workflow rules --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39c41cb..8916206 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -326,6 +326,7 @@ Add pause/resume functionality ### Pull Request Guidelines +- **Use a conventional-commit-style PR title** (e.g. `feat: add pause command`, `fix: correct status display`). The prefix determines the PR's category in the auto-generated release notes โ€” supported: `feat`, `fix`, `perf`, `refactor`, `docs`, `build`, `ci`, `chore`. - Provide a clear description of the changes - Reference any related issues (e.g., "Fixes #123") - Ensure tests pass and code builds successfully @@ -333,6 +334,19 @@ Add pause/resume functionality Reviews can take a few iterations, especially for large contributions. Don't be disheartened if you feel it takes time - we just want to ensure each contribution is high-quality and that any outstanding questions are resolved, captured or documented for posterity. +## Releasing (Maintainers) + +Pushing a `vX.Y.Z` tag triggers the [Release workflow](.github/workflows/release.yml): GoReleaser builds the cross-platform binaries, publishes the GitHub release, and updates the Homebrew tap. + +Release notes are generated automatically from the merged pull requests โ€” there is no manual changelog. Each PR is one entry, attributed to its author and grouped by its `type:` label, which the [PR labeler](.github/workflows/label-prs.yml) applies from the PR title. Add the `ignore-for-release` label to omit a PR. + +To preview the next release's notes before tagging: + +```bash +gh api --method POST repos/DylanDevelops/tmpo/releases/generate-notes \ + -f tag_name=vX.Y.Z -f previous_tag_name= --jq .body +``` + ## Distribution Packaging We appreciate packaging efforts for various package managers and distributions! However, to keep maintenance focused on the core application and avoid setting precedent for supporting every distribution method, we have the following policy: