How to fuck up software releases
drewdevault.com
drewdevault.com
def git_tag():
print("1. git tag the repository")
input("Press enter to continue")
def git_push():
print("2. git push the repository")
input("Press enter to continue")
def pip_deploy():
print("3. Deploy, run: python setup.py sdist upload")
input("Press enter to continue")
def main():
git_tag()
git_push()
pip_deploy()
As simple as that! Then later, you could write code for every step as you have time.https://blog.danslimmon.com/2019/07/15/do-nothing-scripting-...
The setup.py upload command is actually deprecated and has been replaced with "twine upload".
https://setuptools.readthedocs.io/en/latest/setuptools.html#... https://pypi.org/project/twine/
https://github.com/braintree/runbook
One of our biggest use cases is for including ample sanity checks as part of our releases to make sure we don't mess it up.
The rakudo project has monthly releases with tags like 2019.07. Once I mixed up the year and month in e release, so I created a tag 2018.06 instead of 2016.08 (don't remember the exact numbers), and pushed it.
I deleted the wrong tag as soon as I noticed, and pushed the correct one, but of course when the date of the typo'ed release month came around some years later, things blew up for everybody who still had the wrong tag in their local repo (which turned out to be quite some developers).
That was until the version overflowed the 16 bit field it was stored in the assembly manifest. Kaboom. :(
People spend way too much time arguing with semantic versioning when 99% of the time the only important thing is the next number is bigger than the previous number.
{mostRecentAnnotatedTag}-{numberOfCommitsAfter}-g{headCommitShortHash}
Such as: v2.0.3-3-g1cafe9That has enough information to get you back to exactly which branch was built (3 commits after v2.0.3 with a hash starting 1cafe9). Of course if you git describe when you've checked out the tagged commit itself you just get the tag back v2.0.3.
It's not as simple as simple "revision ID", but it is pretty simple.
In some of my CI pipelines I've been considering swapping my simple `git describe` tricks for `git-version` to make it easier to get more semver-like build metadata in order to CD them, but I've also considered just regexing that first hyphen to a plus and calling it a day.
It roughly amounts to:
1) commit fix (git commit -m "fixes #foo bug"
2) get hash/shortlog, add to changelog
3) commit changelog referencing commit in 1)
4) tag hash in 3) with new minor version 1.1.2 to 1.1.3
5) realize you forgot that 1) warranted a bump in minor version, edit changelog to reflect new minor version
6) commit new changelog
7) move tag from 4) to 6) - hope you didn't push yet...
And people say RCS $Id:$ was a hack...
Now,the real issue is actually to find a nice way to tag actual releases, with current tag and hash. I realize this is mostly tricky when a project is "checkout and run" - without a build step (eg ruby on rails project). It's still somewhat painful to make sure there's an up to date global variable that correctly reflects the running version.
Git has so many footguns it's unreal.
Two smart git strategies mitigate the problems: don't push things, and don't pull things.
But this one is a footgun only in the sense that you may mess up things for others, and embarrass yourself. Not in the sense of "permanent data loss", right?
"don't pull things" is definitely bad advice (even if it was ironic) - you don't mess up anything by pulling things; you can easily go back to how things were.
Instead of downvotes, maybe clarify how you mess up things with git? Maybe I can help. I really find it very hard to mess things up. Like, how do you mess up pushing the wrong tag? Unless you force-push it, but why would you do that? Or you mean, you've tagged something wrong? Where's the (permanent) harm in that? I mean, I can imagine "harm" in the sense of "inconvenience", but nothing more serious, unless the mistake goes unnoticed for a very long while (and then, how would any other version control system help you?)
In my experience the difference between my trouble-free use of git and inexperienced users' troubles with git amount to: I avoid doing the wrong thing in the first place, inexperienced users do the wrong thing, then try to fix it.
For example, I just never commit a gigabyte binary file to the repo. But if an inexperienced user does by mistake, and that makes .git/objects/pack big and everyone's checkouts become slow? And then they notice a few days later, after other developers have pulled and pushed other changes? And they try to fix it? Disaster zone.
Pushing messed-up tags can lead to really annoying consequences because tags are a global namespace (except when they aren't), but this comes up much less frequently, especially since tagging a release is a bit ceremonial.
Don't pull things. This is not "ironic" (what is that supposed to mean?) and it will avoid the other side of git footguns. Pulling is a maintainer's shortcut operation (i.e., Linus Torvalds or one of the subsystem vice-Linuxes) and messes up history forever if used on public targets.
I go back and forth depending on mood on what the right thing to do about git's footguns is, but you were right that there's no way to permanently screw up a git repository - if you don't push things and don't pull things. (Seriously, don't! When you publish things, publish them.)
I thought you were being ironic, in the sense of "if you only use git locally you don't mess stuff up". You definitely pull the shared branches; you just don't force-push to them, and that makes it ok.
> Seriously, don't! When you publish things, publish them.
By "publish" I suppose you mean "push, when there is no upstream branch"? Well, yes, but I also force-push a lot and it's quite ok to do so when you work alone on a branch (e.g. to keep it constantly rebased on top of master). I wouldn't say "don't push, ever" - that's an overkill. I'd just say "protect you master branch so that people can't force-push on it".
What does that mean operationally?
Especially when you're the type of person who always seems to have typos after you proof read something a few times. But then when you proof it again AFTER you post it, suddenly they all jump out. I can't count the number of times I've gone and edited a HN comment even after proof reading it a few times.
Needless to say dealing with tags or writing a tweet often includes a silly amount of triple checking.
Over time that has grown into over 1000 lines of shell, e.g.
https://github.com/oilshell/oil/blob/master/devtools/release...
And this is why I'm writing a shell -- because it's good for automating things that you are otherwise too lazy to automate!
I find that "semi-automation" is a good middle ground. This is where shell is better than "real" languages. You don't have to commit to writing a polished program -- you just incrementally improve things and it makes problems go away.
The time you put in is saved immediately, as opposed to other languages, where you might spent 30 minutes writing something that will save you 2 minutes in the next year, which isn't necessarily worth it.
If you keep a text file with a bunch of terminal commands, then you might as well chmod +x it and put #!/bin/sh on the front. That's what a shell script is!
----
Here is the output of the release process, which has tarballs, change logs, documentation, test results, benchmarks, etc.
https://www.oilshell.org/release/0.7.pre5/
It's way to much to do by hand.
I’d love to take a look at your list
- Finalize source
- Legal check `LICENSE-dist.txt`
- Update `CHANGELOG.md`
- Bump version in `Makefile` and `Core.json`
- Commit "Bump version".
- Build (three OS's can be done in parallel)
- `m clean`
- `git pull`
- Make sure you have the latest `Fundamental.zip` package in source root.
- `m dist`
- Manually test installer and fragile features (audio drivers, patch loading) for ~10 minutes.
- `m notarize` (on Mac)
- `m upload`
- `git tag vX.Y.Z`
- `git push --tags`
- Release
- Update version title and URLs in `Rack.pug`
- `m upload`
- At this point, normal users have access to new version.
- Update server version in `config.coffee`
- `m restart`
- At this point, normal users will swarm to download new version. Keep an eye on server bandwidth.
- Publicize
- Twitter https://twitter.com/home
- Facebook https://www.facebook.com/vcvrack/
- Share on group
- Forum https://community.vcvrack.com/c/announcementsManual releasing allows me to iteratively improve the process and spot possible issues, whereas any automation would reduce build reliability.
I guess this depends on what your release process is; for example, most npm libraries benefit from CI that runs the build script and runs `npm version $TAG && npm publish`. There's not much that could be improved here, especially if the build script is just an alias for running `webpack`.
That's a hefty deploy protocol, though, and automating cross-platform builds may be more trouble than it's worth if you're releasing rarely.
It's pretty neat you follow a deploy checklist.
input("Build X")
input("Build Y")
input("Zip X & Y")
input("Upload release")
THen run the script and press enter as you do the steps. It means if you're interrupted as your proceed, you can resume exacly where you were before.If you implement it as functions:
def buildX():
input("Build X")
def buildY():
input("Build Y")
def ZipUp():
input("Zip X & Y")
def Upload():
input("Upload release")
if __name__ == '__main__':
buildX()
buildY()
ZipUp()
Upload()
Then you can automate parts piecemeal.Then those steps must be removed. I thought the mantra "deploy is ONE step" was a more or less universally acknowledged truth.
So it is clearly possible to do -- and there are all sorts of tools which figure out what SPDX license entries apply for every dependency (or vendored dependency) of a given project.
For the latter though, Fossology, Scancode etc. can help.
In a larger team you’d have a small agent on each of your N (likely >> 3) machines, CI pushes to the agent for build/release/automated tests.
Then if those pass on a given system, it fires off a message to start manual QA validation for performance and other “intangibles”.
true. And that is a very sad state of affairs. I use the travis osx hosts for that, but it's not ideal. There's no interface, but at least you can check that the code compiles, runs, and passes automated tests. That's already huge!
For linux and windows hosts, it seems to me that it is a solved problem, as pointed elsewhere.
It's not, as long as you run it on genuine Apple hardware. We use a bunch of macOS VMs that are 100% legal.
The problem with macOS VMs is that there are a lot of compatibility issues, and in my experience it's a lot of effort to set up macOS VMs. If you have the space and the money, real machines are a lot easier to deal with.
Launching a VM via shell script is trivial. And you can install OpenSSH on Windows 10: https://github.com/PowerShell/Win32-OpenSSH
Our main build machine is an actual Mac, because it's just so much easier to keep it running.
VMs are nice if you need a lot of different setups (eg. one app we distribute has components that need to be built on different versions of macOS, and VMs are nice for that).
I wanted to give you an example, but it all seems so easy, I'm not sure where you're getting stuck.
If not fully automated, many CI platforms allow you to pause the pipeline by requiring manual confirmation from an authorized user. I know Jenkins and CircleCI support this off the top of my head. You could have the CI perform any relevant searches or diffs, then display that to the user to get manual confirmation. It’s still not “perfect”, but it does allow you to reliably get someone to look at the data and say “yes” or “no.”
1) Automate your releases and run that on a CI server if you can. "It works on my laptop" is not ideal for releasing stuff.
2) Have a release branch, and only allow stuff to be merged that passes your CI tests and trigger the automation under step 1 after a merge to your release branch. This works for continuous deployment but can also work for releases of other stuff. If you use semantic versioning, the CI server should tag the release and publish the artifacts. That's one less thing that can go wrong. If you forgot to bump the version number on your master branch, the build should fail.
3) Have a release checklist. People forget stuff and having a small list of "Are the release notes and readme updated? Is our CI not complaining? Have all PRs been merged? Etc. Even with the above, premature releases through an early merge to your release branch could happen. A checklist can prevent that. And of course, if you can integrate those checklists into your CI. If your release notes are unchanged since the last version
4) Release often. Small deltas are less work to test and less risky to impose on users and you get feedback on stuff you did earlier.
- You discover more bugs with your shins than your foresight
- As long as you keep fixing it in code, you will eventually win - it's like a video game, as long as you can respawn, you will always defeat the game. Just keep buggering on.
That’s a very honest admission, and one I think lots of “why don’t you automate everything”-crowd seems to fail to recognise as a real-world factor.
Sometimes it feels like we're throwing out good old proper thinking and proper work in order to blame automation, that the results is a natural outcome of all the complexity or that we're pressed for time. Sometimes it's just a matter of taking responsibility and doing a good job.
I have spent many days playing whack-a-mole with issues and hacking out solutions. What is equally great is the community sharing their gotchas and how they solved them.
A wholesome and organic thread, thanks for taking the time to write it!
On a serious note, I have found that the only way to not fuck up is to remove as much human interaction from the release process as possible, which means scripting. Even with scripting, if the inputs are bad, the output will also be bad.
It's just inconsiderate. Things can wait until Monday.
Obviously don't do that if you don't have to, but sometimes it's actually the best time to deploy stuff.
When developers aren't (or are prevented from) improving their own environment, that's a bad sign...
1. `bumpversion` is more general and has more functionality than `semver` in the article 2. `bump2version` is the currently maintained version[0]
I currently use `bumpversion` to manage versioning in applications with many different versioning schemes as well as managing the versioning of their deployment environments (such as updating Terraform files, etc).
e.g. - the latest RC of a project I maintain is L10-14 on https://opendev.org/openstack/releases/src/branch/master/del...
by committing that file, the tooling chooses the right commit, does a tag, and builds the python tarballs, and puts them on releases.openstack.org, and for the client libraries, it pushes them to pypi.
it avoids a lot of the footguns of special scripts in repos, while allowing an easy release process.
This should have been done from the start.
GitHub does not guarantee checksums for the generated source archives to be stable, so they can change when GitHub updates their software (and yes, this has happened).
https://github.com/warner/python-versioneer
It means you only specify the version as a git tag, and all the other things that need a version number get it from that.
"How to fuck up software releases"
http://www.catb.org/esr/shipper/
And some blog posts he's written on it: