Skip to content

Commit

Permalink
Add release trigger (#1501)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
wagoodman authored Feb 8, 2023
1 parent 48528ef commit 8847ba5
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 49 deletions.
50 changes: 50 additions & 0 deletions .github/scripts/trigger-release.sh
Original file line number Diff line number Diff line change
@@ -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)
28 changes: 16 additions & 12 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
name: "Release"
on:
push:
# take no actions on push to any branch...
branches-ignore:
- "**"
# ... only act on release tags
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: tag the latest commit on main with the given version (prefixed with v)
required: true

env:
GO_VERSION: "1.19.x"
Expand All @@ -18,12 +16,11 @@ jobs:
steps:
- uses: actions/checkout@v3

# we don't want to release commits that have been pushed and tagged, but not necessarily merged onto main
- name: Ensure tagged commit is on main
- name: Check if tag already exists
# note: this will fail if the tag already exists
run: |
echo "Tag: ${GITHUB_REF##*/}"
git fetch origin main
git merge-base --is-ancestor ${GITHUB_REF##*/} origin/main && echo "${GITHUB_REF##*/} is a commit on main!"
[[ "${{ github.event.inputs.version }}" == v* ]] || (echo "version '${{ github.event.inputs.version }}' does not have a 'v' prefix" && exit 1)
git tag ${{ github.event.inputs.version }}
- name: Check static analysis results
uses: fountainhead/action-wait-for-check@v1.1.0
Expand Down Expand Up @@ -120,6 +117,13 @@ jobs:
username: ${{ github.actor }}
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
run: make release
env:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/update-bootstrap-tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
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')
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
sed -r -i -e 's/^(GOLANGCILINT_VERSION := ).*/\1'${GOLANGCILINT_LATEST_VERSION}'/' Makefile
Expand All @@ -38,6 +39,7 @@ jobs:
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/^(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
echo "GOLANGCILINT=$GOLANGCILINT_LATEST_VERSION" >> $GITHUB_OUTPUT
Expand All @@ -47,6 +49,7 @@ jobs:
echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT
echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT
id: latest-versions
- uses: tibdex/github-app-token@v1
Expand All @@ -71,5 +74,6 @@ jobs:
- [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 }})
- [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.
token: ${{ steps.generate-token.outputs.token }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
CHANGELOG.md
VERSION
/test/results
/dist
/snapshot
Expand Down
23 changes: 14 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ LINT_CMD := $(TEMP_DIR)/golangci-lint run --tests=false
GOIMPORTS_CMD := $(TEMP_DIR)/gosimports -local github.com/anchore
RELEASE_CMD := $(TEMP_DIR)/goreleaser release --rm-dist
SNAPSHOT_CMD := $(RELEASE_CMD) --skip-publish --skip-sign --snapshot
CHRONICLE_CMD = $(TEMP_DIR)/chronicle
GLOW_CMD = $(TEMP_DIR)/glow

# Tool versions #################################
GOLANGCILINT_VERSION := v1.51.1
GOSIMPORTS_VERSION := v0.3.5
BOUNCER_VERSION := v0.4.0
CHRONICLE_VERSION := v0.5.1
CHRONICLE_VERSION := v0.6.0
GORELEASER_VERSION := v1.15.1
YAJSV_VERSION := v1.4.1
COSIGN_VERSION := v1.13.1
QUILL_VERSION := v0.2.0
GLOW_VERSION := v1.4.1

# Formatting variables #################################
BOLD := $(shell tput -T linux bold)
Expand Down Expand Up @@ -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/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/charmbracelet/glow@$(GLOW_VERSION)

.PHONY: bootstrap-go
bootstrap-go:
Expand Down Expand Up @@ -304,15 +308,16 @@ $(SNAPSHOT_DIR): ## Build snapshot release binaries and packages
$(SNAPSHOT_CMD) --config $(TEMP_DIR)/goreleaser.yaml

.PHONY: changelog
changelog: clean-changelog $(CHANGELOG) ## Generate and show the changelog for the current unreleased version
@docker run -it --rm \
-v $(shell pwd)/$(CHANGELOG):/$(CHANGELOG) \
rawkode/mdv \
-t 748.5989 \
/$(CHANGELOG)
changelog: clean-changelog ## Generate and show the changelog for the current unreleased version
$(CHRONICLE_CMD) -vvv -n --version-file VERSION > $(CHANGELOG)
@$(GLOW_CMD) $(CHANGELOG)

$(CHANGELOG):
$(TEMP_DIR)/chronicle -vv > $(CHANGELOG)
$(CHRONICLE_CMD) -vvv > $(CHANGELOG)

.PHONY: trigger-release
trigger-release:
@.github/scripts/trigger-release.sh

.PHONY: release
release: clean-dist $(CHANGELOG)
Expand Down Expand Up @@ -350,7 +355,7 @@ clean-dist: clean-changelog

.PHONY: 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

Expand Down
56 changes: 28 additions & 28 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 see what is actually in a release
1. Allow for different kinds of releases (major breaking vs backwards compatible
enhancements vs patch updates)
1. Allow for different kinds of releases (major breaking vs backwards compatible enhancements vs patch updates)
1. Specify a repeatable way to build and publish software artifacts

## Planning a release
Expand All @@ -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
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
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.
release (where some partial or breaking features have already been merged).

Unless necessary, feature releases should be small and frequent, which may obviate the
need for regular release planning under a Milestone.
Expand All @@ -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
for each release.

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
allows GitHub to manage the Changelog on each release outside of the git repository while
**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
allows GitHub to manage the Changelog on each release outside of the git source tree while
still being hosted with the released assets.

The Changelog is generated from the metadata from in-repository issues and PRs, using
Expand All @@ -60,17 +56,17 @@ The above suggestions imply that we should:
- The appropriate label is applied to PRs and/or issues to drive specific change type
sections (deprecated, breaking, security, bug, etc)

With this approach as we cultivate good organization of PRs and issues we automatically
get an equally good Changelog.
**With this approach as we cultivate good organization of PRs and issues we automatically
get an equally good Changelog.**

## Major, minor, and patch releases

The latest version of the tool is the only supported version, which implies that multiple
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
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
git log and Changelog, not with the release Milestones (which are purely for planning and
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
tracking).

Semantic versioning should be used to indicate breaking changes, new features, and fixes.
Expand All @@ -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
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
milestone with a partial version, the full version should be used for the git tag (e.g.
with a Milestone of `v0.1` the tag should be `v0.1.0`). You can determine the changes going
into a release by running `make changelog-unreleased`. Use this change list to determine the
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. **Trigger a new release with `make trigger-release`**. At this point you'll see a preview
changelog in the terminal. If you're happy with the changelog, press `y` to continue, otherwise
you can abort and adjust the labels on the PRs and issues to be included in the release and
re-run the release trigger command.

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.
Once approved, the release pipeline will generate all assets and draft 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).
Once approved, the release pipeline will generate all assets and publish a GitHub Release.

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).

0 comments on commit 8847ba5

Please sign in to comment.