diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f6ddf887c0240..9866da9942cce 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,32 +1,16 @@ # GitHub release workflow. name: Release -run-name: Release ${{ github.ref_name }}${{ inputs.dry_run && ' (DRYRUN)' || '' }} on: + push: + tags: + - "v*" workflow_dispatch: inputs: - increment: - description: Preferred version increment (release script may promote e.g. patch to minor depending on changes). - type: choice - required: true - default: patch - options: - - patch - - minor - - major - draft: - description: Create a draft release (for manually editing release notes before publishing). - type: boolean - required: true - default: false dry_run: - description: Perform a dry-run release. + description: Perform a dry-run release (devel). Note that ref must be an annotated tag when run without dry-run. type: boolean required: true default: false - ignore_missing_commit_metadata: - description: WARNING! This option disables the requirement that all commits have a PR. Not needed for dry_run. - type: boolean - default: false permissions: # Required to publish a release @@ -43,29 +27,19 @@ env: # booleans, not strings. # https://github.blog/changelog/2022-06-10-github-actions-inputs-unified-across-manual-and-reusable-workflows/ CODER_RELEASE: ${{ !inputs.dry_run }} - CODER_RELEASE_INCREMENT: ${{ inputs.increment }} - CODER_RELEASE_DRAFT: ${{ inputs.draft }} CODER_DRY_RUN: ${{ inputs.dry_run }} jobs: release: - name: Create and publish + name: Build and publish runs-on: ${{ github.repository_owner == 'coder' && 'ubuntu-latest-16-cores' || 'ubuntu-latest' }} env: # Necessary for Docker manifest DOCKER_CLI_EXPERIMENTAL: "enabled" steps: - - name: Check release on main (or dry-run) - if: ${{ github.ref_name != 'main' && !inputs.dry_run }} - run: | - echo "Release not allowed on ${{ github.ref_name }}, use dry-run." - exit 1 - - uses: actions/checkout@v3 with: fetch-depth: 0 - # Set token for pushing protected tag (vX.X.X). - token: ${{ secrets.RELEASE_GITHUB_PAT }} # If the event that triggered the build was an annotated tag (which our # tags are supposed to be), actions/checkout has a bug where the tag in @@ -75,55 +49,23 @@ jobs: - name: Fetch git tags run: git fetch --tags --force - # Configure git user name/email for creating annotated version tag. - - name: Setup git config - run: | - git config user.name "Coder CI" - git config user.email "dean+cdrci@coder.com" - - - name: Create release tag and release notes + - name: Create release notes + env: + # We always have to set this since there might be commits on + # main that didn't have a PR. + CODER_IGNORE_MISSING_COMMIT_METADATA: "1" run: | set -euo pipefail ref=HEAD old_version="$(git describe --abbrev=0 "$ref^1")" - - if [[ "${{ inputs.ignore_missing_commit_metadata }}" == *t* ]]; then - export CODER_IGNORE_MISSING_COMMIT_METADATA=1 - fi - - # Warn if CODER_IGNORE_MISSING_COMMIT_METADATA is set any other way - # than via dry-run. - if [[ ${CODER_IGNORE_MISSING_COMMIT_METADATA:-0} != 0 ]]; then - echo "WARNING: CODER_IGNORE_MISSING_COMMIT_METADATA is enabled and we will ignore missing commit metadata." 1>&2 - fi - - version_args=() - if [[ $CODER_DRY_RUN == *t* ]]; then - # Allow dry-run of branches to pass. - export CODER_IGNORE_MISSING_COMMIT_METADATA=1 - version_args+=(--dry-run) - fi - - # Cache commit metadata. - . ./scripts/release/check_commit_metadata.sh "$old_version" "$ref" - - declare -p version_args - - # Create new release tag (note that this tag is not pushed before - # release.sh is run). - version="$( - ./scripts/release/tag_version.sh \ - "${version_args[@]}" \ - --ref "$ref" \ - --"$CODER_RELEASE_INCREMENT" - )" + version="$(./scripts/version.sh)" # Generate notes. release_notes_file="$(mktemp -t release_notes.XXXXXX)" ./scripts/release/generate_release_notes.sh --old-version "$old_version" --new-version "$version" --ref "$ref" >> "$release_notes_file" echo CODER_RELEASE_NOTES_FILE="$release_notes_file" >> $GITHUB_ENV - - name: Echo release notes + - name: Show release notes run: | set -euo pipefail cat "$CODER_RELEASE_NOTES_FILE" @@ -243,9 +185,6 @@ jobs: set -euo pipefail publish_args=() - if [[ $CODER_RELEASE_DRAFT == *t* ]]; then - publish_args+=(--draft) - fi if [[ $CODER_DRY_RUN == *t* ]]; then publish_args+=(--dry-run) fi diff --git a/scripts/release.sh b/scripts/release.sh index dd8c27b2ed49f..d68c6780987b0 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -7,7 +7,7 @@ cdroot usage() { cat <] [--draft] [--dry-run] [--ref ] [--major | --minor | --patch] +Usage: ./release.sh [--dry-run] [--ref ] [--major | --minor | --patch] This script should be called to create a new release. @@ -23,47 +23,24 @@ be tagged at, otherwise the latest commit will be used. Set --minor to force a minor version bump, even when there are no breaking changes. Likewise for --major. By default a patch version will be created. -Set --dry-run to run the release workflow in CI as a dry-run (no release will -be created). +Set --dry-run to see what this script would do without making actual changes. To mark a release as containing breaking changes, the commit title should either contain a known prefix with an exclamation mark ("feat!:", "feat(api)!:") or the PR that was merged can be tagged with the "release/breaking" label. - -To test changes to this script, you can set --branch , which will -run the release workflow in CI as a dry-run and use the latest commit on the -specified branch as the release commit. This will also set --dry-run. EOH } -# Warn if CODER_IGNORE_MISSING_COMMIT_METADATA is set any other way than via -# --branch. -if [[ ${CODER_IGNORE_MISSING_COMMIT_METADATA:-0} != 0 ]]; then - log "WARNING: CODER_IGNORE_MISSING_COMMIT_METADATA is enabled externally, we will ignore missing commit metadata." -fi - branch=main -draft=0 dry_run=0 ref= increment= -args="$(getopt -o h -l branch:,draft,dry-run,help,ref:,major,minor,patch -- "$@")" +args="$(getopt -o h -l dry-run,help,ref:,major,minor,patch -- "$@")" eval set -- "$args" while true; do case "$1" in - --branch) - branch="$2" - log "Using branch $branch, implies DRYRUN and CODER_IGNORE_MISSING_COMMIT_METADATA." - dry_run=1 - export CODER_IGNORE_MISSING_COMMIT_METADATA=1 - shift 2 - ;; - --draft) - draft=1 - shift - ;; --dry-run) dry_run=1 shift @@ -115,64 +92,53 @@ fi # Check the current version tag from GitHub (by number) using the API to # ensure no local tags are considered. +log "Checking GitHub for latest release..." 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) old_version=${versions[0]} +log "Latest release: $old_version" +log trap 'log "Check commit metadata failed, you can try to set \"export CODER_IGNORE_MISSING_COMMIT_METADATA=1\" and try again, if you know what you are doing."' EXIT # shellcheck source=scripts/release/check_commit_metadata.sh source "$SCRIPT_DIR/release/check_commit_metadata.sh" "$old_version" "$ref" trap - EXIT -new_version="$(execrelative ./release/tag_version.sh --dry-run --ref "$ref" --"$increment")" +log "Executing DRYRUN of release tagging..." +new_version="$(execrelative ./release/tag_version.sh --old-version "$old_version" --ref "$ref" --"$increment" --dry-run)" +log +read -p "Continue? (y/n) " -n 1 -r continue_release +log +if ! [[ $continue_release =~ ^[Yy]$ ]]; then + exit 0 +fi + release_notes="$(execrelative ./release/generate_release_notes.sh --old-version "$old_version" --new-version "$new_version" --ref "$ref")" -log read -p "Preview release notes? (y/n) " -n 1 -r show_reply log if [[ $show_reply =~ ^[Yy]$ ]]; then + log echo -e "$release_notes\n" fi -create_message="Create release" -if ((draft)); then - create_message="Create draft release" -fi -if ((dry_run)); then - create_message+=" (DRYRUN)" -fi -read -p "$create_message? (y/n) " -n 1 -r create +read -p "Create release? (y/n) " -n 1 -r create log if ! [[ $create =~ ^[Yy]$ ]]; then exit 0 fi -args=() +log +# Run without dry-run to actually create the tag, note we don't update the +# new_version variable here to ensure we're pushing what we showed before. +maybedryrun "$dry_run" execrelative ./release/tag_version.sh --old-version "$old_version" --ref "$ref" --"$increment" >/dev/null +maybedryrun "$dry_run" git push --tags -u origin "$new_version" -# Draft and dry-run are required args. -if ((draft)); then - args+=(-F draft=true) -else - args+=(-F draft=false) -fi if ((dry_run)); then - args+=(-F dry_run=true) -else - args+=(-F dry_run=false) - - # We only set this on non-dry-run releases because it will show a - # warning in CI. - if [[ ${CODER_IGNORE_MISSING_COMMIT_METADATA:-0} == 1 ]]; then - args+=(-F ignore_missing_commit_metadata=true) - fi + # We can't watch the release.yaml workflow if we're in dry-run mode. + exit 0 fi log -logrun gh workflow run release.yaml \ - --ref "$branch" \ - -F increment="$increment" \ - "${args[@]}" -log - read -p "Watch release? (y/n) " -n 1 -r watch log if ! [[ $watch =~ ^[Yy]$ ]]; then diff --git a/scripts/release/check_commit_metadata.sh b/scripts/release/check_commit_metadata.sh index bfdd3b33ba98c..e3df4c76f186d 100755 --- a/scripts/release/check_commit_metadata.sh +++ b/scripts/release/check_commit_metadata.sh @@ -91,10 +91,12 @@ main() { commit_sha_long=${parts[1]} commit_prefix=${parts[2]} - if [[ $ignore_missing_metadata != 1 ]]; then - # Safety-check, guarantee all commits had their metadata fetched. - if [[ ! -v labels[$commit_sha_long] ]]; then + # Safety-check, guarantee all commits had their metadata fetched. + if [[ ! -v labels[$commit_sha_long] ]]; then + if [[ $ignore_missing_metadata != 1 ]]; then error "Metadata missing for commit $commit_sha_short" + else + log "WARNING: Metadata missing for commit $commit_sha_short" fi fi diff --git a/scripts/release/publish.sh b/scripts/release/publish.sh index e76bcdacb32c9..fd34b5759bedd 100755 --- a/scripts/release/publish.sh +++ b/scripts/release/publish.sh @@ -35,10 +35,9 @@ fi version="" release_notes_file="" -draft=0 dry_run=0 -args="$(getopt -o "" -l version:,release-notes-file:,draft,dry-run -- "$@")" +args="$(getopt -o "" -l version:,release-notes-file:,dry-run -- "$@")" eval set -- "$args" while true; do case "$1" in @@ -50,10 +49,6 @@ while true; do release_notes_file="$2" shift 2 ;; - --draft) - draft=1 - shift - ;; --dry-run) dry_run=1 shift @@ -134,20 +129,11 @@ popd log log -log "Pushing git tag" -maybedryrun "$dry_run" git push --quiet origin "$new_tag" - -args=() -if ((draft)); then - args+=(--draft) -fi - # We pipe `true` into `gh` so that it never tries to be interactive. true | maybedryrun "$dry_run" gh release create \ --title "$new_tag" \ --notes-file "$release_notes_file" \ - "${args[@]}" \ "$new_tag" \ "$temp_dir"/* diff --git a/scripts/release/tag_version.sh b/scripts/release/tag_version.sh index f5e47785a0048..c42c88eb4e68e 100755 --- a/scripts/release/tag_version.sh +++ b/scripts/release/tag_version.sh @@ -7,23 +7,27 @@ cdroot usage() { cat <] <--major | --minor | --patch> +Usage: ./version_tag.sh [--dry-run] [--old-version ] [--ref ] <--major | --minor | --patch> This script should be called to tag a new release. It will take the suggested increment (major, minor, patch) and optionally promote e.g. patch -> minor if there are breaking changes between the previous version and the given --ref (or HEAD). -This script will create a git tag, so it should only be run in CI (or via ---dry-run). +Pass --old-version optionally to ensure that the version is bumped from the +provided version instead of the latest tag (for use in release.sh). + +This script will create a git tag, it should only be called by release.sh or in +CI. EOH } dry_run=0 +old_version= ref=HEAD increment= -args="$(getopt -o h -l dry-run,help,ref:,major,minor,patch -- "$@")" +args="$(getopt -o h -l dry-run,help,old-version:,ref:,major,minor,patch -- "$@")" eval set -- "$args" while true; do case "$1" in @@ -31,6 +35,10 @@ while true; do dry_run=1 shift ;; + --old-version) + old_version="$2" + shift 2 + ;; --ref) ref="$2" shift 2 @@ -63,20 +71,12 @@ if [[ -z $increment ]]; then error "No version increment provided." fi -if [[ $dry_run != 1 ]] && [[ ${CI:-} == "" ]]; then - error "This script must be run in CI or with --dry-run." +if [[ -z $old_version ]]; then + old_version="$(git describe --abbrev=0 "$ref^1" --always)" fi - -old_version="$(git describe --abbrev=0 "$ref^1")" -cur_tag="$(git describe --abbrev=0 "$ref")" +cur_tag="$(git describe --abbrev=0 "$ref" --always)" if [[ $old_version != "$cur_tag" ]]; then - message="Ref \"$ref\" is already tagged with a release ($cur_tag)" - if ! ((dry_run)); then - error "$message." - fi - log "DRYRUN: $message, echoing current tag." - echo "$cur_tag" - exit 0 + error "A newer tag than \"$old_version\" already exists for \"$ref\" ($cur_tag), aborting." fi ref=$(git rev-parse --short "$ref")