Neurobagel Release process
For all tools, our goal is to have the main branch releasable at all times.
That means:
- all linters and static tools are passing
- all tests are passing
- all new features since last release have appropriate documentation (as part of PR)
- the build is successful
Note: For features that take a lot of work to implement,
having main always be releasable requires an extra step.
For example a feature to "make this experiment reproducible"
might take several PRs to implement.
To still have main be in a releasable state,
we will have to hide the in-progress feature behind
a feature flag.
The goal is that we could release always, but we choose to release only when it makes sense. Related: the work to do an actual release should be minimal, and only involve the actual release process:
- changelog editing
- version increment picking
- celebrating
It should not include any fixes, changes, or the writing of new documentation.
When to release
We will decide when to make a release as a team. The goal for releases is:
- Release often - A release should not be a major thing that we only do every couple of months.
- Release regularly - As much as we can, the releases should be at regular intervals. This may mean that different tools have different release intervals.
- Release when it makes sense - The idea is that we only push released versions to our deployments. So not every merged PR will get deployed. But we will want to deploy and thus release whenever we fix an important bug or a new feature.
Auto-releasing using intuit/auto
Context
- This is done using a workflow called
release.yamland the.autorcconfiguration file in each repo - Note that the workflow uses PATs (not
GITHUB_TOKEN, which doesn't have sufficient permissions) that have, among other permissions, R+W permissions to both issues and pull requests
About PR labels
autouses custom labels applied to PRs in a repo to determine when a release is made, which PRs are included in the release (and under what CHANGELOG heading), and to calculate the version bump for the release (major/minor/patch)- Each of these PR labels has one associated
releaseType(defined in.autorc) that basically describes the version bump that should happen if a release were to be made containing only a PR with that label applied, and also the version bump if the label were paired with others (of potentially differentreleaseTypes) on the same PR- Possible label
releaseTypes: "major", "minor", "patch", "skip", "none", "release" (note that these do not refer to the labels themselves, which can have different names) - Multiple labels can have the same
releaseType - Labels with a
releaseTypeof "major"/"minor"/"patch" are called SEMVER labels - these are used to calculate the final version bump for a release
- Possible label
Automatically creating the PR labels in a repo
Note: as long as the label properties don't change, this step only needs to be done once in the planning repo, since the labels will then be propagated to all other repos via our label syncing workflow.
-
Install auto using binary on local machine
-
cdinto the repo in which you want to create the labels -
Ensure the
.autorcfile is available at the root of the repo -
Create a
.envfile in the repo root with the variableGH_TOKENcontaining the value of a PAT with repo R+W access to at least issues and code (contents) -
Run
auto create-labelsfrom the repo root -
Check that the list of created labels corresponds to the ones in your
.autorc
Our configuration
You can find our PR label configuration here: https://github.com/neurobagel/workflows/blob/main/template_configs/.autorc
The default PR label, if no labels are applied, is pr-patch (which also has "releaseType": "patch").
Our current auto configuration only releases when a PR with the release label applied has been merged.
When this happens, all PRs that have been merged since the last release are collected and auto looks at the PR label(s) on each to figure out:
- if the PR should be included in the changelog
- if a PR has a label of skip-release (has "releaseType": "skip") this always takes precedence over other SEMVER labels on the same PR
- if so, what changelog heading it should go under (based "changelogTitle" label attribute in .autorc)
Finally, auto looks for the highest-priority releaseType among the included PRs based on their labels, and increments either the major, minor, or patch version accordingly.
Notes from troubleshooting automatic release workflow
- A non-default token (not
GITHUB_TOKEN) needs to be used to create the actual release, otherwise the release is blocked from successfully triggering another workflow (e.g., building Docker image) - Besides the release, the changelog needs to be updated and committed to the default branch
- Because of our
mainbranch protection rules (PR required, 1 approval required), we originally used theprotected-branchplugin for auto, which auto-creates a PR for the changelog, and then auto-reviews it and merges it- This requires a separate, additional token (possibly for a different user?) than the one used to create the release, b/c a PR cannot be approved by the PR author
- In our case, the
NB_PAT_RELEASEandNB_PAT_RELEASE_PROTECTEDtokens were created under different users, which worked out
- In our case, the
- This requires a separate, additional token (possibly for a different user?) than the one used to create the release, b/c a PR cannot be approved by the PR author
- Because of our
Problem
- Because PRs for changelogs were now auto-approved & merged immediately after creation, pre-commit CI (which always runs on every commit to a PR) would not have enough time to run checks before the PR is merged and would thus often fail on these PRs, leading to red checks on
main= bad- Relevant issue: https://github.com/neurobagel/workflows/issues/87
- Here, skip keywords for pre-commit CI did not have any effect because they only work on main branch commits (this is the difference between the
pre-commit - prand thepre-commit - pushchecks), not PRs - We needed a way to either push directly to the main branch (without needing to open a PR) from the wf, or force it to wait for the pre-commit CI
- What didn't work
- Configure
pre-commit - pras arequiredStatusChecksforprotected-branch(https://www.npmjs.com/package/@auto-it/protected-branch?activeTab=readme#usage), to try and force this check before the PR is merged- Result: release failed:
requiredStatusChecksrelies on a token that has theCheckspermission, which is currently not supported for user PATs (this is a known issue for fine-grained PATs) - only apps can have this permission- classic user PATs also don't work for this
- Result: release failed:
- Make
pre-commit - pra required status check for PRs at the repo level (in branch protection settings)- Result: release failed: workflow would error out because required pre-commit check was still pending
- Explicitly temporarily enable & disable force pushes in release workflow, as described in https://github.com/intuit/auto/issues/1491#issuecomment-1610120918
- Result: release failed, with error that PR must be opened
- Creating a basic, no-code app "Neurobagel Bot" (
neurobagel-bot) under the Neurobagel organization to authenticate with, to try using an app installation access token for releasing + updating the changelog, following the GH docs, and making sure it was allowed to bypass required PRs in the repo settings- Result: release succeeded, but changelog checks still failed
pre-commitnow was able to be triggered by theprotected-branchplugin, but pre-commit CI would still run separately and would fail due to the PR being merged first
- Result: release succeeded, but changelog checks still failed
- Configure
- What worked
- (it looks like this IS actually documented in the auto docs from this issue, and I just missed it somehow...)
- The PATs originally being used for the release wf belonged to org admins who should (theoretically) be allowed to bypass required PRs based on current rules
- Same for the bot app, which was allowed to bypass required PRs
- So, it was strange when we removed the
protected-branchplugin (which always creates a PR?), we were still getting the error in the wf failure that a PR was required
- The culprit was that a more privileged access token needed to be given to the earlier
actions/checkoutstep itself, otherwise the limited default auth level (the basic GitHub token) is used and persists in subsequent steps. This prevented more privileged git commands (e.g., pushing directly to a protected branch) from succeeding in subsequent steps, even though those steps had an appropriate token - Refs:
- https://github.com/marketplace/actions/checkout#checkout-v4 (maybe worth looking into
persist-credentials: false?) - https://stackoverflow.com/a/72515781
- https://stackoverflow.com/questions/63733822/cant-push-to-protected-branch-in-github-action
- https://github.com/marketplace/actions/checkout#checkout-v4 (maybe worth looking into
Working configuration
neurobagel-botis installed in all repos- Add
neurobagel-botas app allowed to bypass required PRs in repo settings- ensure that the app ID is stored as an actions variable, and private key as an actions secret
- Remove
protected-branchplugin from auto (so custom reviewer PAT also no longer required) - In release.yaml, obtain & use installation access token for
neurobagel-botin bothactions/checkoutand subsequent steps for the release creation
Related issues: - https://github.com/neurobagel/planning/issues/64
Create a release
- To ensure that PRs since the last release end up under the appropriate heading in the changelog, when opening a PR, the author should always manually apply at least one PR label from our
.autorcconfig (note: thepr-patchlabel is used by default behind the hood if no labels are applied, but we should aim to intentionally apply these to avoid having to modify changelog sections after a release)- For most changes, the author should choose the most relevant* label starting with the
pr-prefix - If a PR should be excluded from the changelog (e.g., some small automation PR that is not user-relevant), the 'skip-release' label should be applied
- For most changes, the author should choose the most relevant* label starting with the
- To auto-release after the PR you are working on is merged, apply the
releaselabel to the PR along with a relevantpr-prefixed label (note to double check: if nopr-labels are applied, the default one used should bepr-patch) - To include extra release notes in the changelog, you can do so by modifying the PR description following these instructions: https://intuit.github.io/auto/docs/generated/changelog#additional-release-notes
- Once your release PR is merged, check that the release was created successfully with the expected tag + SEMVER (you can also double check that the
release.yamlworkflow in the repo succeeded), and double check the changelog
*Each PR included in the release will be assigned to a single changelog section based upon the applied label with the highest releaseType that has a changelogTitle.
So, be careful when applying multiple pr- labels to the same PR - make sure that the label with the highest priority releaseType actually has the changelog section you want the PR to be listed under.
See also https://intuit.github.io/auto/docs/configuration/autorc#changelog-titles)
Docs: https://intuit.github.io/auto/docs
Auto-pushing to DockerHub [WIP]
Once you have made a new release, a new GH workflow will start to:
- Build a new Docker image from the tagged commit
- Ensure that the build and test succeed (if not ->
->
->
)
- Tag the new Docker image with two tags:
latestto show that it is the latest released version<version>to show that it is the specified version
- Push the Docker image to docker hub
- (where applicable) -> push the new version to production
- this may be a manual process
If we encounter a bug or notice things failing somewhere during this process, fixing this bug becomes the top priority for everyone. The fix for the bug becomes the next release and then gets deployed. While the fix is being worked on we will deploy :oldstable.
How to release manually (OLD)
At any given moment, the main branch is in a releasable state (see above).
To actually make a release, we use the normal GitHub release workflow.
Specifically:
- Click the "new Release" button
- Add a new "tag"
- the tag has to follow semantic versioning: https://semver.org/ (see below)
- check out the changelog (see below) to decide whether to make a patch, minor, or major version increase
- Add a title. The title has to match the "tag"
- i.e. for a new release called
"v0.2.4"the Release title would also be"v0.2.4"
- i.e. for a new release called
- Use the "generate release notes" button to add the
git logsince the last release- This also adds a section about new contributors!
- Edit the changelog (see also the Example release notes template)
- for minor and major versions (optional for patches) add a short description of the release on top of the changelog
- e.g. "This release introduces new functionality for EEG data" or "This release introduces a breaking change in the XYZ workflow ..."
- sort the changelog by type of change (i.e. a section with all of the
[FIX]changes, one for all the[FEAT], and so on) - remove irrelevant changes (e.g. dependabot version bumps?)
- Store the release as a draft
- Discuss the ready-to-go release during standup
- gives folks a chance to take a last look
- possibility for feedback on the release notes
- Release if no objections
Example release notes template (OLD)
Summary
This release introduces X. This release includes a breaking change to Y.
What's Changed
(Aim to group changes of the same type/prefix)
New or improved features โจ
Changes to the data model for inputs/outputs 
Documentation updates ๐
Bug fixes ๐ ๏ธ
Other changes ๐งน
Other important notes
Commiting after release was triggered
If a commit is added to the main branch (or whichever branch is configured to release off of) after the release workflow was triggered and before it finishes, auto will create the tag for the release but it will error out and won't finalize the release to avoid collisions. See this query tool issue for more details.