Preface
Releases are a brittle and sensitive process. There are often a lot of steps involved:
- Update CHANGELOG
- Commit CHANGELOG (e.g.
git commit
) - Update semver in repository files (e.g.
package.json
,setup.py
) - Commit semver changes (e.g.
git commit
) - Tag release commit (e.g.
git tag 1.0.0
) - Push committed versions (e.g.
git push
) - Push committed tags (e.g.
git push --tags
) - Publish to repository (e.g.
npm publish
,python setup.py sdist --formats=gztar,zip upload
)
If we forget any step, then we might need to start all over. Or if we notice a typo in our documentation, we will need to perform another release.
As soon as we recognized this bottleneck, we started automating it as much as possible:
- Initially via an integration with
git-extras' release
command: - Then, with
foundry
as a plugin based release manager:
Release v4
The past revisions of foundry
have had a few problems which bugged me:
- Releases were not transparent (e.g. we didn't know what command would fail without looking at source code)
- If a release fails, then there is no easy way to resume (e.g. forgot to log in to
npm
, repository is randomly sending 500's) - Language exclusive (e.g. PyPI release command is written in JavaScript, not Python so we can't leverage its AST)
- If a local
foundry
isn't found, then the global one will be used automatically - No support for custom commands (e.g. we can't add a
./build.sh
when updating files without writing a plugin)
To resolve this, we took the following approach:
- Moved to CLI based commands rather than plugins
- Allows specification to be cross-language
- Opens avenue for writing custom commands in configuration
- Allows for transparent execution of each step since we own the CLI invocation
- Moved to deterministic release steps
- Allows for resuming failed releases
- Moved to current working directory
package.json
/.foundryrc
for release configuration- Removes possibility of running global
foundry
- Allows for customization of things like
registerVersion
(semver to run "register" steps at)
- Removes possibility of running global
Results
All of the proposed changes are now live in foundry
.
https://github.com/twolfson/foundry/tree/4.3.2
We revamped foundry-release-spec
to be a CLI specification:
- Before: https://github.com/twolfson/foundry-release-spec/tree/1.1.0
- After: https://github.com/twolfson/foundry-release-spec/tree/2.0.0
We wrote foundry-release-base
to migrate our plugins to command integrations:
https://github.com/twolfson/foundry-release-base/tree/1.0.2
We updated foundry release
to generate a foundry-resume.json
upon failure. This allows for usage of foundry resume
which picks up the release from where it last failed.
We added support for customCommand
as part of our releaseCommands
. This allows for custom one-off commands (e.g. updateFiles: 'npm run build'
).
Lastly, we added more transparent output so we always know exactly where we are in our release:
The full list of our thoughts and changes can be found here: