Skip to content
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

Improve & update release process to reflect recent changes #3242

Merged
merged 6 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 158 additions & 85 deletions docs/contributing/release_process.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,88 @@
# Release process

_Black_ has had a lot of work automating its release process. This document sets out to
explain what everything does and how to release _Black_ using said automation.

## Cutting a Release

To cut a release, you must be a _Black_ maintainer with `GitHub Release` creation
access. Using this access, the release process is:

1. Cut a new PR editing `CHANGES.md` and the docs to version the latest changes
_Black_ has had a lot of work done into standardizing and automating its release
process. This document sets out to explain what everything works and how to release
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
_Black_ using said automation.

## Release cadence

**We aim to release whatever is on `main` every 1-2 months.** This ensures merged
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

1-2 months felt like the most reasonable given the current pace of Black's development, but I'm open to alternatives.

improvements and bugfixes are shipped to users reasonably quickly, while not massively
fracturing the user-base with too many versions. This also keeps the workload on
maintainers consistent and predictable.

If there's not much new on `main` to justify a release, it's acceptable to skip a
month's release. Although, ideally January releases should not be skipped because as per
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not entirely sure if we want to formalise this, but I feel like sometimes our development pace gets slow enough where releasing monthly stops being valuable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this is fine. We shouldn't bind ourselves to a particular cadence, but this text gives users a reasonable view into what they can expect.

ichard26 marked this conversation as resolved.
Show resolved Hide resolved
our [stability policy](labels/stability-policy), the first release in a new calendar
year may make changes to the _stable_ style. While the policy applies to the first
release (instead of only January releases), confining changes to the stable style to
January will keep things predictable (and nicer) for users.

Unless there is a serious regression or bug that requires immediate patching, **there
should not be more than one release per month**. While version numbers are cheap,
releases require a maintainer to both commit to do the actual cutting of a release, but
also to be able to deal with the potential fallout post-release. Releasing more
frequently than monthly nets rapidly diminishing returns.

## Cutting a release

**You must have `write` permissions for the _Black_ repository to cut a release.**

The 10,000 foot view of the release process is that you publish a [GitHub Release] which
then triggers release automation that builds all release artifacts and publishes them to
the various platforms we publish to.

To cut a release:

1. Determine the release's version number
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried to make this as easy as possible to follow while being thorough, but I might have made this too explicit and made the release process look scarier than it really is. I'd appreciate thoughts on it

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's fine to be verbose here - But we could make a quick release guide and link to more info? But yeah I'm open to either.

- **_Black_ follows the [CalVar] versioning standard using the `YY.M.N` format**
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
- So unless there already has been a release during this month, `N` should be `0`
- Example: the first release in January, 2022 → `22.1.0`
- Example 2: the third release (oh dear) in March, 2023 → `23.3.2`
1. File a PR editing `CHANGES.md` and the docs to version the latest changes
1. Replace the `## Unreleased` header with the version number
1. Remove any empty sections for the current release
2. Add a new empty template for the next release (template below)
3. Example PR: [#2616](https://github.com/psf/black/pull/2616)
4. Example title: `Update CHANGES.md for XX.X release`
2. Once the release PR is merged ensure all CI passes
1. If not, ensure there is an Issue open for the cause of failing CI (generally we'd
want this fixed before cutting a release)
3. Open `CHANGES.md` and copy the _raw markdown_ of the latest changes to use in the
description of the GitHub Release.
4. Go and [cut a release](https://github.com/psf/black/releases) using the GitHub UI so
that all workflows noted below are triggered.
1. The release version and tag should be the [CalVer](https://calver.org) version
_Black_ used for the current release e.g. `21.6` / `21.5b1`
2. _Black_ uses [setuptools scm](https://pypi.org/project/setuptools-scm/) to pull
the current version for the package builds and release.
5. Once the release is cut, you're basically done. It's a good practice to go and watch
to make sure all the [GitHub Actions](https://github.com/psf/black/actions) pass,
although you should receive an email to your registered GitHub email address should
one fail.
1. You should see all the release workflows and lint/unittests workflows running on
the new tag in the Actions UI

If anything fails, please go read the respective action's log output and configuration
file to reverse engineer your way to a fix/soluton.

## Changelog template
1. (_optional_) Read through and copy-edit the changelog (eg. by moving entries,
fixing typos, or rephrasing entries)
1. Add a new empty template for the next release above
([template below](#changelog-template))
1. Update references to the latest version in
{doc}`/integrations/source_version_control` and
{doc}`/usage_and_configuration/the_basics`
- Example PR: [GH-3139]
1. Once the release PR is merged, wait until all CI passes
- If CI does not pass, **stop** and investigate the failure(s) as generally we'd want
to fix failing CI before cutting a release
1. Open `CHANGES.md` and copy the _raw Markdown_ of the latest changes. You'll use it as
the description of the GitHub Release.
- Don't copy the version header as it's redundant
1. [Draft a new GitHub Release][new-release]
1. Click `Choose a tag` and type in the version number, then select the
`Create new tag: YY.M.N on publish` option that appears
1. Verify the new tag targets the `main` branch
1. You can leave the release title blank, GitHub will default to the tag name
1. Paste the changelog Markdown you copied earlier into the description box
1. Publish the GitHub Release you just drafted
- On publication, the release automation will trigger and run on the new tag
1. At this point, you're basically done. It's good practice to go and watch to make sure
all the [release workflows][black-actions] pass, although you will receive a GitHub
notification if something does fail.
- If something fails, don't panic. Please go read the respective workflow's logs and
configuration file to reverse-engineer your way to a fix/solution.

Congratulations! You've successfully cut a new release of _Black_. Go and stand up and
take a break, you deserve it.

```{important}
Once the release artifacts reach PyPI, you may see new issues being filed indicating
regressions. While regressions are not great, they don't automatically mean a hotfix
release is warranted. Unless the regressions are serious and impact many users, a hotfix
release is probably unnecessary.

In the end, use your best judgement and ask other maintainers for their thoughts.
```

### Changelog template
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In general I reordered the sections to what a regular user would find the most useful. Integrations got moved down because they aren't particularly relevant to most users. Packaging/parser changes on the other hand could probably either affect a lot of people and/or would be quite "newsworthy" (like supporting new syntax).

Oh and docs really do not need the attention its fourth place gave it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

very nice 👍🏿


Use the following template for a clean changelog after the release:

Expand All @@ -45,101 +93,126 @@ Use the following template for a clean changelog after the release:

<!-- Include any especially major or disruptive changes here -->

### Style
### Stable style
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thought it was worth making the distinction even clearer.


<!-- Changes that affect Black's stable style -->

### Preview style

<!-- Changes that affect Black's preview style -->

### _Blackd_

<!-- Changes to blackd -->

### Configuration

<!-- Changes to how Black can be configured -->

### Documentation
### Packaging

<!-- Major changes to documentation and policies. Small docs changes
don't need a changelog entry. -->
<!-- Changes to how Black is packaged, such as dependency requirements -->

### Integrations
### Parser

<!-- For example, Docker, GitHub Actions, pre-commit, editors -->
<!-- Changes to the parser or to version autodetection -->

### Performance

<!-- Changes that improve Black's performance. -->

### Output

<!-- Changes to Black's terminal output and error messages -->

### Packaging

<!-- Changes to how Black is packaged, such as dependency requirements -->
### _Blackd_

### Parser
<!-- Changes to blackd -->

<!-- Changes to the parser or to version autodetection -->
### Integrations

### Performance
<!-- For example, Docker, GitHub Actions, pre-commit, editors -->

<!-- Changes that improve Black's performance. -->
### Documentation

<!-- Major changes to documentation and policies. Small docs changes
don't need a changelog entry. -->
```

## Release workflows

All _Blacks_'s automation workflows use GitHub Actions. All workflows are therefore
configured using `.yml` files in the `.github/workflows` directory of the _Black_
All of _Black_'s release automation use [GitHub Actions]. All workflows are therefore
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
configured using YAML files in the `.github/workflows` directory of the _Black_
repository.

They are triggered by the publication of a [GitHub Release].

Below are descriptions of our release workflows.

### Docker
### Publish to PyPI

This workflow uses the QEMU powered `buildx` feature of docker to upload a `arm64` and
`amd64`/`x86_64` build of the official _Black_ docker image™.
This is our main workhorse workflow. It builds a [sdist] and [wheels] to upload to PyPI
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
where the vast majority of users will download Black from. It's divided into three job
groups:

- Currently this workflow uses an API Token associated with @cooperlees account
#### sdist + pure wheel

### pypi_upload
This single job builds the sdist and pure Python wheel (ie. a wheel that only contains
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
Python code) using [build]. These artifacts are general-purpose and can be used on
basically any platform supported by Python.

This workflow builds a Python
[sdist](https://docs.python.org/3/distutils/sourcedist.html) and
[wheel](https://pythonwheels.com) using the latest
[setuptools](https://pypi.org/project/setuptools/) and
[wheel](https://pypi.org/project/wheel/) modules.
#### mypyc wheels (…)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the ellipsis for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Supposed to be a placeholder for the names the matrix jobs will have in practice.
image

I'm happy to remove them if you find them confusing.


It will then use [twine](https://pypi.org/project/twine/) to upload both release formats
to PyPI for general downloading of the _Black_ Python package. This is where
[pip](https://pypi.org/project/pip/) looks by default.
We use [mypyc] to compile _Black_ into a CPython C Extension for significantly improved
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
performance. Wheels built with mypyc are platform and Python version specific.
[Supported platforms are documented in the FAQ](labels/mypyc-support).

- Currently this workflow uses an API token associated with @ambv's PyPI account
These matrix jobs use [cibuildwheel] which handles the complicated task of building C
extensions for many environments for us. Since building these wheels is slow, there are
multiple mypyc wheels jobs (hence the term "matrix") that build for a specific platform
(noted in the job name in parentheses).

### Upload self-contained binaries
...
ichard26 marked this conversation as resolved.
Show resolved Hide resolved

This workflow builds self-contained binaries for multiple platforms. This allows people
to download the executable for their platform and run _Black_ without a
[Python Runtime](https://wiki.python.org/moin/PythonImplementations) installed.
These jobs then use [twine] to upload the built distributions to PyPI.

The created binaries are attached/stored on the associated
[GitHub Release](https://github.com/psf/black/releases) for download over _IPv4 only_
(GitHub still does not have IPv6 access 😢).
#### Update stable branch

## Moving the `stable` tag
So this job doesn't _really_ belong here, but updating the `stable` branch after the
other PyPI jobs pass (they must pass for this job to start) makes the most sense. This
saves us from remembering to update the branch sometime after cutting the release.

_Black_ provides a stable tag for people who want to move along as _Black_ developers
deem the newest version reliable. Here the _Black_ developers will move once the release
has been problem free for at least ~24 hours from release. Given the large _Black_
userbase we hear about bad bugs quickly. We do strive to continually improve our CI too.
- _Currently this workflow uses an API token associated with @ambv's PyPI account_

### Tag moving process
### Publish executables

#### stable
This workflow builds native executable for multiple platforms using [PyInstaller]. This
allows people to download the executable for their platform and run _Black_ without a
[Python runtime](https://wiki.python.org/moin/PythonImplementations) installed.

From a rebased `main` checkout:
The created binaries are attached/stored on the associated GitHub Release for download
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
over _IPv4 only_ (GitHub still does not have IPv6 access 😢).

### docker

This workflow uses the QEMU powered `buildx` feature of Docker to upload a `arm64` and
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
`amd64`/`x86_64` build of the official _Black_ Docker image™.

- _Currently this workflow uses an API Token associated with @cooperlees account_
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't know if it's worth noting, but Ee from PSF also has access should I disappear.

Copy link
Collaborator Author

@ichard26 ichard26 Aug 31, 2022

Choose a reason for hiding this comment

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

I'm pretty sure that Ee is our contact should we lose access to any of the platforms we use haha. They control the psf GitHub organisation, and are an admin of PyPI and the pyfound dockerhub organisation. It's probably not worth noting IMO.


```{note}
This also runs on each push to `main`.
```

1. `git tag -f stable VERSION_TAG`
1. e.g. `git tag -f stable 21.5b1`
1. `git push --tags -f`
[black-actions]: https://github.com/psf/black/actions
[build]: https://pypa-build.readthedocs.io/
[calvar]: https://calver.org
ichard26 marked this conversation as resolved.
Show resolved Hide resolved
[cibuildwheel]: https://cibuildwheel.readthedocs.io/
[gh-3139]: https://github.com/psf/black/pull/3139
[github actions]: https://github.com/features/actions
[github release]: https://github.com/psf/black/releases
[new-release]: https://github.com/psf/black/releases/new
[mypyc]: https://mypyc.readthedocs.io/
[mypyc-platform-support]:
/faq.html#what-is-compiled-yes-no-all-about-in-the-version-output
[pyinstaller]: https://www.pyinstaller.org/
[sdist]:
https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist
[twine]: https://github.com/features/actions
[wheels]: https://packaging.python.org/en/latest/glossary/#term-Wheel
2 changes: 2 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ errors is not a goal. It can format all code accepted by CPython (if you find an
where that doesn't hold, please report a bug!), but it may also format some code that
CPython doesn't accept.

(labels/mypyc-support)=

## What is `compiled: yes/no` all about in the version output?

While _Black_ is indeed a pure Python project, we use [mypyc] to compile _Black_ into a
Expand Down
2 changes: 2 additions & 0 deletions docs/the_black_code_style/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ style aspects and details might change according to the stability policy present
below. Ongoing style considerations are tracked on GitHub with the
[design](https://github.com/psf/black/labels/T%3A%20design) issue label.

(labels/stability-policy)=

## Stability Policy

The following policy applies for the _Black_ code style, in non pre-release versions of
Expand Down