From 80036bec8bba32a8d97d234dec13aa662dfa5273 Mon Sep 17 00:00:00 2001 From: Lars <lars@riok.ch> Date: Sat, 29 Jun 2024 20:00:59 -0500 Subject: [PATCH] chore: rework release process (#1367) For details see the updated contributor documentation. --- .github/release-drafter.yml | 33 ++++++++ .github/workflows/conventional-commits.yml | 18 ---- .github/workflows/docs.yml | 1 + .github/workflows/pr-label-check.yml | 25 ++++++ .github/workflows/release-draft.yml | 29 +++++++ .github/workflows/release-preview.yml | 49 ----------- .github/workflows/release-stable.yml | 27 ------ .github/workflows/release.yml | 96 +++++++++++++--------- .releaserc.yaml | 17 ---- build/package.sh | 2 +- docs/docs/contributing/index.md | 17 +--- docs/docs/contributing/release.md | 42 ++++++++++ 12 files changed, 190 insertions(+), 166 deletions(-) create mode 100644 .github/release-drafter.yml delete mode 100644 .github/workflows/conventional-commits.yml create mode 100644 .github/workflows/pr-label-check.yml create mode 100644 .github/workflows/release-draft.yml delete mode 100644 .github/workflows/release-preview.yml delete mode 100644 .github/workflows/release-stable.yml delete mode 100644 .releaserc.yaml create mode 100644 docs/docs/contributing/release.md diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000000..13f4b4ee4b --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,33 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +change-template: '- $TITLE by @$AUTHOR in #$NUMBER' +change-title-escapes: '\<*_&' +sort-direction: ascending +exclude-labels: + - dependency + - no-changelog + - documentation +categories: + - title: '🚨 Breaking Changes' + labels: + - breaking-change + - title: '🚀 Features' + labels: + - enhancement + - title: '🐛 Bug Fixes' + labels: + - bug +version-resolver: + major: + labels: + - breaking-change + minor: + labels: + - enhancement + patch: + labels: + - bug +template: | + $CHANGES + + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventional-commits.yml deleted file mode 100644 index fac69ee563..0000000000 --- a/.github/workflows/conventional-commits.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: conventional-commits - -on: - pull_request: - branches: - - '**' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - validate: - name: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: webiny/action-conventional-commits@v1.3.0 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 72426177a3..245d7fd2a5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -52,6 +52,7 @@ jobs: build: runs-on: ubuntu-latest steps: + - run: echo "building ${{ inputs.version }} for ${{ inputs.environment }}" - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 - run: dotnet tool restore diff --git a/.github/workflows/pr-label-check.yml b/.github/workflows/pr-label-check.yml new file mode 100644 index 0000000000..d380928b42 --- /dev/null +++ b/.github/workflows/pr-label-check.yml @@ -0,0 +1,25 @@ +name: pr-label-check + +on: + pull_request: + types: + - opened + - synchronize + - labeled + - unlabeled + +concurrency: + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + +jobs: + check-changelog-labels: + runs-on: ubuntu-latest + steps: + - name: PR has no changelog label + if: | + !contains(github.event.pull_request.labels.*.name, 'bug') && + !contains(github.event.pull_request.labels.*.name, 'enhancement') && + !contains(github.event.pull_request.labels.*.name, 'breaking-change') && + !contains(github.event.pull_request.labels.*.name, 'no-changelog') + run: exit 1 diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml new file mode 100644 index 0000000000..d13ac0ae48 --- /dev/null +++ b/.github/workflows/release-draft.yml @@ -0,0 +1,29 @@ +name: draft-releases + +on: + push: + branches: + - main + +concurrency: + group: ${{ github.workflow }} + +jobs: + draft-releases: + permissions: + # write permission is required to create a github release + contents: write + pull-requests: read + runs-on: ubuntu-latest + steps: + - name: draft next + uses: leukeleu/release-drafter@filter-draft-releases + with: + prerelease: true + prerelease-identifier: next + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: draft stable + uses: leukeleu/release-drafter@filter-draft-releases + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-preview.yml b/.github/workflows/release-preview.yml deleted file mode 100644 index 46ebd3f583..0000000000 --- a/.github/workflows/release-preview.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: release-preview - -on: - workflow_dispatch: - inputs: - create_preview_release: - description: 'If this is set to true, a preview release is published, otherwise only the stable branch is updated.' - default: false - type: boolean - required: true - -jobs: - test: - uses: ./.github/workflows/test.yml - - release-preview: - if: ${{ inputs.create_preview_release }} - needs: test - uses: ./.github/workflows/release.yml - with: - environment: next - secrets: - NUGET_API_TOKEN: ${{ secrets.NUGET_API_TOKEN }} - - docs: - uses: ./.github/workflows/docs.yml - needs: release-preview - with: - deploy: true - environment: next - version: ${{ needs.release-preview.outputs.version }} - secrets: - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - - release-stable-update-branch: - runs-on: ubuntu-latest - needs: release-preview - if: ${{ always() }} - steps: - - uses: actions/checkout@v4 - # can't use the secrets.GITHUB_TOKEN since this doesn't trigger further workflows - # https://github.community/t/push-from-action-does-not-trigger-subsequent-action/16854/9 - with: - token: ${{ secrets.GH_REPO_PAT }} - - run: | - git branch -D stable || echo "branch not found, continuing anyway" - git checkout -b stable - git push -f origin stable diff --git a/.github/workflows/release-stable.yml b/.github/workflows/release-stable.yml deleted file mode 100644 index 697fcf7615..0000000000 --- a/.github/workflows/release-stable.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: release-stable - -on: - workflow_dispatch: - -jobs: - test: - uses: ./.github/workflows/test.yml - - release: - needs: test - uses: ./.github/workflows/release.yml - with: - environment: stable - secrets: - NUGET_API_TOKEN: ${{ secrets.NUGET_API_TOKEN }} - - docs: - uses: ./.github/workflows/docs.yml - needs: release - with: - deploy: true - environment: stable - version: ${{ needs.release.outputs.version }} - secrets: - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c86107633..18e8553f04 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,55 +1,75 @@ name: release on: - workflow_call: - inputs: - nuget_source: - required: false - type: string - default: 'https://api.nuget.org/v3/index.json' - environment: - required: true - type: string - secrets: - NUGET_API_TOKEN: - required: true - outputs: - version: - description: 'The created version' - value: ${{ jobs.release.outputs.version }} + release: + types: + - published +# required secrets: NUGET_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN env: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 DOTNET_NOLOGO: true + NUGET_SOURCE: 'https://nuget.pkg.github.com/latonz/index.json' #'https://api.nuget.org/v3/index.json' + +concurrency: + group: ${{ github.workflow }} jobs: release: runs-on: ubuntu-latest - environment: ${{ inputs.environment }} + environment: ${{ github.event.release.prerelease && 'next' || 'stable' }} + permissions: + contents: write outputs: - version: ${{ steps.release-version.outputs.version }} + version: ${{ steps.extract-version.outputs.RELEASE_VERSION }} steps: - - uses: actions/checkout@v4 + - name: checkout git + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-dotnet@v4 - - uses: actions/setup-node@v4 + - id: extract-version + name: extract version + run: echo "RELEASE_VERSION=${RELEASE_NAME#v}" >> "$GITHUB_OUTPUT" + env: + RELEASE_NAME: ${{ github.event.release.name }} + - name: extract release notes + id: extract-release-notes + run: | + { + echo 'RELEASE_NOTES<<EOF'; + gh release view '${{ github.event.release.name }}' --json body --jq .body; + echo 'EOF' + } >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ github.token }} + - name: convert release notes markdown to text + id: release-notes-text + uses: koistya/strip-markdown@v1 with: - node-version: '18' - - run: sudo apt update && sudo apt -y install zipmerge # zipmerge is used to merge the multi target nupkg - - run: | - npm i \ - semantic-release@21.0.2 \ - conventional-changelog-conventionalcommits@5.0.0 \ - @semantic-release/exec@6.0.3 \ - @semantic-release/commit-analyzer@9.0.2 \ - @semantic-release/release-notes-generator@10.0.3 \ - @semantic-release/github@8.0.7 - - run: npx semantic-release + content: ${{ steps.extract-release-notes.outputs.RELEASE_NOTES }} + - name: setup dotnet + uses: actions/setup-dotnet@v4 + - name: install dependencies + run: sudo apt update && sudo apt -y install zipmerge # zipmerge is used to merge the multi target nupkg + - name: build release + run: ./build/package.sh env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NUGET_SOURCE: ${{ inputs.nuget_source }} - NUGET_API_TOKEN: ${{ secrets.NUGET_API_TOKEN }} - MAPPERLY_ENVIRONMENT: ${{ inputs.environment }} - - id: release-version - run: echo "version=${{ env.RELEASE_VERSION }}" >> "$GITHUB_OUTPUT" + MAPPERLY_ENVIRONMENT: ${{ github.event.release.prerelease && 'next' || 'stable' }} + RELEASE_NOTES: ${{ steps.release-notes-text.outputs.content }} + RELEASE_VERSION: ${{ steps.extract-version.outputs.RELEASE_VERSION }} + - name: publish nuget + run: dotnet nuget push './artifacts/*.nupkg' --source "$NUGET_SOURCE" --api-key '${{ secrets.NUGET_API_TOKEN }}' + - name: add release assets + run: gh release upload "${{ github.event.release.name }}" artifacts/*.nupkg + env: + GH_TOKEN: ${{ github.token }} + docs: + uses: ./.github/workflows/docs.yml + needs: release + with: + deploy: true + environment: ${{ github.event.release.prerelease && 'next' || 'stable' }} + version: ${{ needs.release.outputs.version }} + secrets: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.releaserc.yaml b/.releaserc.yaml deleted file mode 100644 index e22aa765cb..0000000000 --- a/.releaserc.yaml +++ /dev/null @@ -1,17 +0,0 @@ -branches: - - name: stable - channel: false - - name: main - channel: next - prerelease: next -plugins: - - - '@semantic-release/commit-analyzer' - - preset: conventionalcommits - - '@semantic-release/release-notes-generator' - - - '@semantic-release/github' - - assets: - - path: 'artifacts/*.nupkg' - - - "@semantic-release/exec" - - verifyReleaseCmd: "echo RELEASE_VERSION=\"${nextRelease.version}\" >> $GITHUB_ENV" - prepareCmd: "RELEASE_NOTES=\"${nextRelease.notes}\" RELEASE_VERSION=\"${nextRelease.version}\" ./build/package.sh" - publishCmd: "dotnet nuget push './artifacts/*.nupkg' --source $NUGET_SOURCE --api-key $NUGET_API_TOKEN" diff --git a/build/package.sh b/build/package.sh index d676f54122..56391aa71c 100755 --- a/build/package.sh +++ b/build/package.sh @@ -14,7 +14,7 @@ RELEASE_NOTES=${RELEASE_NOTES:-''} script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) artifacts_dir="${script_dir}/../artifacts" -echo "building Mapperly v${RELEASE_VERSION}" +echo "building Mapperly v${RELEASE_VERSION} for ${MAPPERLY_ENVIRONMENT:-'local'}" echo "cleaning artifacts dir" mkdir -p "${artifacts_dir}" rm -rf "${artifacts_dir:?}"/* diff --git a/docs/docs/contributing/index.md b/docs/docs/contributing/index.md index 91f4915bc8..d6f1667f38 100644 --- a/docs/docs/contributing/index.md +++ b/docs/docs/contributing/index.md @@ -65,7 +65,7 @@ Before you submit your Pull Request (PR) consider the following guidelines: 6. Create your patch, including appropriate [test](./tests) cases and [documentation](./docs) updates. -7. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit). +7. Commit your changes using a descriptive commit message. Adherence to these conventions is necessary because release notes are automatically generated from these messages. [Husky](https://alirezanet.github.io/Husky.Net/) and [csharpier](https://csharpier.com/) automatically format changed files when commited. @@ -111,18 +111,3 @@ For more information on the failure, check the output logs of the jobs. The reviewers will provide you feedback and approve your changes as soon as they are satisfied. If we ask you for changes in the code, you can follow the [GitHub Guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request) to incorporate feedback in your pull request. - -#### <a name="commit"></a> Commit message format - -Make sure you use [conventional commit message format](https://www.conventionalcommits.org/en/v1.0.0/#summary). - -`<type>: <short summary>` - -#### Type - -Must be one of the following: - -- **feat**: New Feature -- **fix**: Bugfix -- **docs**: Documentation -- **chore**: Chores which should not lead to a changelog / release notes entry diff --git a/docs/docs/contributing/release.md b/docs/docs/contributing/release.md new file mode 100644 index 0000000000..6a09d44519 --- /dev/null +++ b/docs/docs/contributing/release.md @@ -0,0 +1,42 @@ +--- +sidebar_position: 6 +description: How Mapperly versions are released and deployed to NuGet +--- + +# Release process + +Every time a push to the main branch occurs, +a GitHub actions workflow creates or updates +the upcoming next and stable GitHub release drafts by using [release-drafter](https://github.com/release-drafter/release-drafter). +release-drafter generates a changelog based on the PR titles and commit-messages. +The upcoming version is determined by looking at the labels of each PR. +Each merged PR needs to have at least one of the following labels: + +- `no-changelog` or `dependencies` (ignored) +- `breaking-change` (major) +- `enhancement` (minor) +- `bug` (patch) + +To build and publish a Mapperly version, +publish the GitHub draft release. +The release notes can be modified before the publication as needed. + +:::note +If the release notes are changed and another commit is pushed to `main' +after the changes have been saved, +the changes will be lost. +::: + +When the release is published, +GitHub will create a tag pointing to the latest commit of `main`. +A GitHub action will export the release notes, +strip the Markdown (unfortunately NuGet release notes don't support Markdown), +build the Mapperly package, +deploy it on NuGet +and attach the built NuGet package to the GitHub release. + +After the NuGet package is successfully built and deployed, +the documentation will be built and deployed. + +To release a version from another commit than the `HEAD` of `main`, +manually create and publish a GitHub release targeting the desired commit.