From 52eedbcb51fbed499bc74d9efc1f577c330f1230 Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Tue, 20 Jul 2021 09:28:56 -0400 Subject: [PATCH 1/6] Rewrite release checklist and workflow - see #64 --- .github/workflows/release.yml | 137 ++++++++++++----------------- .github/workflows/update-major.yml | 49 +++++++++++ docs/release.md | 26 +++--- 3 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/update-major.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c8d9ae9..0061060 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,107 +1,86 @@ -name: Create a release - -# Use this workflow to: -# - Create a release from a branch -# - Update major and minor tags when a release is published +name: Create a draft release on: # Run when manually triggered workflow_dispatch: inputs: - # User inputs the release version, for example `2.2.1` version: - description: 'Release version to create, for example 2.2.1' + description: 'Release version to create, for example 2.3.0' required: true - default: "2.2.1" - # Run when a release is published - release: - types: [published] + default: "2.3.0" + # Run when a release branch is pushed + push: + branches: + - 'release-*.*.*' defaults: run: shell: bash -env: - GIT_AUTHOR_NAME: ${{ github.repository_owner }} - GIT_AUTHOR_EMAIL: ${{ github.repository_owner }}@users.noreply.github.com - jobs: - create-release: - if: ${{ github.event_name == 'workflow_dispatch' }} + + meta: + name: Parse event data into outputs runs-on: ubuntu-20.04 outputs: - changelog: ${{ steps.changelog.outputs.changelog }} + ver: ${{ steps.get-ver.outputs.ver }} + tag: ${{ steps.get-tag.outputs.tag }} + branch: ${{ steps.get-branch.outputs.branch }} + pr: ${{ steps.get-pr.outputs.pr }} + data: ${{ steps.get-data.outputs.data }} steps: - # Check out this repository at the same ref that triggered - # this workflow run - - uses: actions/checkout@v2 - - # Ensure the specified version has a section in CHANGELOG.md - - name: Check CHANGELOG.md for '${{ github.event.inputs.version }}' - id: changelog + - name: Checkout ${{ github.repository }}:${{ github.ref }} + uses: actions/checkout@v2 + - name: 'ver: Version number for this release' + id: get-ver run: | - _ver="${{ github.event.inputs.version }}" - _changelog= - if grep -Fq "## [${_ver}]" docs/CHANGELOG.md; then - echo "Found '## [${_ver}]' in CHANGELOG.md :+1:" - else - echo "FAIL: Did not find '## [${_ver}]' in CHANGELOG.md" - exit 1 - fi - # TODO: Make this :point_down: actually work - _changelog=":shrug:" - echo "Set changelog to '${_changelog}'." - echo "::set-output name=changelog::${_changelog}" - - # Create the draft release - # https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#get-a-release - # https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#create-a-release - # https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#get-the-latest-release - - - name: Create the draft release - id: mkrel + case "${{ github.event_name }}" in + "workflow_dispatch") + _ver="${{ github.event.inputs.version }}" ;; + "push") + _ver="${GITHUB_REF#refs/heads/release-}}" ;; + *) + exit 1 + esac + echo "::set-output name=ver::${_ver}" + - name: 'tag: Tag for this release' + # TODO: Fail if tag already exists + id: get-tag + run: | + _tag="v${{ steps.get-ver.outputs.ver }}" + echo "::set-output name=tag::${_tag}" + - name: 'branch: Branch for this release' + # TODO: Create branch, if it does not exist + id: get-branch + run: | + _branch="release-${{ steps.get-ver.outputs.ver }}" + echo "::set-output name=branch::${_branch}" + - name: 'data: Payload for releases API call' + # TODO: Ensure output is usable as JSON + id: get-data run: | - _ver="${{ github.event.inputs.version }}" - _tag="v${_ver}" _repo="${{ github.repository }}" - _relbranch="${GITHUB_REF#refs/heads/}" - echo "${_ver} ${_tag} ${_repo} ${_relbranch}" + _ver="v${{ steps.get-ver.outputs.ver }}" + _tag="${{ steps.get-tag.outputs.tag }}" + _branch="${{ steps.get-branch.outputs.branch }}" + echo "${_ver} ${_tag} ${_repo} ${_branch}" _data="{ \"tag_name\": \"${_tag}\", - \"target_commitish\": \"${_relbranch}\", + \"target_commitish\": \"${_branch}\", \"name\": \"${_repo#*/} ${_tag}\", \"body\": \"words words words\", \"draft\": true }" echo "${_data}" - # Create the draft release - echo "${_data}" | \ - GITHUB_TOKEN=${{ secrets.PAT }} \ - gh api -X POST /repos/:owner/:repo/releases --input - + echo "::set-output name=data::${_data}" - # When a release is published, update short - # version tags, for example v2 and v2.2 - update-tags: - if: ${{ github.event_name == 'release' }} + create-release: + name: Create release ${{ steps.get-tag.outputs.tag }} + needs: meta + environment: uses-pat runs-on: ubuntu-20.04 steps: - # Check out this repository at the same ref that triggered - # this workflow run - - uses: actions/checkout@v2 - - # Update short version tags, for example v2 and v2.2 - - name: Update major and minor version tags - id: roll-tags + - name: Create the draft release run: | - # Set up variables. - TAG="${INPUT_TAG:-${GITHUB_REF#refs/tags/}}" # v2.2.0 - MINOR="${TAG%.*}" # v2.2 - MAJOR="${MINOR%.*}" # v2 - MESSAGE="Release ${TAG}" - # Set up Git. - git config user.name "${GIT_AUTHOR_NAME}" - git config user.email "${GIT_AUTHOR_EMAIL}" - # Update MAJOR/MINOR tag - git tag -fa "${MAJOR}" -m "${MESSAGE}" - git tag -fa "${MINOR}" -m "${MESSAGE}" - # Push MAJOR/MINOR tag - git push --force origin "${MINOR}" - git push --force origin "${MAJOR}" + _data="${{ needs.meta.outputs.data }}" + echo "${_data}" | \ + GITHUB_TOKEN=${{ secrets.PAT }} \ + gh api -X POST /repos/:owner/:repo/releases --input - diff --git a/.github/workflows/update-major.yml b/.github/workflows/update-major.yml new file mode 100644 index 0000000..7954dec --- /dev/null +++ b/.github/workflows/update-major.yml @@ -0,0 +1,49 @@ +name: Update major tag + +on: + # Run when a release tag is pushed + push: + branches-ignore: + - '**' + tags: + - 'v*.*.*' + +permissions: + contents: write + +jobs: + # When a release tag is pushed, update the matching short + # version tag, for example v2 + update-tags: + runs-on: ubuntu-20.04 + steps: + + # Check out this repository at the same ref that triggered + # this workflow run + - uses: actions/checkout@v2 + + # Update short version tags, for example v2 and v2.2 + - name: Update major version tag + id: roll-tag + env: + GIT_AUTHOR_NAME: ${{ github.repository_owner }} + GIT_AUTHOR_EMAIL: ${{ github.repository_owner }}@users.noreply.github.com + run: | + # Set up variables. + _tag="${INPUT_TAG:-${GITHUB_REF#refs/tags/}}" # v2.2.0 + _major="${MINOR%.*}" # v2 + _msg="Release ${_tag}" + _curr="$(gh api /repos/:owner/:repo/releases/latest | \ + jq -r .tag_name)" + # If tag pushed is not for latest release, exit early + if [ "$_tag" != "$_curr" ]; then + echo "SKIP: Tag pushed ($_tag) is not for latest release." + exit 0 + fi + # Set up Git. + git config user.name "${GIT_AUTHOR_NAME}" + git config user.email "${GIT_AUTHOR_EMAIL}" + # Update _major tag + git tag -fa "${_major}" -m "${_msg}" + # Push _major tag + git push --force origin "${_major}" diff --git a/docs/release.md b/docs/release.md index 464b187..1253c6c 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,24 +1,26 @@ # Release checklist -TODO: Make this simpler and more reliable. +- [ ] Is the [changelog] up-to-date? -## Prerequisites -- [ ] Identify the release version, for example `2.2.1` -- [ ] Create a corresponding branch from `main`, for example `release-2.2.1` +- [ ] Pick the release version, for example `2.3.0` +- [ ] Create a matching branch from `main`, for example `release-2.3.0` - [ ] Push your branch +- [ ] Did the _Create a draft release_ [workflow] run automatically? -## Run [the _Create a release_ workflow](https://github.com/solvaholic/octodns-sync/actions) from your branch -Which will perform these tasks: +- [ ] Rename _Unreleased_ section of CHANGELOG.md to release version +- [ ] Any other changes to make? -- [ ] Ensure the specified version has a section in CHANGELOG.md -- [ ] Create the draft release -## Finalize and publish [the release](https://github.com/solvaholic/octodns-sync/releases) +- [ ] Finalize and publish the [release], which pushes the release tag +- [ ] Did the _Update major tag_ [workflow] run automatically? -Which will push the release tag. If you'd like to publish this release to the marketplace, be sure to check that box. -## Clean up +- [ ] Delete any [branches] you're done with -- [ ] Delete any [branches](https://github.com/solvaholic/octodns-sync/branches) you're done with + +[changelog]: https://github.com/solvaholic/octodns-sync/blob/main/docs/CHANGELOG.md +[workflow]: https://github.com/solvaholic/octodns-sync/actions +[release]: https://github.com/solvaholic/octodns-sync/releases +[branches]: https://github.com/solvaholic/octodns-sync/branches \ No newline at end of file From 733af293cb5be5e7962d2e16f77921c74f2868c7 Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Tue, 20 Jul 2021 20:51:30 -0400 Subject: [PATCH 2/6] Tell yamllint to allow 85 char/line to maybe-lazily step around the error in this run: https://github.com/solvaholic/octodns-sync/runs/3114434150 --- .github/workflows/linter.yml | 5 ++++- Makefile | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 85523c0..096fc7d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -28,7 +28,10 @@ jobs: # Run yamllint to lint YAML - name: Lint YAML files - run: yamllint --no-warnings ./ + run: | + yamllint --no-warnings \ + -d "{extends: default, rules: {line-length: {max: 85}}}" \ + ./ # Run shellcheck to lint shell scripts - name: Lint shell scripts diff --git a/Makefile b/Makefile index 06284e4..a63163d 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,9 @@ lint-yaml: @docker run --rm \ -v "$(realpath .)":"/tmp/lint":ro \ --entrypoint /usr/local/bin/yamllint \ - github/super-linter --no-warnings /tmp/lint + github/super-linter --no-warnings \ + -d "{extends: default, rules: {line-length: {max: 85}}}" \ + /tmp/lint @echo "All the YAML things linted!" lint-shell: From bd769fbc0d62bcf3175354fe655587ee78817056 Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Wed, 21 Jul 2021 07:59:30 -0400 Subject: [PATCH 3/6] Avoid multiline string output in release.yml and clean up a comment in update-major.yml --- .github/workflows/release.yml | 11 +++++------ .github/workflows/update-major.yml | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0061060..118c0ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,18 +56,17 @@ jobs: _branch="release-${{ steps.get-ver.outputs.ver }}" echo "::set-output name=branch::${_branch}" - name: 'data: Payload for releases API call' - # TODO: Ensure output is usable as JSON id: get-data run: | _repo="${{ github.repository }}" - _ver="v${{ steps.get-ver.outputs.ver }}" + _ver="${{ steps.get-ver.outputs.ver }}" _tag="${{ steps.get-tag.outputs.tag }}" _branch="${{ steps.get-branch.outputs.branch }}" echo "${_ver} ${_tag} ${_repo} ${_branch}" - _data="{ \"tag_name\": \"${_tag}\", - \"target_commitish\": \"${_branch}\", - \"name\": \"${_repo#*/} ${_tag}\", - \"body\": \"words words words\", + _data="{ \"tag_name\": \"${_tag}\", \ + \"target_commitish\": \"${_branch}\", \ + \"name\": \"${_repo#*/} ${_tag}\", \ + \"body\": \"words words words\", \ \"draft\": true }" echo "${_data}" echo "::set-output name=data::${_data}" diff --git a/.github/workflows/update-major.yml b/.github/workflows/update-major.yml index 7954dec..8f1aa11 100644 --- a/.github/workflows/update-major.yml +++ b/.github/workflows/update-major.yml @@ -22,7 +22,7 @@ jobs: # this workflow run - uses: actions/checkout@v2 - # Update short version tags, for example v2 and v2.2 + # Update short version tag, for example v2 - name: Update major version tag id: roll-tag env: From 404cfeea7ed28906ee3eb90a4efc7e400aba39b9 Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Thu, 22 Jul 2021 09:33:51 -0400 Subject: [PATCH 4/6] Modularize release.yml more to put the release creation logic all in that step, and to intentionally deal with the release branch already existing or not. I feel like this workflow should fail if the release tag already exists. But I'm unsure, so leaving it like this to see how that goes. --- .github/workflows/release.yml | 68 ++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 118c0ff..d59478f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,9 +25,8 @@ jobs: outputs: ver: ${{ steps.get-ver.outputs.ver }} tag: ${{ steps.get-tag.outputs.tag }} - branch: ${{ steps.get-branch.outputs.branch }} - pr: ${{ steps.get-pr.outputs.pr }} - data: ${{ steps.get-data.outputs.data }} + branch: ${{ steps.get-ref.outputs.branch }} + ref: ${{ steps.get-ref.outputs.ref }} steps: - name: Checkout ${{ github.repository }}:${{ github.ref }} uses: actions/checkout@v2 @@ -40,46 +39,67 @@ jobs: "push") _ver="${GITHUB_REF#refs/heads/release-}}" ;; *) + echo "FAIL: Unexpected workflow trigger." exit 1 esac echo "::set-output name=ver::${_ver}" - name: 'tag: Tag for this release' - # TODO: Fail if tag already exists id: get-tag run: | _tag="v${{ steps.get-ver.outputs.ver }}" echo "::set-output name=tag::${_tag}" - - name: 'branch: Branch for this release' - # TODO: Create branch, if it does not exist - id: get-branch + - name: 'branch, ref: Branch for this release' + id: get-ref run: | _branch="release-${{ steps.get-ver.outputs.ver }}" + _ref="refs/heads/${_branch}" echo "::set-output name=branch::${_branch}" - - name: 'data: Payload for releases API call' + echo "::set-output name=ref::${_ref}" + + create-branch: + name: Create the release branch for ${{ needs.meta.outputs.ver }} + needs: meta + if: ${{ github.ref != needs.meta.outputs.ref }} + runs-on: ubuntu-20.04 + steps: + - name: Checkout ${{ github.repository }}:${{ github.ref }} + uses: actions/checkout@v2 + - name: Configure Git client + run: | + git config user.name "${{ github.repository_owner }}" + git config user.email "${{ github.repository_owner }}@users.noreply.github.com" + - name: Create branch ${{ needs.meta.outputs.branch }} + run: | + _ghref="${{ github.ref }}" + _myref="${{ needs.meta.outputs.ref }}" + git update-ref "${_myref}" "${_ghref}" + git push origin "${_myref}" + + create-release: + name: Create draft release ${{ needs.meta.outputs.tag }} + needs: [meta, create-branch] + environment: uses-pat + runs-on: ubuntu-20.04 + steps: + - name: Checkout ${{ github.repository }}:${{ github.ref }} + uses: actions/checkout@v2 + - name: Assemble data payload for releases API call id: get-data run: | _repo="${{ github.repository }}" - _ver="${{ steps.get-ver.outputs.ver }}" - _tag="${{ steps.get-tag.outputs.tag }}" - _branch="${{ steps.get-branch.outputs.branch }}" - echo "${_ver} ${_tag} ${_repo} ${_branch}" + _tag="${{ needs.meta.outputs.tag }}" + _branch="${{ needs.meta.outputs.branch }}" + _body="words words words" + _draft="true" _data="{ \"tag_name\": \"${_tag}\", \ \"target_commitish\": \"${_branch}\", \ \"name\": \"${_repo#*/} ${_tag}\", \ - \"body\": \"words words words\", \ - \"draft\": true }" - echo "${_data}" + \"body\": \"${_body}\", \ + \"draft\": ${_draft} }" echo "::set-output name=data::${_data}" - - create-release: - name: Create release ${{ steps.get-tag.outputs.tag }} - needs: meta - environment: uses-pat - runs-on: ubuntu-20.04 - steps: - - name: Create the draft release + - name: Call releases API to create draft release run: | - _data="${{ needs.meta.outputs.data }}" + _data="${{ steps.get-data.outputs.data }}" echo "${_data}" | \ GITHUB_TOKEN=${{ secrets.PAT }} \ gh api -X POST /repos/:owner/:repo/releases --input - From 95d25ce1d0d86cd1a265414673d9715c3990f6ce Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Fri, 23 Jul 2021 13:10:05 -0400 Subject: [PATCH 5/6] Un-do that yamllint exception and learn, again, to lint locally before committing and pushing. --- .github/workflows/linter.yml | 1 - .github/workflows/release.yml | 6 ++++-- .github/workflows/update-major.yml | 13 +++++++------ Makefile | 1 - 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 096fc7d..d3d78d0 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -30,7 +30,6 @@ jobs: - name: Lint YAML files run: | yamllint --no-warnings \ - -d "{extends: default, rules: {line-length: {max: 85}}}" \ ./ # Run shellcheck to lint shell scripts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d59478f..395c997 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,8 +66,10 @@ jobs: uses: actions/checkout@v2 - name: Configure Git client run: | - git config user.name "${{ github.repository_owner }}" - git config user.email "${{ github.repository_owner }}@users.noreply.github.com" + _user="${{ github.repository_owner }}" + _email="${_user}@users.noreply.github.com" + git config user.name "${_user}" + git config user.email "${_email}" - name: Create branch ${{ needs.meta.outputs.branch }} run: | _ghref="${{ github.ref }}" diff --git a/.github/workflows/update-major.yml b/.github/workflows/update-major.yml index 8f1aa11..76256f5 100644 --- a/.github/workflows/update-major.yml +++ b/.github/workflows/update-major.yml @@ -22,12 +22,16 @@ jobs: # this workflow run - uses: actions/checkout@v2 + - name: Configure Git client + run: | + _user="${{ github.repository_owner }}" + _email="${_user}@users.noreply.github.com" + git config user.name "${_user}" + git config user.email "${_email}" + # Update short version tag, for example v2 - name: Update major version tag id: roll-tag - env: - GIT_AUTHOR_NAME: ${{ github.repository_owner }} - GIT_AUTHOR_EMAIL: ${{ github.repository_owner }}@users.noreply.github.com run: | # Set up variables. _tag="${INPUT_TAG:-${GITHUB_REF#refs/tags/}}" # v2.2.0 @@ -40,9 +44,6 @@ jobs: echo "SKIP: Tag pushed ($_tag) is not for latest release." exit 0 fi - # Set up Git. - git config user.name "${GIT_AUTHOR_NAME}" - git config user.email "${GIT_AUTHOR_EMAIL}" # Update _major tag git tag -fa "${_major}" -m "${_msg}" # Push _major tag diff --git a/Makefile b/Makefile index a63163d..0bc2f17 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ lint-yaml: -v "$(realpath .)":"/tmp/lint":ro \ --entrypoint /usr/local/bin/yamllint \ github/super-linter --no-warnings \ - -d "{extends: default, rules: {line-length: {max: 85}}}" \ /tmp/lint @echo "All the YAML things linted!" From 5978f552eaf884e527c7bb7344be346cd9f1c283 Mon Sep 17 00:00:00 2001 From: "Roger D. Winans" Date: Fri, 23 Jul 2021 15:10:38 -0400 Subject: [PATCH 6/6] Use single-quotes around the expression in release.yml, to assign step output to $_data. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 395c997..8e9e785 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,7 +101,7 @@ jobs: echo "::set-output name=data::${_data}" - name: Call releases API to create draft release run: | - _data="${{ steps.get-data.outputs.data }}" + _data='${{ steps.get-data.outputs.data }}' echo "${_data}" | \ GITHUB_TOKEN=${{ secrets.PAT }} \ gh api -X POST /repos/:owner/:repo/releases --input -