|
1 | 1 | #!/usr/bin/env bash
|
2 | 2 |
|
3 |
| -# This script should be called to create a new release. |
4 |
| -# |
5 |
| -# When run, this script will display the new version number and optionally a |
6 |
| -# preview of the release notes. The new version will be selected automatically |
7 |
| -# based on if the release contains breaking changes or not. If the release |
8 |
| -# contains breaking changes, a new minor version will be created. Otherwise, a |
9 |
| -# new patch version will be created. |
10 |
| -# |
11 |
| -# Set --ref if you need to specify a specific commit that the new version will |
12 |
| -# be tagged at, otherwise the latest commit will be used. |
13 |
| -# |
14 |
| -# Set --minor to force a minor version bump, even when there are no breaking |
15 |
| -# changes. |
16 |
| -# |
17 |
| -# To mark a release as containing breaking changes, the commit title should |
18 |
| -# either contain a known prefix with an exclamation mark ("feat!:", |
19 |
| -# "feat(api)!:") or the PR that was merged can be tagged with the |
20 |
| -# "release/breaking" label. |
21 |
| -# |
22 |
| -# Usage: ./release.sh [--ref <ref>] [--minor] |
23 |
| - |
24 | 3 | set -euo pipefail
|
25 | 4 | # shellcheck source=scripts/lib.sh
|
26 | 5 | source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
27 | 6 | cdroot
|
28 | 7 |
|
| 8 | +usage() { |
| 9 | + cat <<EOH |
| 10 | +Usage: ./release.sh [--branch <name>] [--draft] [--dry-run] [--ref <ref>] [--major | --minor | --patch] |
| 11 | +
|
| 12 | +This script should be called to create a new release. |
| 13 | +
|
| 14 | +When run, this script will display the new version number and optionally a |
| 15 | +preview of the release notes. The new version will be selected automatically |
| 16 | +based on if the release contains breaking changes or not. If the release |
| 17 | +contains breaking changes, a new minor version will be created. Otherwise, a |
| 18 | +new patch version will be created. |
| 19 | +
|
| 20 | +Set --ref if you need to specify a specific commit that the new version will |
| 21 | +be tagged at, otherwise the latest commit will be used. |
| 22 | +
|
| 23 | +Set --minor to force a minor version bump, even when there are no breaking |
| 24 | +changes. Likewise for --major. By default a patch version will be created. |
| 25 | +
|
| 26 | +Set --dry-run to run the release workflow in CI as a dry-run (no release will |
| 27 | +be created). |
| 28 | +
|
| 29 | +To mark a release as containing breaking changes, the commit title should |
| 30 | +either contain a known prefix with an exclamation mark ("feat!:", |
| 31 | +"feat(api)!:") or the PR that was merged can be tagged with the |
| 32 | +"release/breaking" label. |
| 33 | +
|
| 34 | +To test changes to this script, you can set --branch <my-branch>, which will |
| 35 | +run the release workflow in CI as a dry-run and use the latest commit on the |
| 36 | +specified branch as the release commit. This will also set --dry-run. |
| 37 | +EOH |
| 38 | +} |
| 39 | + |
| 40 | +branch=main |
| 41 | +draft=0 |
| 42 | +dry_run=0 |
29 | 43 | ref=
|
30 |
| -minor=0 |
| 44 | +increment= |
31 | 45 |
|
32 |
| -args="$(getopt -o n -l ref:,minor -- "$@")" |
| 46 | +args="$(getopt -o h -l branch:,draft,dry-run,help,ref:,major,minor,patch -- "$@")" |
33 | 47 | eval set -- "$args"
|
34 | 48 | while true; do
|
35 | 49 | case "$1" in
|
| 50 | + --branch) |
| 51 | + branch="$2" |
| 52 | + log "Using branch $branch, implies DRYRUN and CODER_IGNORE_MISSING_COMMIT_METADATA." |
| 53 | + dry_run=1 |
| 54 | + export CODER_IGNORE_MISSING_COMMIT_METADATA=1 |
| 55 | + shift 2 |
| 56 | + ;; |
| 57 | + --draft) |
| 58 | + draft=1 |
| 59 | + shift |
| 60 | + ;; |
| 61 | + --dry-run) |
| 62 | + dry_run=1 |
| 63 | + shift |
| 64 | + ;; |
| 65 | + -h | --help) |
| 66 | + usage |
| 67 | + exit 0 |
| 68 | + ;; |
36 | 69 | --ref)
|
37 | 70 | ref="$2"
|
38 | 71 | shift 2
|
39 | 72 | ;;
|
40 |
| - --minor) |
41 |
| - minor=1 |
| 73 | + --major | --minor | --patch) |
| 74 | + if [[ -n $increment ]]; then |
| 75 | + error "Cannot specify multiple version increments." |
| 76 | + fi |
| 77 | + increment=${1#--} |
42 | 78 | shift
|
43 | 79 | ;;
|
44 | 80 | --)
|
|
54 | 90 | # Check dependencies.
|
55 | 91 | dependencies gh sort
|
56 | 92 |
|
| 93 | +if [[ -z $increment ]]; then |
| 94 | + # Default to patch versions. |
| 95 | + increment="patch" |
| 96 | +fi |
| 97 | + |
57 | 98 | # 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 |
| 99 | +log "Fetching $branch and tags from origin..." |
| 100 | +git fetch --quiet --tags origin "$branch" |
60 | 101 |
|
61 | 102 | # Resolve to the latest ref on origin/main unless otherwise specified.
|
62 |
| -ref=$(git rev-parse --short "${ref:-origin/main}") |
| 103 | +ref=$(git rev-parse --short "${ref:-origin/$branch}") |
63 | 104 |
|
64 | 105 | # Make sure that we're running the latest release script.
|
65 |
| -if [[ -n $(git diff --name-status origin/main -- ./scripts/release.sh) ]]; then |
| 106 | +if [[ -n $(git diff --name-status origin/"$branch" -- ./scripts/release.sh) ]]; then |
66 | 107 | error "Release script is out-of-date. Please check out the latest version and try again."
|
67 | 108 | fi
|
68 | 109 |
|
|
71 | 112 | 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)
|
72 | 113 | old_version=${versions[0]}
|
73 | 114 |
|
74 |
| -log "Checking commit metadata for changes since $old_version..." |
75 | 115 | # shellcheck source=scripts/release/check_commit_metadata.sh
|
76 | 116 | source "$SCRIPT_DIR/release/check_commit_metadata.sh" "$old_version" "$ref"
|
77 | 117 |
|
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 |
| - |
| 118 | +new_version="$(execrelative ./release/tag_version.sh --dry-run --ref "$ref" --"$increment")" |
96 | 119 | release_notes="$(execrelative ./release/generate_release_notes.sh --old-version "$old_version" --new-version "$new_version" --ref "$ref")"
|
97 | 120 |
|
98 |
| -echo |
| 121 | +log |
99 | 122 | read -p "Preview release notes? (y/n) " -n 1 -r show_reply
|
100 |
| -echo |
| 123 | +log |
101 | 124 | if [[ $show_reply =~ ^[Yy]$ ]]; then
|
102 | 125 | echo -e "$release_notes\n"
|
103 | 126 | fi
|
104 | 127 |
|
105 |
| -read -p "Create release? (y/n) " -n 1 -r create |
106 |
| -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" |
| 128 | +create_message="Create release" |
| 129 | +if ((draft)); then |
| 130 | + create_message="Create draft release" |
| 131 | +fi |
| 132 | +if ((dry_run)); then |
| 133 | + create_message+=" (DRYRUN)" |
| 134 | +fi |
| 135 | +read -p "$create_message? (y/n) " -n 1 -r create |
| 136 | +log |
| 137 | +if ! [[ $create =~ ^[Yy]$ ]]; then |
| 138 | + exit 0 |
| 139 | +fi |
| 140 | + |
| 141 | +args=() |
| 142 | +if ((draft)); then |
| 143 | + args+=(-F draft=true) |
| 144 | +fi |
| 145 | +if ((dry_run)); then |
| 146 | + args+=(-F dry_run=true) |
112 | 147 | fi
|
| 148 | + |
| 149 | +log |
| 150 | +gh workflow run release.yaml \ |
| 151 | + --ref "$branch" \ |
| 152 | + -F increment="$increment" \ |
| 153 | + -F snapshot=false \ |
| 154 | + "${args[@]}" |
| 155 | +log |
| 156 | + |
| 157 | +read -p "Watch release? (y/n) " -n 1 -r watch |
| 158 | +log |
| 159 | +if ! [[ $watch =~ ^[Yy]$ ]]; then |
| 160 | + exit 0 |
| 161 | +fi |
| 162 | + |
| 163 | +log 'Waiting for job to become "in_progress"...' |
| 164 | + |
| 165 | +# Wait at most 3 minutes (3*60)/3 = 60 for the job to start. |
| 166 | +for _ in $(seq 1 60); do |
| 167 | + mapfile -t run < <( |
| 168 | + # Output: |
| 169 | + # 3886828508 |
| 170 | + # in_progress |
| 171 | + gh run list -w release.yaml \ |
| 172 | + --limit 1 \ |
| 173 | + --json status,databaseId \ |
| 174 | + --jq '.[] | (.databaseId | tostring), .status' |
| 175 | + ) |
| 176 | + if [[ ${run[1]} != "in_progress" ]]; then |
| 177 | + sleep 3 |
| 178 | + continue |
| 179 | + fi |
| 180 | + gh run watch --exit-status "${run[0]}" |
| 181 | + exit 0 |
| 182 | +done |
| 183 | + |
| 184 | +error "Waiting for job to start timed out." |
0 commit comments