This PR automates the release process steps to:
- update the candidate branch with the contents of the release branch on
the repo in the subfolder
- create a PR merging the candidate branch into main
- create a new branch without the version bump. usually `1.2.x`, unless
it's a point release.
- if it's a point release, create a PR merging the new branch without
version bumps into the existing `1.2.x` branch.
This draws from steps 10, 12, and 13. Step 10 should still have the code
to push to pypi, since that's a different though, though I think that
could move here in the future as well.
My general design philosophy was "error out instead of letting the
script put git into a bad state," with the exception of PR creation
which seemed safe to skip and continue.
I've also added some flags, mostly to make testing this easier, but
could be useful for re-running the script as well. Unlike
`promote_snaps` and `generate_community_forum_post`,
`synchronize_github_repo` is *not* idempotent. I do not think it should
be, because of how git works. I think if branches already exist and the
user really did want to synchronize branches again, the user would want
to know that it can't be done automatically, and should instead be told
what to do to make it possible, or how to skip the whole thing. I don't
think we should, for example, go ahead and create a PR based on an old
version of a branch and just skip the pulling step, or automatically
delete a branch.
In `_create_and_push_branch_without_version_bump`, if you have created
the branch then fail after, you'll rerun and then get a message saying
to delete the branch. I think that's nicer than automatically deleting
it, in case you want to inspect it.
successful test run:
```
$ git switch create-pr
Switched to branch 'create-pr'
$ RELEASE_GPG_KEY=[test key] tools/release.sh 4.35.0 4.36.0
[release output]
$ tools/finish_release.py --test-version 4.35.0 --skip-snaps
Creating PR to merge candidate-4.35.0 into main...
PR location: https://github.com/certbot/certbot/pull/10714
Creating branch without version bump commit named 4.35.x...
Created.
Generating announcement text for community forum post
release not found
Generating announcement text failed.
$ git switch 4.35.x
Switched to branch '4.35.x'
$ RELEASE_GPG_KEY=[test key] tools/release.sh 4.35.1 4.36.0
[release output]
$ tools/finish_release.py --test-version 4.35.1 --skip-snaps
Creating PR to merge candidate-4.35.1 into main...
PR location: https://github.com/certbot/certbot/pull/10715
Creating branch without version bump commit named point-candidate-4.35.1...
Created.
Creating PR to merge point-candidate-4.35.1 into 4.35.x...
PR location: https://github.com/certbot/certbot/pull/10716
Generating announcement text for community forum post
release not found
Generating announcement text failed.
```
then here's some errors and their outputs --
trying to run `finish_release.py` again:
```
$ tools/finish_release.py --test-version 4.34.1 --skip-snaps
Creating PR to merge candidate-4.35.1 into main...
PR to merge release changes into main already exists...skipping creation. To create a new PR, delete the old one on GitHub.
PR location: https://github.com/certbot/certbot/pull/10715
Creating branch without version bump commit named point-candidate-4.35.1...
Error running `git branch point-candidate-4.35.1`
Branch point-candidate-4.35.1 already exists. Delete it using `git branch -D point-candidate-4.35.1`.
fatal: a branch named 'point-candidate-4.35.1' already exists
To skip pushing updated branches to GitHub and creating PRs, run this script with the `--skip-github-sync` flag.
Traceback (most recent call last):
File "/Users/erica/certbot/tools/finish_release.py", line 370, in <module>
main(sys.argv[1:])
~~~~^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 366, in main
synchronize_github_repo(version)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 329, in synchronize_github_repo
_create_and_push_branch_without_version_bump(version, branch_name)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 280, in _create_and_push_branch_without_version_bump
_run_silent_except_error(f'git branch {branch_name}'.split(), msg)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 209, in _run_silent_except_error
raise e
File "/Users/erica/certbot/tools/finish_release.py", line 201, in _run_silent_except_error
process = subprocess.run(cmd, check=True, universal_newlines=True, capture_output=True)
File "/Users/erica/.pyenv/versions/3.14.3/lib/python3.14/subprocess.py", line 577, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['git', 'branch', 'point-candidate-4.35.1']' returned non-zero exit status 128.
```
local changes to branch:
```
$ touch test_file.txt
$ git add -A
$ tools/finish_release.py --test-version 4.35.1 --skip-snaps
Error running `git diff --quiet HEAD`
You have uncommitted changes that will be deleted. Stash your changes before rerunning this script.
To skip pushing updated branches to GitHub and creating PRs, run this script with the `--skip-github-sync` flag.
Traceback (most recent call last):
File "/Users/erica/certbot/tools/finish_release.py", line 370, in <module>
main(sys.argv[1:])
~~~~^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 366, in main
synchronize_github_repo(version)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 315, in synchronize_github_repo
_run_silent_except_error('git diff --quiet HEAD'.split(), message)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 209, in _run_silent_except_error
raise e
File "/Users/erica/certbot/tools/finish_release.py", line 201, in _run_silent_except_error
process = subprocess.run(cmd, check=True, universal_newlines=True, capture_output=True)
File "/Users/erica/.pyenv/versions/3.14.3/lib/python3.14/subprocess.py", line 577, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['git', 'diff', '--quiet', 'HEAD']' returned non-zero exit status 1.
```
branch doesn't match the one on github (shows that stdout is now also
printed on error):
```
$ cd releases/le.4.35.1.89372/
$ git commit --amend # change the message
[candidate-4.35.1 8d34a67a4] Bump version to 4.36.0 new message
Date: Tue Jun 23 10:47:56 2026 -0700
20 files changed, 20 insertions(+), 20 deletions(-)
$ cd ../../
$ tools/finish_release.py --test-version 4.35.1 --skip-snaps
Error running `git push origin candidate-4.35.1`
To delete the branch on GitHub, run `git push origin --delete candidate-4.35.1`.
To https://github.com/certbot/certbot.git
! [rejected] candidate-4.35.1 -> candidate-4.35.1 (non-fast-forward)
error: failed to push some refs to 'https://github.com/certbot/certbot.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. If you want to integrate the remote changes,
hint: use 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
To skip pushing updated branches to GitHub and creating PRs, run this script with the `--skip-github-sync` flag.
Traceback (most recent call last):
File "/Users/erica/certbot/tools/finish_release.py", line 370, in <module>
main(sys.argv[1:])
~~~~^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 366, in main
synchronize_github_repo(version)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 317, in synchronize_github_repo
_sync_candidate_from_temp_to_origin(version)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 247, in _sync_candidate_from_temp_to_origin
_run_silent_except_error(command_str.split(), message)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/erica/certbot/tools/finish_release.py", line 209, in _run_silent_except_error
raise e
File "/Users/erica/certbot/tools/finish_release.py", line 201, in _run_silent_except_error
process = subprocess.run(cmd, check=True, universal_newlines=True, capture_output=True)
File "/Users/erica/.pyenv/versions/3.14.3/lib/python3.14/subprocess.py", line 577, in run
raise CalledProcessError(retcode, process.args,
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['git', 'push', 'origin', 'candidate-4.35.1']' returned non-zero exit status 1.
```
I've hit basically all of the errors with text at some point during
testing, but can recreate them if you'd like.
Once this is merged, I'll update the release instructions and delete the
test branches and PRs.
---------
Co-authored-by: Will Greenberg <ifnspifn@gmail.com>
Co-authored-by: Will Greenberg <willg@eff.org>
Item 5 of https://github.com/certbot/certbot/issues/10600
When this is merged, the setup section of the [release
process](https://github.com/EFForg/certbot-misc/wiki/The-Mystical-Release-Process)
should be modified.
- `gh` should be added to os packages for mac and debian
- A new step should be added: "Run `gh auth login` and log into a GitHub
account." Technically any account should work here.
The contents of the step saying to post to the community forum should be
shortened to say something like "copy the output from the terminal." We
could add the link to the client-dev tag here, but personally I think
it's easiest to just keep it in the release instructions.
in https://github.com/canonical/snapcraft/pull/5720, snapcraft made a
change. `snapcraft status certbot` output changed from something like
this:
```
Track Arch Channel Version Revision Progress
latest amd64 stable 5.1.0 5057 -
candidate ↑ ↑ -
beta 5.2.1 5214 -
edge 5.2.0.dev0 5210 -
arm64 stable 5.1.0 5058 -
candidate ↑ ↑ -
beta 5.2.1 5215 -
edge 5.2.0.dev0 5211 -
armhf stable 5.1.0 5056 -
candidate ↑ ↑ -
beta 5.2.1 5213 -
edge 5.2.0.dev0 5212 -
```
to this:
```
Track Arch Channel Version Revision Progress
latest amd64 stable 5.1.0 5057 -
latest amd64 candidate ↑ ↑ -
latest amd64 beta 5.2.1 5214 -
latest amd64 edge 5.2.0.dev0 5210 -
latest arm64 stable 5.1.0 5058 -
latest arm64 candidate ↑ ↑ -
latest arm64 beta 5.2.1 5215 -
latest arm64 edge 5.2.0.dev0 5211 -
latest armhf stable 5.1.0 5056 -
latest armhf candidate ↑ ↑ -
latest armhf beta 5.2.1 5213 -
latest armhf edge 5.2.0.dev0 5212 -
```
when its output is captured like it is in finish_release.py in the lines
above the code i'm modifying here
not matching on the beginning of lines makes this pattern a little less
strict, but based on the rest of the pattern and the output here, i
personally think this is fine
after carefully verifying this works with the current state of things, i
went ahead and finished the release with this change and it worked just
fine. instead, this PR proposes a way to fix things going forward
I want to use isort as part of https://github.com/certbot/certbot/issues/9572 because I want to do it programmatically, however, I felt like the config needed to be tweaked a bit due to it not understanding what is and is not our own code.
This PR updates the isort config so it recognizes our own modules and runs `isort .` from the root of the repo to update everything.
* update isort config
* run "isort ."
This PR:
* Deletes the 2.0 pre-release pipeline
* Causes 1.x releases to be released to Docker Hub without updating the latest tag, PyPI, and the candidate and stable channels of the snap store
* Causes 2.x releases to be released to Docker Hub, PyPI, the beta channel of the snap store, and our Windows installer
We could potentially look into how to continue to do 1.x Windows installer releases through GitHub releases and tech ops tooling, but I personally don't think it's worth it right now.
This PR DOES NOT do anything about progressive snap releases. I think we can revisit this when/if we decide (how) to do them.
* Add Code Signing action for Windows Installer
* Clean up variable names and input
* Amend and add to documentation per PR guidelines
* Update tools/finish_release.py
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
* Update tools/finish_release.py
Amend typo
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
* Amend release script for better work flow
- SCP commands to upload and download unsigned & signed installers from CSS
* Collapse spaces
* Update tools/finish_release.py
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
* Create new windows signer function
* Update Windows Installer Script
- Update change log
- add new function for signing and document
- @TODO Streammline SSH session
* Remove Azure and Github release methods
- Methods moved to CSS
- Reduced to a ssh function that triggers the process on a CSS
* Amend Chnagelog and Remove Unneeded Deps
* Update tools/finish_release.py
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
* Add Verison Fetch Function
- For the purpose of snap releases
- Add back package to dev extras for function
* Chaneg path in ssh command
* Amend release script
* Amend the ssh command for CSS
* Update tools/finish_release.py
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
* Update script with proper path and subprocess call
* Update ssh command
* Correct typo in path
* Fix typo in path
* Update certbot/CHANGELOG.md
Co-authored-by: ohemorange <ebportnoy@gmail.com>
* Remove missed conflict text
Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
Co-authored-by: ohemorange <ebportnoy@gmail.com>
* Remove Windows 2016 environment, generate 64 bit installer
* Add note to changelog
* Use win_amd64 as installer suffix
* Bump PYTHON_BITNESS to 64
* Require 64 bit Windows for the installer_build job
* Update certbot install path
* update windows test name
* Base installer suffix on PYTHON_BITNESS again
* Update changelog to request users uninstall old version
Fixes https://github.com/certbot/certbot/issues/8171.
See the comment at the top of the script to learn how to set things up and run this. Running the script between releases will have no effect on our snaps and it should fail when creating the GitHub release. The latter is described at https://github.com/certbot/certbot/pull/8189#discussion_r466707114.
* Rename create_github_release to finish_release
* Add initial version of snap release automation.
* Handle snapcraft login.
* Catch OSError raised when snapcraft doesn't exist.
* Update documentation.
* Only publish the Certbot snap for now.
* Fix typo.
* Document other exceptions.
* Document assertion
* Add status message before getting revisions.
* Publish all snaps.