beets
beets copied to clipboard
Add a workflow for easily creating releases
I humbly present a solution our lack of releases: a workflow that can be triggered to automatically create one. This workflow builds the project, creates a GitHub release, and publishes beets to PyPi, for a one-stop solution.
@sampsyo this would make it much easier to create releases, as it requires only one little interaction: going to the actions tab and entering a version number. Once that's done, the workflow should take care of the rest.
I have only tested the build
job so far, since I can't do anything about the pypi or do a release just to test, but the code is lifted from other similar actions and should work fine.
It also requires one piece of setup. This is that PyPi must be set up with a trusted publisher to receive the new package. Once that's done, the process should go off automatically.
For testing the actual push to pypi you could use test.pypi.org
Another idea for the manual intervention step: the workflow could be triggered by something like
on: tag-push
fixme: let me give you the exact syntax later today....on the go atm
like that also a human could decide for the version number
It could, but that would require exactly the same amount of intervention. I can make it trigger the same thing on a tag push to the repository but this workflow has the same amount of user input and does the tag. It's just a choice of whether to do it through git or the GitHub interface.
My goal is to make releases a one-click affair. Ultimately that's on @sampsyo as to what he finds easier: pushing a tag or running an action.
You are right, it's actually a matter of taste. Just wanted to mention as often triggering things via git tags is what I see other projects do and also what I use myself in other projects.
A second topic to think of at this point is permissions. Who should hit that release button? Or push that tag?
A second topic to think of at this point is permissions. Who should hit that release button? Or push that tag?
But let me pull back here a little, maybe that's not of too much importance atm. I think my mind was going more of what happens if you push a faulty release to pypi for some reason. But also that's not the end of the world. Worst case is you push a broken version x.x and, thanks to everything is automated, quickly are able to push a bugfix release x.x.1 afterwards :-)
So back to the actual topic...
What do you think of including bumping version in files to that workflow? Sphinx doc version, setup.py and wherever else beets version needs to be increased.
The script in extra/release.py did all kinds of stuff to prep a release. We could brainstorm which stuff of that is absolutely required and what could be left out.
We can run that script as part of the workflow easily enough. I can integrate it in.
Ah, I see that it makes a GitHub release itself. We can strip it down to the bare minimum of changing the version numbers, and then add a new commit as part of the jobs.
Ah, I see that it makes a GitHub release itself. We can strip it down to the bare minimum of changing the version numbers, and then add a new commit as part of the jobs.
This script seems to be a very handy tool - I use something like that but not so sophisticated in my own projects where frequent releases are required. Basically what I do - and I call it "half automation", is:
- bump the version with a shellscript that basically uses a tool named `bump2version", which increases version in all relevant files and then commits it to git and adds a git-tag locally.
- I then push the tag to githu, which triggers a workflow that creates a draft for a new release
- I edit my changelog in that draft-release manually (also using the "what's new" button which gives my all the latest PR's as a checklist already) and set the release to "publish"
- As a final step I run another shellscript that creates a wheel and pushes to pypi.
Now looking at that script we have in beets, it does something very similar, but in a much more sophisticated way, and also has some more features (for example translating our changelog written in ReST to markdown, probably so it can be used as the changelog text within the actual github release)
It's a python cli-tool using the Click framework. The subcommands it provides give a clue of what it does exactly:
./release.py --help
Usage: release.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
bump Bump the version number.
changelog Get the most recent version's changelog as Markdown.
datestamp Enter today's date as the release date in the changelog.
ghrelease Create a GitHub release using the `github-release`...
prep Run all steps to prepare a release.
publish Unleash a release unto the world.
version Display the current version.
We might not even have to strip down that script but since it can run "single tasks" we could try to incorporate only some of those into the gh-actions workflow.
The thing is: Is it easily possible to run this program within a gh-actions workflow? You have experience with that @Serene-Arc ?
If you think it's easily possible to run some of those subcommands in our gh-workflow, I think the next step should be to brainstorm which of those things listed under "Commands:" are absolutely required for our needs.
Ok, so to summarise: What in my opinion is required for a release and what the workflow you provide supports so far (and please do correct me if I'm wrong!):
- [x] 1. Graphical way to input desired release version
- [x] 2. Set version in project files and manifest changes as a commit
- [x] 3. Create a git tag with the version
- [x] 4. Create a GitHub release from that tag
- [x] 5. Add the latest changelog to that GitHub release
- [x] 6. Build the package
- [x] 7. Publish the package to PyPi
- Add the latest changelog to that GitHub release
extra/release.py
does a lot of crazy stuff to make that happen! Do we really need that? A very simple way of adding the release notes to that auto-generated GitHub release could be to just simply state
beets version x.x.x was released on $timestamp, find all the details in "link to docs/changelog.rest"
Wouldn't that be sufficient? Stuff like that can be easily done with gh-actions already. No crazy magic required. For example the release action I used once supports a body
statement which could als be filles with version number, timestamp and so on: https://github.com/JOJ0/synadm/blob/382664db21a1776b6e39e5a82cc646cba6a7362c/.github/workflows/release.yaml#L78-L94
Awesome!!! I love the idea of having this be as automated as possible. The high-level vision would be (a) move release.py
into GitHub actions, so it's as easy as possible to trigger, and (b) giving up on all manual steps, such as manual changelog cleanup, which was nice to do when the project was smaller but is no longer really sustainable.
@JOJ0, you mention the issue with GitHub releases. I have lovingly crafted a script to fill out a Markdown changelog for exactly this purpose, as you noted. But that is probably not worth it. We could keep the messy conversion thing just for fun or throw it out and use the simple "link to the docs" approach you propose.
I think the best option would be to strip the release script we currently have down to just changing the version numbers, then use that in an action that also commits it. So it would still only be triggered from the action.
We can also use the markdown conversion in the script automatically and put it into the release. That really isn't a difficult thing. We can capture the output and add it to the release information extremely easily.
I've changed the workflow and the script so that, hopefully, it changes the version numbers, commits it, and then uses the output to use the changelog in the release.
I'm a little perplexed how to test this. Maybe running it on my own fork would work for most of it? That would check everything except the pushing to pypi and the release I think
Right, well I just spent the last few hours testing this action and it works, but there is one issue with the changelog @sampsyo @JOJ0 . I tried dynamically including the changelog in the release but it just wasn't working. If you really want I can keep troubleshooting, but right now it's a hard coded link to the changelog in the docs.
Right now, I just need to modify the script so that it changes the changelog again so that the latest release version and date is added as a header. Once that is done, the workflow should be finished.
I've changed the workflow and the script so that, hopefully, it changes the version numbers, commits it, and then uses the output to use the changelog in the release.
I'm a little perplexed how to test this. Maybe running it on my own fork would work for most of it? That would check everything except the pushing to pypi and the release I think
Awesome work you are doing here, it's just so so cool to finally see a future with realeases! Thanks so much! Love it!
I have one idea for testing: Yes do everything on your own fork and then for testing add one more commit where you change the actual pushing to pypi, to test.pypi.org - that could work, given that the pypi action you are using can be configured to push to that one instead of "real" pypi.org. HTH
Right, well I just spent the last few hours testing this action and it works, but there is one issue with the changelog @sampsyo @JOJ0 . I tried dynamically including the changelog in the release but it just wasn't working. If you really want I can keep troubleshooting, but right now it's a hard coded link to the changelog in the docs.
Right now, I just need to modify the script so that it changes the changelog again so that the latest release version and date is added as a header. Once that is done, the workflow should be finished.
As already mentionend I think having a super-nice changelog within the realease would be a "nice to have feature", not a "must have feature". So if you ask me, screw this for now and go the easy way. If someone wants to play around with this later on, they can do it. Let's focus on a working solution for now. Tidying up later :-)
Agreed with @JOJ0 here: awesome that this works at all! IMO, we can move ahead with the simple "see elsewhere" changelogs on GitHub and revisit populating those in the future (in some other PR).
@JOJ0 I have completely tested everything, and it all works, bar the Pypi one, which is copied line for line from their docs, so I don't doubt that it's fine. Once @sampsyo sets up the trusted publisher stuff, it'll work.
Do we want the script to do anything to the changelog? Like add a header to the changelog with the version and the release data, similar to how it is formatted now?
I think if you name the release title something like beets 1.6.0 (2021-01-01)
it will show up similar to what we had here: https://github.com/beetbox/beets/releases/tag/v1.6.0
But actually you could even spare including the date with the title, it's sitting right next to the title in the releases list anyway: https://github.com/beetbox/beets/releases as well as in the actual release view.
For the text, actually just the link to the changelog, something like Find out what's new in [the changleog]<link to changelog>
I've done that, the releases are fine. I mean changes to the changelog file. What do we want to do for that? Are there changes that need to be made? Do we need to rename the most recent section?
Ah gotcha! Sorry, I misunderstood, so you mean "the changelog" changelog ;-)
Ok so what do we have here....
Hmmm could be a tricky task.
- Removal of
(in development)
- The line
Changelog goes here...
should actually stay and below that a new changelog starts.... - Then preparing for the next version:
- A h1 heading with the next version number, but we might not know it at this point, maybe just make the top headline always the same:
In development
- then the basic lists "major features", "features", "bug fixes" would need to be created.
- A h1 heading with the next version number, but we might not know it at this point, maybe just make the top headline always the same:
Hmm I am really not sure if this all can be automated like this. We might have to simplify how this changelog looks in the future. For example those "how to and where to write my line things" should maybe go into a tutorial/the docs. somewhere else. And the changelog is consisting purely "of data" - descriptions somewhere else.
Sidenote:
I am not sure if 1.6.1 should be the next version number. I think we could skip that and make one (maybe still manual? Or even trying out this workflow and see what's missing") release. And I would call it 1.7.0 - there is dozens of new things in beets since the Nov 2021 release 1.6.0 ;-)
Usually semver says: Major is for breaking changes -> 2.0.0, New features are Minor -> 1.7.0, and only fixes would be -> 1.6.1
But as always there is no-one-answer on how to put what semver suggests (and my idea of it is very very simplified I guess). I think we need @sampsyo here for some thoughts on how to proceed. And let's brainstorm more of what are the essentials of changes to the format of the changelog that could be easily automated and how would we need to adapt it - maybe even now! Before the auto-release-era begins!
We are definitely doing 1.7.0, the project uses semantic versioning and we're way past a patch. Providing the version number is the only input required for the workflow, so I think we should stick with keeping that as the only intervention required.
I'll try restoring the functionality of the script to change that file. Hopefully it'll do the changes necessary to do all of the changed.
Yes, manual intervention to pick the version number seems perfectly good to me! FWIW, I find strict SemVer to be an awkward fit for end-user applications (like beets), as opposed to libraries with an API (where the concept of "breaking changes" is easier to define). So I think it's inevitably somewhat fuzzy.
Once @sampsyo sets up the trusted publisher stuff, it'll work.
Happy to do this! Shall we first merge the basic mechanics in this PR, and then pull the trigger on actual PyPI uploads in the next one (since that seems worth being very careful about)?
It's all one thing, if we merge this then it will do the Pypi uploads as long as it's working. Right now it'll fail because the trusted publisher stuff isn't set up but there shouldn't be any problems whatsoever with them. The workflow is what they recommend, I really didn't change the code.
There's something I want to do before we merge. I need to reactivate the code that changes the changelog in the right way. I feel that this workflow should be the absolute minimum intervention: you provide the version number and that's it. So I need to propagate that back to the changelog so it doesn't need to be done manually.
Note also that there are no conditions (at this time) where this release will be run automatically. It will always need human intervention to kick things off. Now if you'd like to switch to some kind of weekly release @sampsyo with the date as the version, we can also do that very easily. youtube-dl and yt-dlp do that. Then it would really be automatic.
@sampsyo this is ready to merge! I highly recommend setting up the PyPi trusted publisher stuff as that is part of this PR and the workflows as submitted. Nothing can really go wrong with what it is right now. I've tested everything up to the PyPi and the worst that can happen is that it just doesn't work, in which case everything up to that (including making a GitHub release, which I have tested) will have worked and for the first release we'll have to do that manually. But I really don't think that's going to happen, since it's very basic and copied from their docs.
I'm quite sure that everything is working as intended, as it went fine in my own fork. Once this is merged, we should think about doing a release (1.7.0!) unless you'd like to consider changing beets to a date-based versioning system. But if not, then this PR is complete :)
@JOJ0 I think we should wait until @sampsyo sets up the pypi trusted publishing. It won't work until that's done.
This PR is a big improvement after nearly two years in 1.6.0, great work guys. But how is this going? Is there something I can help with?
@vicholp Thanks but we're good. We're just waiting on @sampsyo to do the final step.
Just friendly reminder
I've given all maintainers 48 hours to merge and review whatever they like and we'll do a release of beets 2.0.0 after that. Thanks for your patience.