Skip to content

Commit 4fa5100

Browse files
committed
ci: Do release tagging in CI and add --draft support
1 parent 2effea5 commit 4fa5100

File tree

6 files changed

+263
-60
lines changed

6 files changed

+263
-60
lines changed

.github/workflows/release.yaml

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
# GitHub release workflow.
2-
name: release
2+
name: New Release
33
on:
4-
push:
5-
tags:
6-
- "v*"
74
workflow_dispatch:
85
inputs:
6+
increment:
7+
description: Preferred version increment (release script may promote e.g. patch to minor depending on changes).
8+
type: choice
9+
required: true
10+
default: patch
11+
options:
12+
- patch
13+
- minor
14+
- major
15+
draft:
16+
description: Create a draft release (for manually editing release notes before publishing).
17+
type: boolean
18+
required: true
919
snapshot:
1020
description: Force a dev version to be generated, implies dry_run.
1121
type: boolean
@@ -100,6 +110,31 @@ jobs:
100110
AC_CERTIFICATE_PASSWORD: ${{ secrets.AC_CERTIFICATE_PASSWORD }}
101111
AC_APIKEY_P8_BASE64: ${{ secrets.AC_APIKEY_P8_BASE64 }}
102112

113+
- name: Create release tag and release notes
114+
run: |
115+
ref=HEAD
116+
old_version="$(git describe --abbrev=0 "$ref^1")"
117+
118+
# Cache commit metadata.
119+
. ./scripts/release/check_commit_metadata.sh "$old_version" "$ref"
120+
121+
# Create new release tag.
122+
version="$(
123+
./scripts/release/increment_version_tag.sh \
124+
${{ (github.event.inputs.dry_run || github.event.inputs.snapshot) && '--dry-run' }} \
125+
--ref "$ref" \
126+
--${{ github.event.inputs.increment }}
127+
)"
128+
129+
# Generate notes.
130+
release_notes="$(./scripts/release/generate_release_notes.sh --old-version "$old_version" --new-version "$version" --ref "$ref")"
131+
echo 'CODER_RELEASE_NOTES<<RN_EOF' >> $GITHUB_ENV
132+
echo "$release_notes" >> $GITHUB_ENV
133+
echo 'RN_EOF' >> $GITHUB_ENV
134+
135+
- name: Echo release notes
136+
run: echo "$CODER_RELEASE_NOTES"
137+
103138
- name: Build binaries
104139
run: |
105140
set -euo pipefail
@@ -158,7 +193,9 @@ jobs:
158193
- name: Publish release
159194
run: |
160195
./scripts/release/publish.sh \
196+
${{ github.event.inputs.draft && '--draft' }} \
161197
${{ (github.event.inputs.dry_run || github.event.inputs.snapshot) && '--dry-run' }} \
198+
--release-notes "$CODER_RELEASE_NOTES" \
162199
./build/*_installer.exe \
163200
./build/*.zip \
164201
./build/*.tar.gz \
@@ -195,6 +232,7 @@ jobs:
195232
with:
196233
name: release-artifacts
197234
path: |
235+
./build/*_installer.exe
198236
./build/*.zip
199237
./build/*.tar.gz
200238
./build/*.tgz

scripts/release.sh

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,61 @@
1212
# be tagged at, otherwise the latest commit will be used.
1313
#
1414
# Set --minor to force a minor version bump, even when there are no breaking
15-
# changes.
15+
# changes. Likewise for --major. By default a patch version will be created.
16+
#
17+
# Set --dry-run to run the release workflow in CI as a dry-run (no release will
18+
# be created).
1619
#
1720
# To mark a release as containing breaking changes, the commit title should
1821
# either contain a known prefix with an exclamation mark ("feat!:",
1922
# "feat(api)!:") or the PR that was merged can be tagged with the
2023
# "release/breaking" label.
2124
#
22-
# Usage: ./release.sh [--ref <ref>] [--minor]
25+
# To test changes to this script, you can set `--branch <my-branch>`, which will
26+
# run the release workflow in CI as a dry-run and use the latest commit on the
27+
# specified branch as the release commit. This will also set --dry-run.
28+
#
29+
# Usage: ./release.sh [--branch <name>] [--draft] [--dry-run] [--ref <ref>] [--major | --minor | --patch]
2330

2431
set -euo pipefail
2532
# shellcheck source=scripts/lib.sh
2633
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
2734
cdroot
2835

36+
branch=main
37+
draft=0
38+
dry_run=0
2939
ref=
30-
minor=0
40+
increment=
3141

32-
args="$(getopt -o n -l ref:,minor -- "$@")"
42+
args="$(getopt -o n -l branch:,draft,dry-run,ref:,major,minor,patch -- "$@")"
3343
eval set -- "$args"
3444
while true; do
3545
case "$1" in
46+
--branch)
47+
branch="$2"
48+
log "Using branch $branch, implies DRYRUN and CODER_IGNORE_MISSING_COMMIT_METADATA."
49+
dry_run=1
50+
export CODER_IGNORE_MISSING_COMMIT_METADATA=1
51+
shift 2
52+
;;
53+
--draft)
54+
draft=1
55+
shift
56+
;;
57+
--dry-run)
58+
dry_run=1
59+
shift
60+
;;
3661
--ref)
3762
ref="$2"
3863
shift 2
3964
;;
40-
--minor)
41-
minor=1
65+
--major | --minor | --patch)
66+
if [[ -n $increment ]]; then
67+
error "Cannot specify multiple version increments."
68+
fi
69+
increment=${1#--}
4270
shift
4371
;;
4472
--)
@@ -54,15 +82,20 @@ done
5482
# Check dependencies.
5583
dependencies gh sort
5684

85+
if [[ -z $increment ]]; then
86+
# Default to patch versions.
87+
increment="patch"
88+
fi
89+
5790
# Make sure the repository is up-to-date before generating release notes.
58-
log "Fetching main and tags from origin..."
59-
git fetch --quiet --tags origin main
91+
log "Fetching $branch and tags from origin..."
92+
git fetch --quiet --tags origin "$branch"
6093

6194
# Resolve to the latest ref on origin/main unless otherwise specified.
62-
ref=$(git rev-parse --short "${ref:-origin/main}")
95+
ref=$(git rev-parse --short "${ref:-origin/$branch}")
6396

6497
# Make sure that we're running the latest release script.
65-
if [[ -n $(git diff --name-status origin/main -- ./scripts/release.sh) ]]; then
98+
if [[ -n $(git diff --name-status origin/"$branch" -- ./scripts/release.sh) ]]; then
6699
error "Release script is out-of-date. Please check out the latest version and try again."
67100
fi
68101

@@ -71,28 +104,10 @@ fi
71104
mapfile -t versions < <(gh api -H "Accept: application/vnd.github+json" /repos/coder/coder/git/refs/tags -q '.[].ref | split("/") | .[2]' | grep '^v' | sort -r -V)
72105
old_version=${versions[0]}
73106

74-
log "Checking commit metadata for changes since $old_version..."
75107
# shellcheck source=scripts/release/check_commit_metadata.sh
76108
source "$SCRIPT_DIR/release/check_commit_metadata.sh" "$old_version" "$ref"
77109

78-
mapfile -d . -t version_parts <<<"$old_version"
79-
if [[ $minor == 1 ]] || [[ $COMMIT_METADATA_BREAKING == 1 ]]; then
80-
if [[ $COMMIT_METADATA_BREAKING == 1 ]]; then
81-
log "Breaking change detected, incrementing minor version..."
82-
else
83-
log "Forcing minor version bump..."
84-
fi
85-
version_parts[1]=$((version_parts[1] + 1))
86-
version_parts[2]=0
87-
else
88-
log "No breaking changes detected, incrementing patch version..."
89-
version_parts[2]=$((version_parts[2] + 1))
90-
fi
91-
new_version="${version_parts[0]}.${version_parts[1]}.${version_parts[2]}"
92-
93-
log "Old version: ${old_version}"
94-
log "New version: ${new_version}"
95-
110+
new_version="$(execrelative ./release/increment_version_tag.sh --dry-run --ref "$ref" --"$increment")"
96111
release_notes="$(execrelative ./release/generate_release_notes.sh --old-version "$old_version" --new-version "$new_version" --ref "$ref")"
97112

98113
echo
@@ -102,11 +117,31 @@ if [[ $show_reply =~ ^[Yy]$ ]]; then
102117
echo -e "$release_notes\n"
103118
fi
104119

105-
read -p "Create release? (y/n) " -n 1 -r create
120+
create_message="Create release"
121+
if ((draft)); then
122+
create_message="Create draft release"
123+
fi
124+
if ((dry_run)); then
125+
create_message+=" (DRYRUN)"
126+
fi
127+
read -p "$create_message? (y/n) " -n 1 -r create
106128
echo
107-
if [[ $create =~ ^[Yy]$ ]]; then
108-
log "Tagging commit $ref as $new_version..."
109-
git tag -a "$new_version" -m "$new_version" "$ref"
110-
log "Pushing tag to origin..."
111-
git push -u origin "$new_version"
129+
if ! [[ $create =~ ^[Yy]$ ]]; then
130+
exit 0
112131
fi
132+
133+
args=()
134+
if ((draft)); then
135+
args+=(-F draft=true)
136+
fi
137+
if ((dry_run)); then
138+
args+=(-F dry_run=true)
139+
fi
140+
141+
gh workflow run release.yaml \
142+
--ref "$branch" \
143+
-F increment="$increment" \
144+
-F snapshot=false \
145+
"${args[@]}"
146+
147+
log "Release process started, you can watch the release via: gh run watch --exit-status <run-id>"

scripts/release/check_commit_metadata.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ dependencies gh
3434
COMMIT_METADATA_BREAKING=0
3535
declare -A COMMIT_METADATA_TITLE COMMIT_METADATA_CATEGORY
3636

37+
# This environment variable can be set to 1 to ignore missing commit metadata,
38+
# useful for dry-runs.
39+
ignore_missing_metadata=${CODER_IGNORE_MISSING_COMMIT_METADATA:-0}
40+
3741
main() {
3842
# Match a commit prefix pattern, e.g. feat: or feat(site):.
3943
prefix_pattern="^([a-z]+)(\([a-z]*\))?:"
@@ -137,6 +141,9 @@ export_commit_metadata() {
137141
if [[ ${_COMMIT_METADATA_CACHE:-} == "${range}:"* ]]; then
138142
eval "${_COMMIT_METADATA_CACHE#*:}"
139143
else
144+
if [[ $ignore_missing_metadata == 1 ]]; then
145+
log "WARNING: Ignoring missing commit metadata, breaking changes may be missed."
146+
fi
140147
main
141148
fi
142149

scripts/release/generate_release_notes.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ if [[ -z $ref ]]; then
5959
fi
6060

6161
# shellcheck source=scripts/release/check_commit_metadata.sh
62-
source "$SCRIPT_DIR/release/check_commit_metadata.sh" "${old_version}" "${ref}"
62+
source "$SCRIPT_DIR/release/check_commit_metadata.sh" "$old_version" "$ref"
6363

6464
# Sort commits by title prefix, then by date, only return sha at the end.
65-
mapfile -t commits < <(git log --no-merges --pretty=format:"%ct %h %s" "${old_version}..${ref}" | sort -k3,3 -k1,1n | cut -d' ' -f2)
65+
mapfile -t commits < <(git log --no-merges --pretty=format:"%ct %h %s" "$old_version..$ref" | sort -k3,3 -k1,1n | cut -d' ' -f2)
6666

6767
# From: https://github.com/commitizen/conventional-commit-types
6868
# NOTE(mafredri): These need to be supported in check_commit_metadata.sh as well.
@@ -140,7 +140,7 @@ image_tag="$(execrelative ./image_tag.sh --version "$new_version")"
140140
echo -e "## Changelog
141141
$changelog
142142
143-
Compare: [\`${old_version}...${new_version}\`](https://github.com/coder/coder/compare/${old_version}...${new_version})
143+
Compare: [\`$old_version...$new_version\`](https://github.com/coder/coder/compare/$old_version...$new_version)
144144
145145
## Container image
146146
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env bash
2+
3+
# This script should be called to tag a new release. It will take the suggested
4+
# increment (major, minor, patch) and optionally promote e.g. patch -> minor if
5+
# there are breaking changes between the previous version and the given --ref
6+
# (or HEAD).
7+
#
8+
# Usage: ./increment_version_tag.sh [--dry-run] [--ref <ref>] <--major | --minor | --patch>
9+
#
10+
# This script will create a git tag, so it should only be run in CI (or via
11+
# --dry-run).
12+
13+
set -euo pipefail
14+
# shellcheck source=scripts/lib.sh
15+
source "$(dirname "$(dirname "${BASH_SOURCE[0]}")")/lib.sh"
16+
cdroot
17+
18+
dry_run=0
19+
ref=HEAD
20+
increment=
21+
22+
args="$(getopt -o n -l dry-run,ref:,major,minor,patch -- "$@")"
23+
eval set -- "$args"
24+
while true; do
25+
case "$1" in
26+
--dry-run)
27+
dry_run=1
28+
shift
29+
;;
30+
--ref)
31+
ref="$2"
32+
shift 2
33+
;;
34+
--major | --minor | --patch)
35+
if [[ -n $increment ]]; then
36+
error "Cannot specify multiple version increments."
37+
fi
38+
increment=${1#--}
39+
shift
40+
;;
41+
--)
42+
shift
43+
break
44+
;;
45+
*)
46+
error "Unrecognized option: $1"
47+
;;
48+
esac
49+
done
50+
51+
# Check dependencies.
52+
dependencies git
53+
54+
if [[ -z $increment ]]; then
55+
error "No version increment provided."
56+
fi
57+
58+
if [[ $dry_run != 1 ]] && [[ ${CI:-} == "" ]]; then
59+
error "This script must be run in CI or with --dry-run."
60+
fi
61+
62+
old_version="$(git describe --abbrev=0 "$ref^1")"
63+
cur_tag="$(git describe --abbrev=0 "$ref")"
64+
if [[ $old_version != "$cur_tag" ]]; then
65+
message="Ref \"$ref\" is already tagged with a release ($cur_tag)"
66+
if ! ((dry_run)); then
67+
error "$message."
68+
fi
69+
log "DRYRUN: $message, echoing current tag."
70+
echo "$cur_tag"
71+
exit 0
72+
fi
73+
ref=$(git rev-parse --short "$ref")
74+
75+
log "Checking commit metadata for changes since $old_version..."
76+
# shellcheck source=scripts/release/check_commit_metadata.sh
77+
source "$SCRIPT_DIR/release/check_commit_metadata.sh" "$old_version" "$ref"
78+
79+
if ((COMMIT_METADATA_BREAKING == 1)); then
80+
prev_increment=$increment
81+
if [[ $increment == patch ]]; then
82+
increment=minor
83+
fi
84+
if [[ $prev_increment != "$increment" ]]; then
85+
log "Breaking change detected, changing version increment from \"$prev_increment\" to \"$increment\"."
86+
else
87+
log "Breaking change detected, provided increment is sufficient, using \"$increment\" increment."
88+
fi
89+
else
90+
log "No breaking changes detected, using \"$increment\" increment."
91+
fi
92+
93+
mapfile -d . -t version_parts <<<"${old_version#v}"
94+
case "$increment" in
95+
patch)
96+
version_parts[2]=$((version_parts[2] + 1))
97+
;;
98+
minor)
99+
version_parts[1]=$((version_parts[1] + 1))
100+
version_parts[2]=0
101+
;;
102+
major)
103+
version_parts[0]=$((version_parts[0] + 1))
104+
version_parts[1]=0
105+
version_parts[2]=0
106+
;;
107+
*)
108+
error "Unrecognized version increment."
109+
;;
110+
esac
111+
112+
new_version="v${version_parts[0]}.${version_parts[1]}.${version_parts[2]}"
113+
114+
log "Old version: $old_version"
115+
log "New version: $new_version"
116+
maybedryrun "$dry_run" git tag -a "$new_version" -m "Release $new_version" "$ref"
117+
maybedryrun "$dry_run" git push --quiet origin "$new_version"
118+
119+
echo "$new_version"

0 commit comments

Comments
 (0)