Skip to content

Instantly share code, notes, and snippets.

@nikclayton
Last active July 26, 2023 16:02
Show Gist options
  • Save nikclayton/676a555688b219fa0131af43095ea67b to your computer and use it in GitHub Desktop.
Save nikclayton/676a555688b219fa0131af43095ea67b to your computer and use it in GitHub Desktop.

Notes from the v22 release

PR descriptions

One of the release tasks is putting together the release notes.

This involves going back through all the merged PRs, figuring out what they did, and boiling that down to a one or two sentence summary.

This is significantly easier if the PR is self-contained. This means:

  1. The PR title concisely explains what it's for
  2. The PR description explains the problem, how it was solved, and user-visible changes because of the PR

Linking from the PR back to an issue or issues is not a substitute for this.

  1. The issue may not describe exactly how the problem manifests
  2. The issue may not describe the user-visible changes that the PR introduces in order to fix the problem.

PRs should be squash-merged, with proper commit messages

The commit history is a mess because at the moment every PR is merged with a merge commit, so all the individual commits in that PR are merged to develop.

To fix this PRs should be squash-merged, and a real commit message should be written for the squashed merge.

A common objection to this goes as follows:

  1. We use git
  2. Git's unit of work is a commit
  3. Keeping the history of every commit that makes up a PR is important
  4. Therefore squash merges are bad

This is tempting, but wrong.

It's wrong because while we do use git we also use GitHub.

And GitHub's unit of work is a PR.

A PR is merged in totality -- you can't choose to merge some commits from a PR and not others without creating a fresh PR that includes just the commits you want to merge.

Ideally, the PR's title and the first full comment in the description should be usable as the commit message.

Exceptions

  • PRs from Weblate, they do bundle individual commits.

PRs that include unrelated changes

Don't mix formatting changes and technical changes in the same PR.

Generally. If it's a sweeping architectural change this might not be possible.

  • Reviewing the PR is more time consuming
  • Understanding the PR to write release notes is more time consuming

For example: https://github.com/tuskyapp/Tusky/pull/3421/files

As well as the core technical changes that PR also:

  • Renames variables (mediaValue to mediaList in multiple unrelated areas of the code)
  • Makes whitespace changes to string resource files

PR authors could write the release notes

Producing the release notes could be done for a PR by the PR's author, and enforced by a pre-merge check.

For example:

  • Add a PR check with the following logic:
    • PR has label "no-user-visible-change", the check passes
    • PR contains modifications to CHANGELOG.md, the check passes
    • The check fails

This logic:

  1. Sets the default expectation to "PR requires a CHANGELOG.md change"
  2. Allows PR authors to opt-out of this by labelling their PR appropriately

A PR label is one possible control mechanism, but there are others.

For example, we could use Conventional Commits and require anything with a fix: or feat: commit type to include a CHANGELOG.md change.

Bitrise is slow

  • Only 1 x concurrency
  • Nightly builds on every merged PR, which takes ~ 10 minutes

Github Actions has 20x concurrency, might be considerably faster. tuskyapp/Tusky#3728, if merged, would allow us to get data on whether this would be faster.

GPG signed releases / 2FA / security posture

What's the policy on using GPG to sign releases? Do we need one? We've got 125+K users so perhaps a moderately attractive target for bad actors.

Or require 2FA to be enabled for anyone who can merge to the repository (https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-two-factor-authentication-for-your-organization/requiring-two-factor-authentication-in-your-organization)?

More concretely, is there a threat model we care about?

Unclear about the different branches

We appear to be not-quite following the git-flow model for branches (https://nvie.com/posts/a-successful-git-branching-model/).

We have develop, main, and feature branches. But no release branches.

This is a problem.

PRs get merged to develop first.

Releases are tagged at a particular point in time.

This has the side effect of effectively freezing most work on develop. I was only merging PRs to develop if I was confident they were going to go in to the release. This is a roadblock for everyone else.

I.e.,

gitGraph
   commit
   branch develop
   commit
   branch feature
   commit
   checkout main
   merge develop tag: "rc1"
   checkout feature
   commit
   commit
   commit id: "approved" type: HIGHLIGHT
   checkout develop
   branch fix1
   commit
   checkout develop
   merge fix1
   checkout main
   merge develop tag: "rc2"
   checkout develop
   branch fix2
   commit
   checkout develop
   merge fix2
   checkout main
   merge develop tag: "rc3"

In this example there's a feature branch (feature) that has a few commits, and is approved. But it can't be merged to develop because the release process is underway, and only fixes are being merged. So the feature branch is frozen for the life of the release.

To remove this, we could create a release branch for each release. E.g., when the release process for v23 starts, create a branch from develop for the release (e.g., release-23.0).

PRs can continue to be merged to develop during this process. Any PRs that should go in to the release are merged in to release-23.0, and a GitHub action automatically also merges it in to develop.

Specific beta releases from the release-23.0 branch, and the final release, can be tagged as normal.

That would be the full git-flow model.

But also, after cutting over to this scheme there's no real need for the develop branch, PRs can be merged in to main.

That would look something like this:

gitGraph
  commit
  branch release-23
  branch feature
  checkout release-23
  commit tag: "23.0-beta.1"
  checkout main
  merge release-23
  checkout release-23
  checkout feature
  commit
  checkout release-23
  branch fix
  commit
  checkout release-23
  merge fix tag: "23.0-beta.2"
  checkout main
  merge release-23
  checkout feature
  merge main
  commit
  checkout main
  merge feature
  branch feature2
  commit
  commit
  checkout main
  merge feature2
  checkout release-23
  commit tag: "23.0"
  checkout main
  merge release-23

With these changes, main takes the role that develop does now (and there is no develop branch).

When the release process starts a release-23 branch is created, then there is the commit that sets the versionCode, updates the changelog, etc, and 23.0-beta.1 is released. This is merged back to main.

In the meantime, someone else starts working on a feature branch, branched from main.

There's a problem with beta 1, so a fix branch is created, the fix is made there, merged in to release-23, and tagged as a new beta release. This is also merged back to main.

feature continues, with occasional merges from main. It's reviewed, approved, and merged back in to main at the appropriate time. A second feature branch is also created for an unrelated feature, development happens, and is merged in to main.

Beta 2 is the last beta release, so there's a final commit on the release-23 branch to set the final versionCode, etc. Then it's tagged, and merged in to main.

Open questions

After the release has started should any changes be merged from main to the release branch (release-23 in this example)?

Probably not. The only thing I can think of is PRs containing translations from Weblate. They can be merged in to the release branch instead of main. Since the release branch is periodically merged in to main those changes will end up on main pretty soon anyway.

GitHub discussions?

Creating a release on GitHub has a "Create a discussion for this release" option which notes "People will be able to leave comments and reactions on this release using Discussions.". Maybe that's a good place to try and centralise commentary about the release?

Create signed APKs from PRs?

When debugging it became necessary to prepare custom APKs for users to sideload.

This worked, but I'm uneasy about the level of trust this requires from the user.

So I'm wondering if it's possible to have the CI system build an APK for each PR, and attach it to a fixed comment on each PR. If new commits are made to the PR then the APK is rebuilt and reattached.

Then, if we have a fix that we want a user to try we can prep a PR and tell them to download the APK from the PR.

Bitrise is supposed to do this (I think), but it's not working, per:

If I'm reading the bitrise.yml file correctly the deploy-to-bitrise step should mean that there's an "Artifacts" entry on the Build details page for each build (per https://devcenter.bitrise.io/en/builds/managing-build-files/build-artifacts-online.html)

This would be very handy, as each PR would automatically have a signed APK built that we could point people to for individual testing.

But if I look at a build, like:

(nightly) https://app.bitrise.io/build/f8e9cdf3-cab7-475f-bd08-f15401d59e1c
(primary) https://app.bitrise.io/build/8bf6e421-eb7c-48a1-ac34-b24cb48f5271?tab=details

there's no "Artifacts" tab.

https://devcenter.bitrise.io/en/deploying/bitrise-ota-app-deployment.html#the-public-install-page suggests there's a config option to the step which defaults to true (https://github.com/bitrise-io/bitrise-steplib/blob/master/steps/deploy-to-bitrise-io/2.1.8/step.yml#L179) and we don't seem to have overridden that.

I had an (unrelated) call with Bitrise Dev. Rel. a few days back, and raised this with them, so they're looking in to it.

Staged roll outs

The current release process makes a new final release available to 100% of the userbase, and they then take a few weeks to update.

If we discover anything at the last minute where we want to pause the release (which can happen, even with nightly and betas) we can't.

We could do the final rollout over a longer period, say 4 days, with increments of say 5%, 25%, 50%, 100%

It's more work (but it could be automated), and it would allow us to pause a release if we discover a problem where we want to stop and think about the best way to proceed.

Tooling

There's a lot of manual work to be done on a release.

I've written something that automates a lot of it. It's not ready for review yet, but https://github.com/nikclayton/Tusky/compare/mklanguages...nikclayton:Tusky:mkrelease.

You run mkrelease start to start a new release process, and then mkrelease beta for each new beta version you want to release.

It walks you through the steps, automating some of them (editing build.gradle, creating release notes from the changelog, creating the GitHub release, editing the F-Droid JSON, etc), and prompts you with the manual steps for things that aren't automated yet (e.g., clicking through the Play Console to promote a release).

When the beta chain is finished you run mkrelease final to create the non-beta release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment