frontends-team-compass
frontends-team-compass copied to clipboard
JupyterLab Development Cycle RFC
Goals:
- Formalize our process around releases and git branches
- Automate the release process as much as possible
- Spread out any required human labor for a release into the PRs that make up the release
- Decrease amount of time necessary to do a release
Non goals:
- Change the user or extension developer expectations around our current releases
- Switch from using SemVer
- Change the organization of our code
I was originally writing a comment on https://github.com/jupyterlab/jupyterlab/issues/8195, but as it grew longer I realized that an issue comment maybe wasn't the best format to get feedback.
TODO:
- [ ] Come to agreement on how to bump JS versions not in sync with Python versions
- [ ] Switch
masterbranch tox.0.0(wanted to do this originally for consistency, but felt that it wasn't worth upheaval. However, with all the discussion around removing master anyways, think this might make sense. similar proposal in Python). - [ ] Think about and add strategy for dealing with docs, with only goal to keep master up to date with all changelogs.
Thank you @blink1073 for the comments and review here! I will update with your suggestions. I am glad it made some sense out of my own head :)
Any other feedback on how to make it more readable is very much appreciated.
@blink1073 I updated it to use branch merges to release. One other thing I added is the ability to test out the full release, of JS and Python packages, in the branch PR without actually releasing anything, by using a proxy NPM server to publish the packages there. Also adds the built packages as artifacts to that PR action
Then, once it has been merge, all that needs to be done is grab those artifacts and actually publish them.
Do you think this would work?
JupyterLab Git has some dry run logic for both pypi and npm. We don't necessarily need to pull from the test servers, we could have two release targets.
We don't necessarily need to pull from the test servers, we could have two release targets.
Yeah I meant we would pull from the github target artifacts.
One big blocker for this is figuring out a plan if we want to allow not all NPM package versions to be bumped when we have a major release. Last we chatted, it was preferred to allow this behavior.
However, I am wondering if in our next major release, 3.0.0, we plan on selectively bumping major versions or just bumping all of them. If it is the latter, I wonder if we could consider this plan to at least automate our current process, even if it does force us to bump everything by a major version.
To @vidartf's comment, yes this would be formalizing something we are not OK with.
However, if we are doing it anyway, even if we aren't OK with it, then formalizing it is just being honest with ourselves and the community about the current state of how things works.
I know @blink1073 had a strategy in the past about bumping all minor versions up by ten or something, as a sort of hacky way around this. We could also formalize that kind of workaround instead, if we prefer that. EDIT: I feel like this hack is maybe a bad idea? If people don't pin exactly, and we are releasing things on two strands basically of minor versions for a single package, I could see this getting very confusing..
I prefer "very hacky and pragmatic" :smile:
i.e. to expand on my edit above:
Let's say we release version 3.0.0 and package a currently is at 2.0.0. My understanding of @blink1073's "hacky" approach (lmk if this is correct or not) is to bump a to 2.10.0 for the 3.0 release. Then we can continue having parallel minor releases of package a in the 2.x branch as well as in the new 3.x branch. Is that right?
If so, then let's say we release 2.11.0 as a new minor version of a on 3.0. Then, couldn't extensions built for the 2.x release pull this in if they use the ^2.0.0 version spec? Wouldn't that sorta be bad? Conceptually they would just wanna pull in 2.0.0 up to but less than 2.9.0, because that would be the last minor that could be on the 2 release?
If so, then let's say we release 2.11.0 as a new minor version of
aon 3.0. Then, couldn't extensions built for the 2.x release pull this in if they use the^2.0.0version spec? Wouldn't that sorta be bad? Conceptually they would just wanna pull in2.0.0up to but less than2.9.0, because that would be the last minor that could be on the 2 release?
The whole point here is that some extensions would not need to change across major jlab releases, so indeed a 2.11 extension from jlab 3 could be used in jlab 2. The idea here is that we bump on actual backwards incompatible changes, so if there were no backwards incompatibilities for a particular extension, it doesn't get bumped.
The whole point here is that some extensions would not need to change across major jlab releases, so indeed a 2.11 extension from jlab 3 could be used in jlab 2.
Would we release new minor versions though of the previous packages? We currently do this for patch releases, so maybe an example there is more illustrative.
Let's say we have an NPM package at version 2.0.0, and we release a new patch version for the 3.0.0 release, so we have 2.0.10 as the version NPM released in Python 3.0.0.
Now we do some backports into the 2 branch and release a 2.0.1 NPM release of that package.
However, there is now a 2.0.10 version as well, that has the patches we added for the 3.0.0 release. This will now be installed on old users who have the 2.0.0 Python package installed, since it is ahead of 2.0.1 in SemVer terms. This is the issue I mean above. Does that make sense?
If we adopted an "only move forward approach" where there is no 2.x minor release after a 3.x release, then we could avoid the hacky approach. The hacky approach was adopted to enable that capability.
If we adopted an "only move forward approach" where there is no 2.x minor release after a 3.x release, then we could avoid the hacky approach. The hacky approach was adopted to enable that capability.
We still have the problem if we release alphas/betas of 3.0 while releasing a new minor 2.x release. For example, if we release an alpha of 3.0, then release 2.2, we still need to handle there being two parallel versions out in the wild.
(so if by "a 3.x release" you mean "any 3.x release, including prereleases", then yes...)
I think I have lost some of the thread of our current discussion around the "hacky aproach".
Goal
To step back, the goal here is to articulate a strategy that works for not having to do a major version bump of all packages for each major python release, only those with breaking changes. We still want to do releases of the previous major version while the next major version is in the process of being released and has been released.
Issue
The root issue is that if we do the minimum bump of each package we can during each release, and we then do later releases on the older branch, we have two separate git branches to release the same release line of packages (like 1.2.x or 1.x.0).
Solution
One way to make it safe to keep doing releases on older branches is to make sure they can only upgrade the packages that won't possibly have a duplicate version. The rule would be like this:
If we are doing a major Python release, the next minor or patch versions for NPM packages of the previous major Python version are limited as so:
- If the NPM package has a major version bump in the next major Python release, then it can have subsequent minor or patch releases in the previous major Python version.
- If the NPM package has a minor version bump in the next major Python release, then it can have patch releases in the previous major Python version
- If the NPM package has a patch release, or not release in the in the next major Python release, then it cannot have anymore releases in the previous major Python version.
If we are doing a minor Python release, the next patch versions for NPM packages of the previous minor Python version are limited as so:
- If the NPM package has a minor version bump, it can have subsequent patch releases
- If the NPM package has a patch or no version bump, it cannot have any subsequent releases on the old minor Python version.
This seems like the maximally flexible rules that still preserves version consistency. We could opt to not use all this flexibility, however and trade off some preciseness in our SemVer version bumps for the information we need to persist across time. For example, we could use the strategy:
On every major release, bump all packages a minor release at a minimum. Any packages that have breaking changes, bump as a major version. This means we are free in the old major version to keep making as many patch releases as we like, and minor releases for any package that we bumped a major version.
On every minor release, bump all packages a a minor release. This allows us to make subsequent patch releases on any NPM packages on the previous minor release.
In this strategy, we would only have to care about what package was having a major version bump, for patch and minor, they would be global.
Otherwise, if we wanted to do the least invasive version bumps necessary, we could track for each PR what version bump it has on every package it touches. And then, we would track on each next release which bumps happen and limit the older releases on the other branches according to the above rules. I.e. if a package had just a patch release in the major python version, then we could not make any more releases of that package in the previous python version. However, if it had a minor, we could make only patch releases. And if it had a major, we could make either minor or patch releases.
Does this reasoning make sense @jasongrout @blink1073 @vidartf @telamonian? If not, I think it might be helpful to arrange a call around this if either of you are interested. It's quite confusing and nuanced...
On every major release, bump all packages a minor release at a minimum. Any packages that have breaking changes, bump as a major version. This means we are free in the old major version to keep making as many patch releases as we like, and minor releases for any package that we bumped a major version.
On every minor release, bump all packages a a minor release. This allows us to make subsequent patch releases on any NPM packages on the previous minor release.
This sounds like the most sane (and humane) of the alternatives you present, Saul. Otherwise, I say we just give up and start versioning everything with (pypi_ver, npm_ver) tuples
I agree with @telamonian's assessment.
A further question... Should we allow any minor releases on previous major versions? By "previous" I mean like if we have a 3.0.0-alpha0 then the 2 major version is now "previous".
- Yes: We allow continued minor python releases previous versions. However, the only NPM packages which were had a major version bump in the first release of the current major python version (3.0.0-alpha0 in this example) can have minor releases. Those with a minor release, can only have patch releases. This means, when we do our first major version alpha, any packages which we decide to not major version bump at that time (note we can still major version bump them in subsequent major releases, say 3.0.0-alpha1), cannot have ongoing minor releases in the previous major. One way to think about this is that each release line can only exist in one branch at a time. There can only ever be one branch that can have the next release in a certain release line. So if a package gets a minor version bump for the next major, then the next major branch (master/x.0.0) now owns that minor release line and the previous major version can no longer release on it, only allowing patch releases.
- No: We only allow patch releases in previous Python minor releases. This means after the first alpha we release of 3.0.0 we cannot release another minor release in the 2.x line. This makes it simpler to track, since we don't have to care which NPM packages had minor/major versions in the next major release, but does limit our ability to keep doing minor releases on the previous major release.
As an example, we're using JupyterLab 1.x at AWS, and planning to wait until 3.0 is released to upgrade, since we decided that the 2.0 release did not have enough incentives for users to upgrade and would be too disruptive. I am considering making a 1.3 release to backport a few features while we are waiting.
I am considering making a 1.3 release to backport a few features while we are waiting.
OK so you are saying we should allow minor releases on older major versions?
OK so you are saying we should allow minor releases on older major versions?
Yep, that's what I'm advocating for.
Sounds good. Well then my next step would be to try to put together a plan for how we can achieve this, given the constraints under the "yes" answer above and type that up in the doc.