Skip to content

feat: Add release.sh script and detect breaking changes #5366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 15, 2022

Conversation

mafredri
Copy link
Member

@mafredri mafredri commented Dec 9, 2022

NOTE: This is not the final form of how releases will be made, see below.

This commit introduces three new scripts:

  • release.sh To be run by a user on their local machine to preview and
    create a new release (tag + push)
  • check_commit_metadata.sh For e.g. detecting breaking changes
  • genereate_release_notes.sh To display the generated release notes,
    used for previews and in publish_release.sh

The release.sh script can be run without arguments, and it will
automatically determine if we're to do a patch or minor release. A minor
release can be forced via --minor flag.

Breaking changes can be annotated either via commit/merge title prefix
(feat!:, feat(api)!:), or by adding the release/breaking label to
the PR that was merged (on GitHub).

Related #5233

This work will be followed up by changes to move the tag creation from
release.sh to CI workflows, the release.sh will then become a
lightweight wrapper to run the workflow.

Documented here: #5381, #5382


Example output:

./scripts/release.sh
Fetching main and tags from origin...
Checking commit metadata for changes since v0.13.1...
(faked) Breaking change detected, incrementing minor version...
Old version: v0.13.1
New version: v0.14.0

Preview release notes? (y/n)
## Changelog:

### BREAKING CHANGES

- 81c39487 fix: Close tty first in `ptytest` cleanup (#5252)

### Features

- 061635c3 feat: Allow multiple OIDC domains (#5210)
- 9e4d213c feat: add lazygit, remove kubic, use dotfiles in dogfood image (#5271)
- e17fd0bb feat: Add GET previous template version endpoint (#5230)
- ce76d9d5 feat: Add diff and Dockerfile support for template version page (#5339)

### Bug fixes

- 92c217bd fix: add index on `workspace_agents.auth_token` (#5244)
- 8469dbc0 fix: add index to `provisioner_jobs.started_at` (#5245)
- fa641554 fix: Improve agent connection tracking when agent is closed (#5253)
- 137a48c2 fix: workspaceapps: overloaded test server responds with 502s (#5255)
- a02617b6 fix: remove unnecessary `WHERE`s from `AcquireProvisionerJob` (#5257)
- cec667d3 fix: prettier misses docs directory (#5285)
- e04877a6 fix: race conditions in replicasync (#5289)
- 02dcd0e2 fix: Fix resource avatar when icon is empty string (#5291)
- d3200382 fix: agent panics on closed network (#5295)
- 84872d97 fix: loadtest/reconnectingpty tweak timeout (#5300)
- b2dc60c0 fix: markdown-link-check base-branch should not be set on main branch (#5311)
- c77c1b4b fix: Retry if there is no git auth user yet (#5316)
- 6651c163 fix: avoid terraform state concurrent access, remove global mutex (#5273)
- 161465db fix: do not canonicalize Sec-WebSocket-* headers in apps (#5334)
- 2f3ff6ce fix: improve pty and ptytest (#5327)
- 3cea5f96 fix: wait for creating template versions (#5335)
- f7467cac fix: Improve ptytest closure on expect match timeout (#5337)
- ee605b34 fix: Don't show progress bar for new templates (#5298)
- 8ea09235 fix: UX issues in template settings form's default auto-stop field (#5330)
- 534bff2f fix: coderd/prometheusmetrics wait for all metrics in require.Eventually (#5338)
- c063ac24 fix: use doWithRetries when making HTTP calls (#5344)
- 687261c8 fix: Close coordinator in wsconncache test (#5348)
- f68a6569 fix: winget package releases (#5352)
- 3c9dab34 fix: Fix CSP for monaco editor (#5358)
- 92c5e97f fix: Fix CSP style directive for Monaco editor (#5360)
- 05130db5 fix: Improve closing of services in agent tests (#5355)
- 935d2eb5 fix: fmt should check for unstaged files (#5362)
- cd04330c fix: replace fireEvent with userEvent (#5361)

### Other changes

- 65407462 Add audit links/kira pilot (#5156)
- df389d42 Add build number to workspace_build audit logs (#5267)
- 1cfe5de1 Add Service Banners (#5272)
- 02bb052d Fix template Avatar (#5294)
- ee74df3d Fix scope of dbTTL (#5197)
- ee4f0fc5 chore: enable debug logging for gotestsum (#5248)
- 133b2de1 chore: improve markdown-link-check workflow (#5303)
- 825480ae chore: bump crate-ci/typos from 1.12.12 to 1.13.3 (#5304)
- 4a0ca481 chore: bump @typescript-eslint/parser from 5.38.1 to 5.45.1 in /sit (#5305)
- 03328d4f chore: bump eslint from 8.24.0 to 8.29.0 in /site (#5306)
- a973c35a chore: collect gotestsum TestEvents as workflow artifacts (#5336)
- 1ac1af7d chore: bump emoji-mart from 5.2.1 to 5.3.3 in /site (#5226)
- 3d95c925 chore: Add DatoCMS token to dogfood template (#5254)
- 7eb3ab04 chore: bump decode-uri-component from 0.2.0 to 0.2.2 in /site (#5251)
- 971e3678 chore: improve logging in provisionerd_test (#5353)
- 91973e1e cli: remove redundant client creation requests (#5264)
- a3083f77 docs: fix community templates link on site. (#5278)
- b4603582 docs: add templates link to README.md (#5201)
- 59af8349 docs: add offical kubernetes provider runtime_class_name (#5157)
- 85945af5 fixed bug; wrote tests (#5329)
- fd545128 helm: add certs secret mount (#4641)
- 9cfdbec2 refactor: Remove login banner (#5239)
- 85a6d14f skip: reconnectingpty tests (#5322)

https://github.com/coder/coder/compare/v0.13.1...v0.14.0

## Container image:

- `docker pull ghcr.io/coder/coder:v0.14.0`

Create release? (y/n)
Tagging commit 971e3678 as v0.14.0...
git tag -a v0.14.0 -m v0.14.0 971e3678
Pushing tag to origin...
git push -u origin v0.14.0

This commit introduces three new scripts:

- `release.sh` To be run by a user on their local machine to preview and
  create a new release (tag + push)
- `check_commit_metadata.sh` For e.g. detecting breaking changes
- `genereate_release_notes.sh` To display the generated release notes,
  used for previews and in `publish_release.sh`

The `release.sh` script can be run without arguments, and it will
automatically determine if we're to do a patch or minor release. A minor
release can be forced via `--minor` flag.

Breaking changes can be annotated either via commit/merge title prefix
(`feat!:`, `feat(api)!:`), or by adding the `release/breaking` label to
the PR that was merged (on GitHub).

Related #5233

This work will be followed up by changes to move the tag creation from
`release.sh` to CI workflows, the `release.sh` will then become a
lightweight wrapper to run the workflow.
@mafredri mafredri self-assigned this Dec 9, 2022
@bpmct
Copy link
Member

bpmct commented Dec 9, 2022

Looks great!! What do you think about failing the release workflow if the user did a manual git tag && git push? @kylecarbs and I are the only releasers right now, so it might not be a big deal but it might not hurt to systematically avoid manual releases now that we have the script :)

@mafredri
Copy link
Member Author

mafredri commented Dec 9, 2022

Looks great!! What do you think about failing the release workflow if the user did a manual git tag && git push? @kylecarbs and I are the only releasers right now, so it might not be a big deal but it might not hurt to systematically avoid manual releases now that we have the script :)

I'm definitely supportive of that idea. Do you think we should have it in this PR already or is it OK to leave to a follow up one? I'm going to do some workflow changes and a bit more refactoring of publish_release.sh in a follow up PR and it would be a good fit there.

One easy way to throw it into this PR would be to change the tag message -m 'Release vX.X.X (via release.sh)' or something similar, and then skip it if it doesn't contain the right message format.

@bpmct
Copy link
Member

bpmct commented Dec 9, 2022

Do you think we should have it in this PR already or is it OK to leave to a follow up one?

A follow-up one (and issue) makes sense! I wouldn't consider it a blocker at all :)

Copy link
Member

@mtojek mtojek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My biggest concern is lack of tests to validate all cases of generating the changelog, but I don't insist on enabling any test runner like bats. I guess that it might be hard to integrate with gh api.

@@ -0,0 +1,108 @@
#!/usr/bin/env bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about placing all these scripts in the separate release directory?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we have enough release related scripts to put them there, I'd still like to keep /scripts/release.sh (because used by humans) but the rest can go in /scripts/release/*.sh.

continue
fi

if [[ $commit_prefix =~ $prefix_pattern ]]; then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I still have a feeling that we should switch to Python or mage at this point :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't get behind Python, but I'm indifferent to mage, so it's not me you have to convince. 😉

#
# Example: ./check_commit_tags.sh v0.13.1..971e3678
#
# When sourced, this script will populate the COMMIT_METADATA_* variables
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applicable only to squash & merge - Do you think that we may need another script executed in CI to check if the title of PR is consistent?

Copy link
Member Author

@mafredri mafredri Dec 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, what do you mean by Applicable only to squash & merge? I guess we could make it clearer this script is only intended for the commit log on the main branch.

Yes, we'll add an action/lint for PR title 👍🏻 (follow up PR)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we'll add an action/lint for PR title 👍🏻 (follow up PR)

👍

Applicable only to squash & merge?

I thought that Create a merge commit option is also enabled for this repo, but it seems to be off. If it's on, it might be more challenging to validate also commits than only the PR title.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, yeah we'll only validate PR titles, I made the assumption when writing that we'll only squash merge.

Obviously owners/admins could bypass this by pushing straight to main. 🙄

Co-authored-by: Marcin Tojek <mtojek@users.noreply.github.com>
@mafredri
Copy link
Member Author

My biggest concern is lack of tests to validate all cases of generating the changelog, but I don't insist on enabling any test runner like bats. I guess that it might be hard to integrate with gh api.

It's not necessarily hard per-se, we can just gh() { if ((testing)); then gh_fake "$@"; else command gh "$@"; fi }, but I don't think it will bring a whole lot of value for a few reasons:

  • The changelog is generated the same way locally and in CI, and it can be previewed by the releaser (I could change to always print the changelog instead of optionally)
  • The changelog is editable after the face (if something went wrong)
  • If we encounter an error, release will abort (set -e)

I guess the biggest risk is in potentially not detecting a breaking change and only doing patch release if something goes wrong or a future change in the script breaks it.

@mafredri mafredri force-pushed the mafredri/release-improvements branch from 190f7ed to 0f5883f Compare December 12, 2022 11:56
Copy link
Member

@bpmct bpmct left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tested it and all works as expected!

(I manually copied the changelog since I understand the script will not do the new changelog format until we merge the new workflow)

Copy link
Member

@deansheather deansheather left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These scripts seem good but they are hard to follow even for me. We're starting to reach a point where it'd be better to write these more complex scripts in a different language like python or go

scripts/lib.sh Outdated
Comment on lines 126 to 128
if issourced; then
return 1
fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you put a comment here explaining why this is necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'll just remove it. It's not really necessary if we always want to exit on error. I had the scripts set up in a different way before.

fi

# Get the labels for the PR associated with this commit.
mapfile -t labels < <(gh api -H "Accept: application/vnd.github+json" "/repos/coder/coder/commits/${commit_sha}/pulls" -q '.[].labels[].name')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rate limit for calls from github actions is 1k per hour per repository. I'd say an upper bound for the amount of PRs in a release would be 100, but combined with other things in the repo we might be pushing on 1k per hour during busy periods. Can this instead use a list endpoints?

The commits we release are a contiguous block on main so if you list from first ref to end ref then you will get them all in much less requests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did consider this but it's not clear how we'd trigger it. Now I don't remember but do we run dry-run for every merged commit? I considered adding a --skip-label-check to this script used in dry-run scenarios, but omitted it for simplicity and because it would generate "false" release notes.

The commits we release are a contiguous block on main so if you list from first ref to end ref then you will get them all in much less requests.

The problem isn't listing the commits, but in that endpoint they won't include the metadata we're looking for (PR labels). I tried to find a way to get all of this information in one go, but each one has caveats.. 😅

We could use /repos/coder/coder/pulls, but even with filtering ?state=closed&base=main&per_page=100 (maybe sort=updated), we might have to keep traversing multiple pages to find all commits that we're looking for (say we have a month old PR that got merged).

The current API is also interesting in that, I'm speculating here, but it lists PRs related to the commit. As such I'm thinking maybe it would also list a PR that has been merged into a PR that then is merged into main. This could be useful.

But alas, that benefit is theoretical and ultimately, in most cases, not beneficial. The pulls endpoint will likely result in less requests so maybe we go with that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we run dry-run for every merged commit

We do not

What if you instead listed all PRs that had the breaking label and then checked if the merge commit is from any of those PRs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deansheather Thanks for your suggestion, this put me on the right path:

❯ old_tagged_commit=$(git rev-list -n 1 v0.13.2)
❯ old_commit_date=$(git show --no-patch --date=short --format=%cd "$old_tagged_commit")
# 2022-12-12
❯ gh pr list --base main --state merged --limit 9999 --search "merged:>=$old_commit_date" --json mergeCommit,labels --jq '.[] | .mergeCommit.oid + " " + ([.labels[].name] | join(" "))'
2b864cee9e71341dac86691805b9552b3277ff5f
1907f13c5f509f28be5101354f870a67a578a5e2
88bb901283055397c9d04d27ad51ce3d4417d645
08a6a182265e7351979defbce512e36a058006f0
e7fc21e28568b8ae0c9959a670cc7df237bd8aea

I think this is a pretty nice solution, and it'll be easily extensible in the future too. It's quite fast so I'd venture it only does one graphql lookup or some such. If we really wanted to we could add --label release/breaking, but we may want to look up other metadata in the future too. Although, if we have more than 9999 commits in a release, we're screwed 😅... So maybe we should start by filtering by the one label.

Could also consider using API endpoints straight (instead of gh pr list), but I couldn't find how to filter on coder/coder, kept getting results from all of GitHub ¯\_(ツ)_/¯.

(I'm hoping the gh command will support gh pr list in CI as well.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deansheather this is now implemented in c871cb7 (#5366).

I opted to not filter the search results based on a single tag because I wanted to create a safety-check that ensures we fetched PR data for each commit.

@mafredri mafredri merged commit e96fdbe into main Dec 15, 2022
@mafredri mafredri deleted the mafredri/release-improvements branch December 15, 2022 13:41
@github-actions github-actions bot locked and limited conversation to collaborators Dec 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants