diff --git a/.github/workflows/generate-changelog.yaml b/.github/workflows/generate-changelog.yaml index 75caf29..5da7429 100644 --- a/.github/workflows/generate-changelog.yaml +++ b/.github/workflows/generate-changelog.yaml @@ -48,6 +48,12 @@ on: default: 'chore: generate changelog' type: string + release_commit_pattern: + description: 'Skip changelog generation when the head commit subject matches this extended regular expression. Set to an empty string to disable the check.' + required: false + default: '^chore:[[:space:]]release([[:space:]]|$)' + type: string + token_app_id: description: 'A GitHub App ID used to generate an access token to create a pull request.' required: true @@ -94,18 +100,55 @@ jobs: fetch-depth: 0 fetch-tags: true + - name: Check for release commit + id: release_commit + shell: bash + env: + RELEASE_COMMIT_PATTERN: ${{ inputs.release_commit_pattern }} + run: | + subject=$(git log -1 --format=%s) + skip=false + + # A release commit bumps the project version, and the matching tag is usually not + # pushed yet when this workflow runs. Commitizen then has nothing to changelog and + # exits non-zero, failing the job. Skip those commits entirely. + if [ -n "$RELEASE_COMMIT_PATTERN" ]; then + set +e + [[ "$subject" =~ $RELEASE_COMMIT_PATTERN ]] + match=$? + set -e + + # A malformed pattern returns 2 rather than 1. Treated as "no match" it would + # disable the check while the job stayed green, so fail on it instead. + if [ "$match" -eq 2 ]; then + echo "::error::release_commit_pattern is not a valid extended regular expression: $RELEASE_COMMIT_PATTERN" + exit 1 + fi + + if [ "$match" -eq 0 ]; then + skip=true + echo "::notice::Head commit is a release commit ('$subject'); skipping changelog generation." + fi + fi + + echo "skip=$skip" >> "$GITHUB_OUTPUT" + - name: Setup Python + if: steps.release_commit.outputs.skip != 'true' uses: dfinity/ci-tools/actions/setup-python@afeee4fbdc0683a88ec5a74ed7f59a2ce0e833ad # main - name: Setup Commitizen + if: steps.release_commit.outputs.skip != 'true' uses: dfinity/ci-tools/actions/setup-commitizen@afeee4fbdc0683a88ec5a74ed7f59a2ce0e833ad # main - name: Generate changelog + if: steps.release_commit.outputs.skip != 'true' uses: dfinity/ci-tools/actions/generate-changelog@afeee4fbdc0683a88ec5a74ed7f59a2ce0e833ad # main with: file_name: ${{ inputs.file_name }} - name: Create pull request + if: steps.release_commit.outputs.skip != 'true' uses: dfinity/ci-tools/actions/create-pr@afeee4fbdc0683a88ec5a74ed7f59a2ce0e833ad # main with: branch_name: ${{ inputs.branch_name }} diff --git a/actions/generate-changelog/action.yaml b/actions/generate-changelog/action.yaml index d34be07..233a6a6 100644 --- a/actions/generate-changelog/action.yaml +++ b/actions/generate-changelog/action.yaml @@ -14,4 +14,17 @@ runs: shell: bash env: FILE_NAME: ${{ inputs.file_name }} - run: cz changelog --incremental --merge-prerelease --file-name="$FILE_NAME" --version-scheme semver2 + run: | + set +e + cz changelog --incremental --merge-prerelease --file-name="$FILE_NAME" --version-scheme semver2 + exit_code=$? + set -e + + # Commitizen exits 3 when it finds no commits to add to the changelog. That is a + # no-op rather than a failure, so it must not fail the job. + if [ "$exit_code" -eq 3 ]; then + echo "::notice::No commits to add to the changelog; nothing to do." + exit 0 + fi + + exit "$exit_code" diff --git a/actions/setup-commitizen/README.md b/actions/setup-commitizen/README.md index 8bed46b..ff64d85 100644 --- a/actions/setup-commitizen/README.md +++ b/actions/setup-commitizen/README.md @@ -2,6 +2,8 @@ This actions sets up [Commitizen](https://commitizen-tools.github.io/commitizen/) for use in actions. It assumes [Python](https://www.python.org/) and [pip](https://pip.pypa.io/en/stable/) are already setup, see the [setup Python action](../setup-python/README.md) for a ready to use action to do this. +The Commitizen version is pinned in `action.yaml`, the same way the [setup Python action](../setup-python/README.md) pins Python. The [generate changelog workflow](../../workflows/generate-changelog/README.md) treats specific Commitizen exit codes as a no-op rather than a failure, so the version it runs against should change deliberately. Bump the pin in `action.yaml` to upgrade. + ## Example usage ```yaml diff --git a/actions/setup-commitizen/action.yaml b/actions/setup-commitizen/action.yaml index 72c90c4..a316d4b 100644 --- a/actions/setup-commitizen/action.yaml +++ b/actions/setup-commitizen/action.yaml @@ -4,6 +4,8 @@ description: 'This action sets up Commitizen for use in actions.' runs: using: composite steps: - - name: Install Commitzen + # Pinned rather than upgraded on every run: the changelog workflow relies on + # Commitizen's exit codes to tell a no-op apart from a real failure. + - name: Install Commitizen shell: bash - run: pip install -U Commitizen + run: pip install 'commitizen==4.18.1' diff --git a/workflows/generate-changelog/README.md b/workflows/generate-changelog/README.md index ea0482d..23509dd 100644 --- a/workflows/generate-changelog/README.md +++ b/workflows/generate-changelog/README.md @@ -8,18 +8,21 @@ Any files that will be changed and committed to the pull request must be listed CHANGELOG.md ``` +Release commits are skipped. A release bumps the project version without adding anything a changelog entry could be generated from, so Commitizen has nothing to do and exits non-zero. Both that and an empty changelog are treated as a no-op rather than a failure. + ## Workflow inputs -| Input | Description | Default | -| -------------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------- | -| `branch_name` | The name of the branch to create the pull request from. | `'patch'` | -| `base_branch_name` | The name of the base branch to create a pull request against. | `'main'` | -| `pull_request_title` | The title of the pull request. | `'chore: automated by GitHub actions'` | -| `pull_request_body` | The body of the pull request. | `'This pull request was automatically created by a GitHub Action.'` | -| `author_name` | The name of the author of the pull request and commit. | `${{ github.actor }}` | -| `author_email` | The email of the author of the pull request and commit. | `${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com` | -| `commit_message` | The message of the commit. | `'chore: automated by GitHub actions'` | -| `token_app_id` | A GitHub App ID used to generate an access token to create a pull request. | _required_ | +| Input | Description | Default | +| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| `branch_name` | The name of the branch to create the pull request from. | `'patch'` | +| `base_branch_name` | The name of the base branch to create a pull request against. | `'main'` | +| `pull_request_title` | The title of the pull request. | `'chore: automated by GitHub actions'` | +| `pull_request_body` | The body of the pull request. | `'This pull request was automatically created by a GitHub Action.'` | +| `author_name` | The name of the author of the pull request and commit. | `${{ github.actor }}` | +| `author_email` | The email of the author of the pull request and commit. | `${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com` | +| `commit_message` | The message of the commit. | `'chore: automated by GitHub actions'` | +| `release_commit_pattern` | Skip changelog generation when the head commit subject matches this extended regular expression. Set to an empty string to disable the check. | `'^chore:[[:space:]]release([[:space:]]\|$)'` | +| `token_app_id` | A GitHub App ID used to generate an access token to create a pull request. | _required_ | ## Workflow secrets