Add release trigger (#1501)

* add release trigger

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* deduplicate version and changelog calls + add gh checks

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add more chronicle verbosity, but not when triggering releases

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* bump chronicle version to get --version-file feature

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update bootstrap tool workflow to include glow

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* add version prefix check on tags in release quality gate

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

---------

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2023-02-08 11:38:27 -05:00 committed by GitHub
parent 48528efff3
commit 8847ba5d0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 49 deletions

50
.github/scripts/trigger-release.sh vendored Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -eu
bold=$(tput bold)
normal=$(tput sgr0)
if ! [ -x "$(command -v gh)" ]; then
echo "The GitHub CLI could not be found. To continue follow the instructions at https://github.com/cli/cli#installation"
exit 1
fi
gh auth status
# we need all of the git state to determine the next version. Since tagging is done by
# the release pipeline it is possible to not have all of the tags from previous releases.
git fetch --tags
# populates the CHANGELOG.md and VERSION files
echo "${bold}Generating changelog...${normal}"
make changelog 2> /dev/null
NEXT_VERSION=$(cat VERSION)
if [[ "$NEXT_VERSION" == "" || "${NEXT_VERSION}" == "(Unreleased)" ]]; then
echo "Could not determine the next version to release. Exiting..."
exit 1
fi
while true; do
read -p "${bold}Do you want to trigger a release for version '${NEXT_VERSION}'?${normal} [y/n] " yn
case $yn in
[Yy]* ) echo; break;;
[Nn]* ) echo; echo "Cancelling release..."; exit;;
* ) echo "Please answer yes or no.";;
esac
done
echo "${bold}Kicking off release for ${NEXT_VERSION}${normal}..."
echo
gh workflow run release.yaml -f version=${NEXT_VERSION}
echo
echo "${bold}Waiting for release to start...${normal}"
sleep 10
set +e
echo "${bold}Head to the release workflow to monitor the release:${normal} $(gh run list --workflow=release.yaml --limit=1 --json url --jq '.[].url')"
id=$(gh run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[].databaseId')
gh run watch $id --exit-status || (echo ; echo "${bold}Logs of failed step:${normal}" && GH_PAGER="" gh run view $id --log-failed)

View File

@ -1,12 +1,10 @@
name: "Release" name: "Release"
on: on:
push: workflow_dispatch:
# take no actions on push to any branch... inputs:
branches-ignore: version:
- "**" description: tag the latest commit on main with the given version (prefixed with v)
# ... only act on release tags required: true
tags:
- "v*"
env: env:
GO_VERSION: "1.19.x" GO_VERSION: "1.19.x"
@ -18,12 +16,11 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
# we don't want to release commits that have been pushed and tagged, but not necessarily merged onto main - name: Check if tag already exists
- name: Ensure tagged commit is on main # note: this will fail if the tag already exists
run: | run: |
echo "Tag: ${GITHUB_REF##*/}" [[ "${{ github.event.inputs.version }}" == v* ]] || (echo "version '${{ github.event.inputs.version }}' does not have a 'v' prefix" && exit 1)
git fetch origin main git tag ${{ github.event.inputs.version }}
git merge-base --is-ancestor ${GITHUB_REF##*/} origin/main && echo "${GITHUB_REF##*/} is a commit on main!"
- name: Check static analysis results - name: Check static analysis results
uses: fountainhead/action-wait-for-check@v1.1.0 uses: fountainhead/action-wait-for-check@v1.1.0
@ -120,6 +117,13 @@ jobs:
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Tag release
run: |
git tag ${{ github.event.inputs.version }}
git push origin --tags
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build & publish release artifacts - name: Build & publish release artifacts
run: make release run: make release
env: env:

View File

@ -29,6 +29,7 @@ jobs:
GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version') GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version')
YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version') YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version')
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign@latest 2>/dev/null | jq -r '.Version') COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign@latest 2>/dev/null | jq -r '.Version')
GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version')
# update version variables in the Makefile # update version variables in the Makefile
sed -r -i -e 's/^(GOLANGCILINT_VERSION := ).*/\1'${GOLANGCILINT_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(GOLANGCILINT_VERSION := ).*/\1'${GOLANGCILINT_LATEST_VERSION}'/' Makefile
@ -38,6 +39,7 @@ jobs:
sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile
sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile
sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile
sed -r -i -e 's/^(GLOW_VERSION := ).*/\1'${GLOW_LATEST_VERSION}'/' Makefile
# export the versions for use with create-pull-request # export the versions for use with create-pull-request
echo "GOLANGCILINT=$GOLANGCILINT_LATEST_VERSION" >> $GITHUB_OUTPUT echo "GOLANGCILINT=$GOLANGCILINT_LATEST_VERSION" >> $GITHUB_OUTPUT
@ -47,6 +49,7 @@ jobs:
echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT
id: latest-versions id: latest-versions
- uses: tibdex/github-app-token@v1 - uses: tibdex/github-app-token@v1
@ -71,5 +74,6 @@ jobs:
- [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }}) - [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }})
- [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }}) - [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }})
- [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }}) - [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }})
- [glow ${{ steps.latest-versions.outputs.GLOW }}](https://github.com/charmbracelet/glow/releases/tag/${{ steps.latest-versions.outputs.GLOW }})
This is an auto-generated pull request to update all of the bootstrap tools to the latest versions. This is an auto-generated pull request to update all of the bootstrap tools to the latest versions.
token: ${{ steps.generate-token.outputs.token }} token: ${{ steps.generate-token.outputs.token }}

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
CHANGELOG.md CHANGELOG.md
VERSION
/test/results /test/results
/dist /dist
/snapshot /snapshot

View File

@ -6,16 +6,19 @@ LINT_CMD := $(TEMP_DIR)/golangci-lint run --tests=false
GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist
SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot
CHRONICLE_CMD = $(TEMP_DIR)/chronicle
GLOW_CMD = $(TEMP_DIR)/glow
# Tool versions ################################# # Tool versions #################################
GOLANGCILINT_VERSION := v1.51.1 GOLANGCILINT_VERSION := v1.51.1
GOSIMPORTS_VERSION := v0.3.5 GOSIMPORTS_VERSION := v0.3.5
BOUNCER_VERSION := v0.4.0 BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.5.1 CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.15.1 GORELEASER_VERSION := v1.15.1
YAJSV_VERSION := v1.4.1 YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v1.13.1 COSIGN_VERSION := v1.13.1
QUILL_VERSION := v0.2.0 QUILL_VERSION := v0.2.0
GLOW_VERSION := v1.4.1
# Formatting variables ################################# # Formatting variables #################################
BOLD := $(shell tput -T linux bold) BOLD := $(shell tput -T linux bold)
@ -88,6 +91,7 @@ bootstrap-tools: $(TEMP_DIR)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION) GOBIN="$(realpath $(TEMP_DIR))" go install github.com/rinchsan/gosimports/cmd/gosimports@$(GOSIMPORTS_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/neilpa/yajsv@$(YAJSV_VERSION) GOBIN="$(realpath $(TEMP_DIR))" go install github.com/neilpa/yajsv@$(YAJSV_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/sigstore/cosign/cmd/cosign@$(COSIGN_VERSION) GOBIN="$(realpath $(TEMP_DIR))" go install github.com/sigstore/cosign/cmd/cosign@$(COSIGN_VERSION)
GOBIN="$(realpath $(TEMP_DIR))" go install github.com/charmbracelet/glow@$(GLOW_VERSION)
.PHONY: bootstrap-go .PHONY: bootstrap-go
bootstrap-go: bootstrap-go:
@ -304,15 +308,16 @@ $(SNAPSHOT_DIR): ## Build snapshot release binaries and packages
$(SNAPSHOT_CMD) --config $(TEMP_DIR)/goreleaser.yaml $(SNAPSHOT_CMD) --config $(TEMP_DIR)/goreleaser.yaml
.PHONY: changelog .PHONY: changelog
changelog: clean-changelog $(CHANGELOG) ## Generate and show the changelog for the current unreleased version changelog: clean-changelog ## Generate and show the changelog for the current unreleased version
@docker run -it --rm \ $(CHRONICLE_CMD) -vvv -n --version-file VERSION > $(CHANGELOG)
-v $(shell pwd)/$(CHANGELOG):/$(CHANGELOG) \ @$(GLOW_CMD) $(CHANGELOG)
rawkode/mdv \
-t 748.5989 \
/$(CHANGELOG)
$(CHANGELOG): $(CHANGELOG):
$(TEMP_DIR)/chronicle -vv > $(CHANGELOG) $(CHRONICLE_CMD) -vvv > $(CHANGELOG)
.PHONY: trigger-release
trigger-release:
@.github/scripts/trigger-release.sh
.PHONY: release .PHONY: release
release: clean-dist $(CHANGELOG) release: clean-dist $(CHANGELOG)
@ -350,7 +355,7 @@ clean-dist: clean-changelog
.PHONY: clean-changelog .PHONY: clean-changelog
clean-changelog: clean-changelog:
rm -f $(CHANGELOG) rm -f $(CHANGELOG) VERSION
clean-test-image-cache: clean-test-image-tar-cache clean-test-image-docker-cache ## Clean test image cache clean-test-image-cache: clean-test-image-tar-cache clean-test-image-docker-cache ## Clean test image cache

View File

@ -4,8 +4,7 @@ A good release process has the following qualities:
1. There is a way to plan what should be in a release 1. There is a way to plan what should be in a release
1. There is a way to see what is actually in a release 1. There is a way to see what is actually in a release
1. Allow for different kinds of releases (major breaking vs backwards compatible 1. Allow for different kinds of releases (major breaking vs backwards compatible enhancements vs patch updates)
enhancements vs patch updates)
1. Specify a repeatable way to build and publish software artifacts 1. Specify a repeatable way to build and publish software artifacts
## Planning a release ## Planning a release
@ -21,10 +20,7 @@ completed, would allow the release to be considered complete. A Milestone is onl
Not all releases need to be planned. For instance, patch releases for fixes should be Not all releases need to be planned. For instance, patch releases for fixes should be
released when they are ready and when releasing would not interfere with another current released when they are ready and when releasing would not interfere with another current
release (where some partial or breaking features have already been merged). Beta releases release (where some partial or breaking features have already been merged).
and release candidates should not be independently planned from the non-beta release. That
is, the features for a `v0.1-beta.1` release should be planned under the `v0.1` Milestone,
not under a separate `v0.1-beta.1` Milestone.
Unless necessary, feature releases should be small and frequent, which may obviate the Unless necessary, feature releases should be small and frequent, which may obviate the
need for regular release planning under a Milestone. need for regular release planning under a Milestone.
@ -42,9 +38,9 @@ Changelog line. Furthermore, there should be a place to see all released version
release date for each release, the semantic version of the release, and the set of changes release date for each release, the semantic version of the release, and the set of changes
for each release. for each release.
This project auto-generates the Changelog contents for each current release and posts the **This project auto-generates the Changelog contents for each current release and posts the
generated contents to the GitHub Release page. Leveraging the GitHub Releases feature generated contents to the GitHub Release page**. Leveraging the GitHub Releases feature
allows GitHub to manage the Changelog on each release outside of the git repository while allows GitHub to manage the Changelog on each release outside of the git source tree while
still being hosted with the released assets. still being hosted with the released assets.
The Changelog is generated from the metadata from in-repository issues and PRs, using The Changelog is generated from the metadata from in-repository issues and PRs, using
@ -60,8 +56,8 @@ The above suggestions imply that we should:
- The appropriate label is applied to PRs and/or issues to drive specific change type - The appropriate label is applied to PRs and/or issues to drive specific change type
sections (deprecated, breaking, security, bug, etc) sections (deprecated, breaking, security, bug, etc)
With this approach as we cultivate good organization of PRs and issues we automatically **With this approach as we cultivate good organization of PRs and issues we automatically
get an equally good Changelog. get an equally good Changelog.**
## Major, minor, and patch releases ## Major, minor, and patch releases
@ -69,8 +65,8 @@ The latest version of the tool is the only supported version, which implies that
parallel release branches will not be a regular process (if ever). Multiple releases can parallel release branches will not be a regular process (if ever). Multiple releases can
be planned in parallel, however, only one can be actively developed at a time. That is, if be planned in parallel, however, only one can be actively developed at a time. That is, if
PRs attached to a release Milestone have been merged into the main branch, that release is PRs attached to a release Milestone have been merged into the main branch, that release is
now the "next" release. This implies that the source of truth for release lies with the now the "next" release. **This implies that the source of truth for release lies with the
git log and Changelog, not with the release Milestones (which are purely for planning and git log and Changelog, not with the release Milestones** (which are purely for planning and
tracking). tracking).
Semantic versioning should be used to indicate breaking changes, new features, and fixes. Semantic versioning should be used to indicate breaking changes, new features, and fixes.
@ -81,24 +77,28 @@ instead the minor version indicates both new features and breaking changes.
Ideally releasing should be done often with small increments when possible. Unless a Ideally releasing should be done often with small increments when possible. Unless a
breaking change is blocking the release, or no fixes/features have been merged, a good breaking change is blocking the release, or no fixes/features have been merged, a good
target release cadence is between every 2 or 4 weeks. target release cadence is between every 1 or 2 weeks.
This release process itself should be as automated as possible, and have only a few steps: This release process itself should be as automated as possible, and has only a few steps:
1. Tag the main branch with a full semantic-version, prefixed with a `v`. If there is a 1. **Trigger a new release with `make trigger-release`**. At this point you'll see a preview
milestone with a partial version, the full version should be used for the git tag (e.g. changelog in the terminal. If you're happy with the changelog, press `y` to continue, otherwise
with a Milestone of `v0.1` the tag should be `v0.1.0`). You can determine the changes going you can abort and adjust the labels on the PRs and issues to be included in the release and
into a release by running `make changelog-unreleased`. Use this change list to determine the re-run the release trigger command.
release increment. After determining the release increment (major, minor, patch), create the tag.
Given the above example the command to create the tag would be `git tag v0.1.0`.
1. Push the tag. Given the above example the command to push the tag would be `git push origin v0.1.0`.
1. A release admin must approve the release on the GitHub Actions release pipeline run page. 1. A release admin must approve the release on the GitHub Actions release pipeline run page.
Once approved, the release pipeline will generate all assets and draft a GitHub Release. Once approved, the release pipeline will generate all assets and publish a GitHub Release.
1. Navigate to the GitHub Release draft page to review the final changelog and publish the
release. Once published, a release-follow-up pipeline will publish derivative artifacts
(docker image to DockerHub, brew formula to the external homebrew git repo, etc).
1. If there is a release Milestone, close it. 1. If there is a release Milestone, close it.
## Retracting a release
If a release is found to be problematic, it can be retracted with the following steps:
- Deleting the GitHub Release
- Untag the docker images in the `ghcr.io` and `docker.io` registries
- Revert the brew formula in [`anchore/homebrew-syft`](https://github.com/anchore/homebrew-syft) to point to the previous release
**Note**: do not delete release tags from the git repository since there may already be references to the release
in the go proxy, which will cause confusion when trying to reuse the tag later (the H1 hash will not match and there
will be a warning when users try to pull the new release).