diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..7528cfb99e6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,49 @@ +name: Release Please + +on: + push: + branches: [master] + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + +jobs: + release-please: + name: Release Please + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + # A pull request opened with GITHUB_TOKEN does not trigger workflows, so + # the release PR is opened as the app instead. + - name: Mint app token + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + client-id: ${{ secrets.RELEASE_APP_CLIENT_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + + - name: Run release-please + id: release + uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5 + with: + token: ${{ steps.app-token.outputs.token }} + config-file: release-please-config.json + manifest-file: .release-please-manifest.json + + - name: Summarise + env: + RELEASE_CREATED: ${{ steps.release.outputs.release_created }} + TAG_NAME: ${{ steps.release.outputs.tag_name }} + PRS_CREATED: ${{ steps.release.outputs.prs_created }} + run: | + if [ "$RELEASE_CREATED" = "true" ]; then + echo "Released $TAG_NAME" >> "$GITHUB_STEP_SUMMARY" + elif [ "$PRS_CREATED" = "true" ]; then + echo "Release PR created/updated" >> "$GITHUB_STEP_SUMMARY" + else + echo "Nothing to release" >> "$GITHUB_STEP_SUMMARY" + fi diff --git a/.github/workflows/semantic-pr.yml b/.github/workflows/semantic-pr.yml new file mode 100644 index 00000000000..ca2199f0be8 --- /dev/null +++ b/.github/workflows/semantic-pr.yml @@ -0,0 +1,37 @@ +name: Semantic PR + +on: + pull_request_target: + types: [opened, edited, synchronize, reopened] + +permissions: + pull-requests: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + title: + name: Conventional PR title + runs-on: ubuntu-latest + timeout-minutes: 4 + steps: + # Squash-merge uses the PR title as the commit message, so linting the + # title is what keeps master's history conventional for release-please. + # The scope is the crate, e.g. `feat(store): ...`. + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + docs + test + refactor + perf + build + ci + chore + revert diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000000..e564a448c98 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.45.0" +} diff --git a/CLAUDE.md b/CLAUDE.md index b8ec983428b..ce8738323bb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -184,11 +184,14 @@ Blockchain → Chain Adapter → Block Stream → Trigger Processing → Runtime ### Commit Convention -Use format: `{crate-name}: {description}` +Use [Conventional Commits](https://www.conventionalcommits.org) with the crate as scope: `{type}({crate-name}): {description}` -- Single crate: `store: Support 'Or' filters` -- Multiple crates: `core, graphql: Add event source to store` -- All crates: `all: {description}` +- Single crate: `feat(store): Support 'Or' filters` +- Multiple crates: `feat(core,graphql): Add event source to store` +- All crates: `refactor(all): {description}` +- Types: `feat`, `fix`, `docs`, `test`, `refactor`, `perf`, `build`, `ci`, `chore`, `revert`; mark breaking changes with `!` (`feat(store)!: ...`) + +release-please reads every commit on master, so each one must follow this format. The Semantic PR workflow checks PR titles, which become the commit message when a PR is squash-merged. release-please derives versions and the changelog from these commits: while the version is below 1.0, `feat` and breaking changes bump the minor version, `fix` and `perf` bump the patch version. ### Git Workflow diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7992c32c49f..cfa58c70632 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,12 +50,18 @@ detailed instructions about how to run the sharded integrations tests. ## Commit messages and pull requests -We use the following format for commit messages: -`{crate-name}: {Brief description of changes}`, for example: `store: Support 'Or' filters`. - -If multiple crates are being changed list them all like this: `core, -graphql: Add event source to store` If all (or most) crates are affected -by the commit, start the message with `all: `. +We use [Conventional Commits](https://www.conventionalcommits.org) with +the crate as scope: `{type}({crate-name}): {Brief description of changes}`, +for example: `feat(store): Support 'Or' filters`. The types are `feat`, +`fix`, `docs`, `test`, `refactor`, `perf`, `build`, `ci`, `chore` and +`revert`; mark breaking changes with `!` (`feat(store)!: ...`). + +If multiple crates are being changed list them all like this: +`feat(core,graphql): Add event source to store`. If all (or most) crates +are affected by the commit, use the scope `all`. + +release-please derives versions and the changelog from the commits on +master, so every commit that lands there must follow this format. The body of the message can be terse, with just enough information to explain what the commit does overall. In a lot of cases, more extensive diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000000..1418f9eec85 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "bootstrap-sha": "8e3debd675309d96eb987bf90f053f378bc917bf", + "last-release-sha": "8e3debd675309d96eb987bf90f053f378bc917bf", + "bump-minor-pre-major": true, + "include-component-in-tag": false, + "packages": { + ".": { + "release-type": "simple", + "changelog-path": "CHANGELOG.md", + "extra-files": [ + { + "type": "toml", + "path": "Cargo.toml", + "jsonpath": "$.workspace.package.version" + }, + { + "type": "toml", + "path": "Cargo.lock", + "jsonpath": "$.package[?(!@.source)].version" + } + ] + } + } +}