diff --git a/.github/semantic-release.md b/.github/semantic-release.md new file mode 100644 index 0000000..86ba50d --- /dev/null +++ b/.github/semantic-release.md @@ -0,0 +1,158 @@ +# Semantic Release Configuration + +This repository uses a custom semantic release configuration to provide enhanced changelog generation and GitHub interaction. + +## Features + +### ๐Ÿ“– Enhanced Changelog + +The changelog automatically categorizes commits with emojis and includes contributor information: + +- โœจ **Features** - New functionality (`feat` commits) +- ๐Ÿฉน **Fixes** - Bug fixes (`fix` commits) +- ๐Ÿ“š **Documentation** - Documentation updates (`docs` commits) +- ๐Ÿ› ๏ธ **Internals** - Internal changes (`refactor`, `style`, `test`, `build`, `ci`, `chore` commits) +- โšก **Performance** - Performance improvements (`perf` commits) +- โ†ฉ๏ธ **Reverts** - Reverted changes (`revert` commits) +- โš ๏ธ **BREAKING CHANGES** - Breaking changes (commits with `BREAKING CHANGE` footer) + +Each commit entry includes: + +- Scope (if provided) +- Commit message with linked issues and usernames +- Commit hash link +- Author attribution (@username) + +### ๐Ÿ’ฌ Custom GitHub Comments + +#### Issue Comments + +**For Pre-releases:** + +``` +๐ŸŽ‰ This issue is included in version X.X.X-next.X which is now available for testing! ๐Ÿงช + +๐Ÿ“ฆ NPM: astro-loader-pocketbase@X.X.X-next.X +๐Ÿ“– GitHub Release: X.X.X-next.X + +@username Can you check if everything works as expected in your project with this new version? Any feedback is welcome. + +This change will be included in the next regular release. +``` + +**For Regular Releases:** + +``` +๐ŸŽ‰ This issue is included in version X.X.X which is now available! ๐Ÿš€ + +๐Ÿ“ฆ NPM: astro-loader-pocketbase@X.X.X +๐Ÿ“– GitHub Release: X.X.X +``` + +#### Pull Request Comments + +**For Pre-releases:** + +``` +๐ŸŽ‰ This PR is included in version X.X.X-next.X which is now available for testing! ๐Ÿงช + +๐Ÿ“ฆ NPM: astro-loader-pocketbase@X.X.X-next.X +๐Ÿ“– GitHub Release: X.X.X-next.X + +@username Thank you for your contribution! + +This change will be included in the next regular release. +``` + +**For Regular Releases:** + +``` +๐ŸŽ‰ This PR is included in version X.X.X which is now available! ๐Ÿš€ + +๐Ÿ“ฆ NPM: astro-loader-pocketbase@X.X.X +๐Ÿ“– GitHub Release: X.X.X + +Thank you for your contribution! +``` + +### ๐Ÿ“š Documentation + +Added comprehensive documentation in `.github/semantic-release.md` explaining: + +- How the new changelog categories work +- Examples of issue and PR comment templates +- Configuration file structure +- Commit types and their release impact + +### ๐Ÿ’ป Example Changelog Entry + +Here's an example of how a changelog entry would look for a release like [PR #48](https://github.com/pawcoding/astro-loader-pocketbase/pull/48): + +```markdown +## 2.7.1 (2025-08-10) + +### ๐Ÿฉน Fixes + +- Cleanup entries correctly when using `idField` configuration option ([a1b2c3d](https://github.com/pawcoding/astro-loader-pocketbase/commit/a1b2c3d)) by @pawcoding + +### ๐Ÿ› ๏ธ Internals + +- Replace `eslint` with `oxlint` to make linting tasks even faster ([d4e5f6g](https://github.com/pawcoding/astro-loader-pocketbase/commit/d4e5f6g)) by @pawcoding +- Setup for Copilot and other agents ([h7i8j9k](https://github.com/pawcoding/astro-loader-pocketbase/commit/h7i8j9k)) by @pawcoding +- Add GitHub issue templates for better bug reporting and feature requests ([l0m1n2o](https://github.com/pawcoding/astro-loader-pocketbase/commit/l0m1n2o)) by @pawcoding +- Update dependencies to latest versions ([p3q4r5s](https://github.com/pawcoding/astro-loader-pocketbase/commit/p3q4r5s)) by @pawcoding + +[2.7.1](https://github.com/pawcoding/astro-loader-pocketbase/compare/2.7.0...2.7.1) (5 commits) +``` + +## Configuration Files + +### `release.config.cjs` + +Main configuration file that sets up: + +- Commit analysis rules +- Release notes generation with custom templates +- Changelog generation +- NPM publishing +- Git asset updates +- GitHub releases and comments + +### `.semantic-release/templates/` + +Custom Handlebars templates for changelog generation: + +- `template.hbs` - Main changelog template with emoji categories +- `header.hbs` - Release header format +- `commit.hbs` - Individual commit entry format with contributor attribution +- `footer.hbs` - Release footer with links and commit count + +## Release Branches + +- **`master`** - Stable releases (latest) +- **`next`** - Pre-releases for testing (next channel) + +## Commit Types and Release Impact + +| Type | Description | Release | Changelog Section | +| ---------- | ---------------------------- | ------- | ----------------- | +| `feat` | New feature | Minor | โœจ Features | +| `fix` | Bug fix | Patch | ๐Ÿฉน Fixes | +| `docs` | Documentation (README scope) | Patch | ๐Ÿ“š Documentation | +| `docs` | Other documentation | None | ๐Ÿ“š Documentation | +| `refactor` | Code refactoring | Patch | ๐Ÿ› ๏ธ Internals | +| `style` | Code style changes | Patch | ๐Ÿ› ๏ธ Internals | +| `test` | Adding/updating tests | None | ๐Ÿ› ๏ธ Internals | +| `build` | Build system (deps scope) | Patch | ๐Ÿ› ๏ธ Internals | +| `build` | Other build changes | None | ๐Ÿ› ๏ธ Internals | +| `ci` | CI configuration | None | ๐Ÿ› ๏ธ Internals | +| `chore` | Maintenance tasks | None | ๐Ÿ› ๏ธ Internals | +| `perf` | Performance improvements | Patch | โšก Performance | + +## Breaking Changes + +Commits with `BREAKING CHANGE` or `BREAKING CHANGES` in the footer will: + +- Trigger a major release +- Be listed in a special "โš ๏ธ BREAKING CHANGES" section +- Include migration information from the commit footer diff --git a/.prettierignore b/.prettierignore index c2fe8b4..1696b31 100644 --- a/.prettierignore +++ b/.prettierignore @@ -31,3 +31,6 @@ pnpm-debug.log* .prettierignore .gitignore .nvmrc + +# semantic-release templates (handlebars) +.semantic-release/templates/ diff --git a/.semantic-release/templates/commit.hbs b/.semantic-release/templates/commit.hbs new file mode 100644 index 0000000..2e86076 --- /dev/null +++ b/.semantic-release/templates/commit.hbs @@ -0,0 +1,24 @@ +* +{{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{#if + commit.subject +}}{{commit.subject}}{{else}}{{commit.header}}{{/if}} + +{{~! commit link }} +{{#if @root.linkReferences~}} + ([{{hash}}]({{@root.repository}}/{{@root.commit}}/{{hash}})) +{{~else}} + {{~#if @root.repository}} + ([{{hash}}]({{@root.repository}}/{{@root.commit}}/{{hash}})) + {{~/if}} +{{~/if}} + +{{~! commit references }} +{{~#if references~}} + {{~#each references}} + {{#if + @root.linkReferences + }}([{{this.issue}}]({{@root.repository}}/{{@root.issue}}/{{this.issue}})){{else}}{{this.issue}}{{/if}}{{/each}} +{{~/if}} + +{{~! commit author }} +{{~#if commit.author.name}} by @{{commit.author.name}}{{/if}} \ No newline at end of file diff --git a/.semantic-release/templates/footer.hbs b/.semantic-release/templates/footer.hbs new file mode 100644 index 0000000..5869f08 --- /dev/null +++ b/.semantic-release/templates/footer.hbs @@ -0,0 +1,35 @@ +{{#if host}} + {{~! release link }} + {{~#if linkCompare}} + {{~#if previousTag}} + {{~#if currentTag}} + [{{currentTag}}]({{host}}/{{owner}}/{{repository}}/{{compareUrlFormat}}/{{previousTag}}...{{currentTag}}) + {{~else}} + {{~currentTag}} + {{~/if}} + {{~else}} + {{~#if currentTag}} + {{~currentTag}} + {{~/if}} + {{~/if}} + {{~else}} + {{~#if currentTag}} + {{~currentTag}} + {{~/if}} + {{~/if}} + + {{~! commit range }} + {{~#if commitsUrl}} + {{~#if previousTag}} + ([{{shortHash}}.{{previousTag}}]({{host}}/{{owner}}/{{repository}}/{{commitsUrlFormat}}/{{previousTag}}..{{currentTag}})) + {{~else}} + ([{{shortHash}}]({{host}}/{{owner}}/{{repository}}/{{commitsUrlFormat}}/{{currentTag}})) + {{~/if}} + {{~/if}} + + {{~! commit count }} + {{~#if (gt commits.length 0)}} + ({{commits.length}} + commit{{#unless (eq commits.length 1)}}s{{/unless}}) + {{~/if}} +{{~/if}} \ No newline at end of file diff --git a/.semantic-release/templates/header.hbs b/.semantic-release/templates/header.hbs new file mode 100644 index 0000000..0f94dac --- /dev/null +++ b/.semantic-release/templates/header.hbs @@ -0,0 +1,4 @@ +{{#if version~}} + ## + {{#if title}}{{title}}{{else}}{{version}}{{/if}}{{#if date}} ({{date}}){{/if}} +{{~/if}} \ No newline at end of file diff --git a/.semantic-release/templates/template.hbs b/.semantic-release/templates/template.hbs new file mode 100644 index 0000000..57eef04 --- /dev/null +++ b/.semantic-release/templates/template.hbs @@ -0,0 +1,40 @@ +{{> header}} + +{{#each commitGroups}} + +{{#if title}} +{{#if (eq title 'Features')}} +### โœจ Features +{{else if (eq title 'Bug Fixes')}} +### ๐Ÿฉน Fixes +{{else if (eq title 'Documentation')}} +### ๐Ÿ“š Documentation +{{else if (eq title 'Internals')}} +### ๐Ÿ› ๏ธ Internals +{{else if (eq title 'Performance Improvements')}} +### โšก Performance +{{else if (eq title 'Reverts')}} +### โ†ฉ๏ธ Reverts +{{else}} +### {{title}} +{{/if}} + +{{/if}} +{{#each commits}} +{{> commit root=@root}} +{{/each}} + +{{/each}} + +{{#if noteGroups}} +{{#each noteGroups}} + +### โš ๏ธ BREAKING CHANGES + +{{#each notes}} +* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{text}} +{{/each}} +{{/each}} + +{{/if}} +{{> footer}} \ No newline at end of file diff --git a/.semantic-release/utils/transform-commit.js b/.semantic-release/utils/transform-commit.js new file mode 100644 index 0000000..9851016 --- /dev/null +++ b/.semantic-release/utils/transform-commit.js @@ -0,0 +1,78 @@ +/** + * Transform function for semantic-release commit processing + * Categorizes commits and processes issue/PR links + */ +function transformCommit(commit, context) { + const issues = []; + + commit.notes.forEach((note) => { + note.title = "BREAKING CHANGES"; + }); + + // Group different commit types under appropriate categories + if (commit.type === "feat") { + commit.type = "Features"; + } else if (commit.type === "fix") { + commit.type = "Bug Fixes"; + } else if (commit.type === "docs") { + commit.type = "Documentation"; + } else if ( + ["style", "refactor", "test", "build", "ci", "chore"].includes(commit.type) + ) { + commit.type = "Internals"; + } else if (commit.type === "perf") { + commit.type = "Performance Improvements"; + } else if (commit.revert) { + commit.type = "Reverts"; + } else { + return; + } + + if (commit.scope === "*") { + commit.scope = ""; + } + + if (typeof commit.hash === "string") { + commit.shortHash = commit.hash.substring(0, 7); + } + + if (typeof commit.subject === "string") { + let url = context.repository + ? `${context.host}/${context.owner}/${context.repository}` + : context.repoUrl; + if (url) { + url = `${url}/issues/`; + // Issue URLs. + commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => { + issues.push(issue); + return `[#${issue}](${url}${issue})`; + }); + } + if (context.host) { + // User URLs. + commit.subject = commit.subject.replace( + /\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, + (_, username) => { + if (username.includes("/")) { + return `@${username}`; + } + + return `[@${username}](${context.host}/${username})`; + } + ); + } + } + + // remove references that already appear in the subject + commit.references = commit.references.filter((reference) => { + if (issues.indexOf(reference.issue) === -1) { + return true; + } + + return false; + }); + + return commit; +} + +module.exports = { transformCommit }; diff --git a/release.config.cjs b/release.config.cjs index ff3338b..e681896 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -1,3 +1,9 @@ +const fs = require("fs"); +const path = require("path"); +const { + transformCommit +} = require("./.semantic-release/utils/transform-commit"); + const branch = process.env.GITHUB_REF_NAME; const assetsToUpdate = ["package.json", "package-lock.json"]; @@ -31,7 +37,27 @@ const config = { noteKeywords: ["BREAKING CHANGE", "BREAKING CHANGES"] }, writerOpts: { - commitsSort: ["subject", "scope"] + commitsSort: ["subject", "scope"], + groupBy: "type", + commitGroupsSort: "title", + noteGroupsSort: "title", + mainTemplate: fs.readFileSync( + path.join(__dirname, ".semantic-release/templates/template.hbs"), + "utf-8" + ), + headerPartial: fs.readFileSync( + path.join(__dirname, ".semantic-release/templates/header.hbs"), + "utf-8" + ), + commitPartial: fs.readFileSync( + path.join(__dirname, ".semantic-release/templates/commit.hbs"), + "utf-8" + ), + footerPartial: fs.readFileSync( + path.join(__dirname, ".semantic-release/templates/footer.hbs"), + "utf-8" + ), + transform: transformCommit } } ], @@ -52,7 +78,18 @@ const config = { "@semantic-release/github", { successCommentCondition: - '<% return issue.pull_request || !nextRelease.channel || !issue.labels.some(label => label.name === "released on @next"); %>' + '<% return issue.pull_request || !nextRelease.channel || !issue.labels.some(label => label.name === "released on @next"); %>', + successComment: `<% if (nextRelease.channel) { %>:tada: This <%= issue.pull_request ? 'PR' : 'issue' %> is included in version <%= nextRelease.version %> which is now available for testing! :test_tube: + +๐Ÿ“ฆ **NPM:** [\`<%= package.name %>@<%= nextRelease.version %>\`](https://www.npmjs.com/package/<%= package.name %>/v/<%= nextRelease.version %>) +๐Ÿ“– **GitHub Release:** [<%= nextRelease.version %>](<%= releases[0].url %>) + +<%= issue.pull_request ? '@' + issue.pull_request.user.login + ' Thank you for your contribution!' : '@' + issue.user.login + ' Can you check if everything works as expected in your project with this new version? Any feedback is welcome.' %> + +This change will be included in the next regular release.<% } else { %>:tada: This <%= issue.pull_request ? 'PR' : 'issue' %> is included in version <%= nextRelease.version %> which is now available! :rocket: + +๐Ÿ“ฆ **NPM:** [\`<%= package.name %>@<%= nextRelease.version %>\`](https://www.npmjs.com/package/<%= package.name %>/v/<%= nextRelease.version %>) +๐Ÿ“– **GitHub Release:** [<%= nextRelease.version %>](<%= releases[0].url %>)<%= issue.pull_request ? '\\n\\nThank you for your contribution!' : '' %><% } %>` } ] ]