diff --git a/.commitlintrc.yml b/.commitlintrc.yml new file mode 100644 index 0000000..092c091 --- /dev/null +++ b/.commitlintrc.yml @@ -0,0 +1,12 @@ +# Conventional Commits — enforced on PRs via CI +# https://www.conventionalcommits.org/ +rules: + type-enum: + - 2 + - always + - [feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert] + type-case: [2, always, lower-case] + type-empty: [2, never] + subject-empty: [2, never] + subject-full-stop: [2, never, "."] + header-max-length: [2, always, 100] diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..17ee2b0 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,21 @@ +name: Commitlint + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + commitlint: + name: Validate Commit Messages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + fetch-depth: 0 + + - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6 + with: + configFile: .commitlintrc.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7018ac0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +name: Release + +on: + push: + branches: [main] # semantic-release determines if a new version is needed + workflow_dispatch: # manual re-run (only releases if new commits warrant it) + +permissions: + contents: write + issues: write + pull-requests: write + id-token: write # npm provenance + +jobs: + # ── Semantic Release ──────────────────────────────────────────────── + # Analyzes conventional commits since the last tag, determines the next + # version, publishes to npm, and creates a GitHub Release + git tag. + # No release-worthy commits → no release (idempotent). + release: + name: Semantic Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + fetch-depth: 0 + persist-credentials: true + + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: "1.3.11" + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "lts/*" + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Test + run: bun run test -- --run + + - name: Build + run: bun run build + + - uses: cycjimmy/semantic-release-action@16ca923e6ccbb50770c415a0ccd43709a8c5f7a4 # v4.2.2 + id: release + with: + extra_plugins: | + @semantic-release/commit-analyzer + @semantic-release/release-notes-generator + @semantic-release/npm + @semantic-release/github + @semantic-release/git + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Release result + if: steps.release.outputs.new_release_published == 'true' + run: echo "Released v${{ steps.release.outputs.new_release_version }} to npm" diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..bee79a1 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,21 @@ +{ + "branches": ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/npm", + { + "npmPublish": true + } + ], + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": ["package.json"], + "message": "chore(release): ${nextRelease.version} [skip ci]" + } + ] + ] +}