|git-ubuntu staged uploads
Currently, a merge proposal of a branch against the git-ubuntu repository for an Ubuntu package is tightly coupled to the upload of the branch after it is approved. But uploading a package triggers additional process which isn’t always desirable. Ubuntu developers would like to stage branches without uploading them immediately. Other Ubuntu developers could then build upon these branches and upload from them in the future when appropriate.
Uploading incurs a cost of going through proposed migration. Tests on reverse dependencies may take a long time. Decoupling the upload itself and approval of changes to the package allows us to save the cost of unnecessary uploads.
If we can incorporate a reliable method of staging uploads into git-ubuntu workflows, then:
developers whose workflows primarily use git-ubuntu will benefit from this functionality; and
some developers who currently do not use any VCS for Ubuntu package maintenance will benefit from starting to do so.
Namespace and content
The git ref namespace in git-ubuntu imported repositories shall set aside
refs/heads/next/* for Ubuntu uploaders to stage changes for future upload; for the purpose of this specification, these shall be called “staging branches”. This subdivision of the ref namespace shall be used as follows:
refs/heads/next/ubuntu/develshall be used to stage changes to the development release.
refs/heads/next/ubuntu/<series>-proposedshall be used to stage changes to stable releases, but only when all such staged changes are suitable for the SRU process and related process (eg. SRU documentation in bugs) are ready.
Other names inside
The staging branches shall be “unapplied”; staged “applied” branches are not considered and are not in scope of this specification.
The git-ubuntu importer shall fast-forward the staging branches against the non-next unapplied branches when it updates a repository against the latest source package publications in Launchpad. Branches that cannot be fast-forwarded shall be left as-is for Ubuntu developers to resolve the situation manually. Staging branches that do not exist shall be created and pointed to the corresponding importer branches.
Once implemented, we will re-run the importer against every repository. After that, it can be assumed that the staging branches always exist.
Only an Ubuntu developer who can upload to a particular package and series shall have permission to push to the corresponding staging branch. Non-fast-forwarding pushes (ie. “force push”) shall also be permitted to those who can push. Pushes to all other refs remain reserved for the importer only.
Team owner rename
Since the ~git-ubuntu-import Launchpad team will now include branches that contain primary work that are not imports, this team shall be renamed to ~git-ubuntu.
These requirements cannot be enforced by implementation of this specification itself, but assumptions are documented here as they are required for the staging system to work in practice. They will need to be socialised and consensus established by public discourse.
An Ubuntu developer who chooses to push a particular change to a staging branch shall take responsibility for that change as if they uploaded it themselves.
Uploads to the staging branch for a stable release should only be made for the purpose of staging SRUs and only after associated SRU process requirements (eg. SRU documentation in bugs) have been prepared.
Ubuntu developers who upload any changes to a package to Ubuntu should endeavour to incorporate any changes previously staged by other Ubuntu developers. However, such incorporation may not always happen. In this case, it will be up to those who had previously staged changes to rebase them and/or upload themselves as required.
Location and behaviour of staging branches
Development workflows in Ubuntu are a consequence of Debian and Ubuntu engineering history. Prevailing software engineering practice today is to use a VCS (in our field today, git) as the single source of truth.
Developers, including non-distribution developers, expect there to be a branch (“main”, formerly “master”) that represents the cutting edge and the base against which changes should generally be proposed first.
git-ubuntu currently reflects this principle by presenting its
ubuntu/devel branches as equivalent, but this only mirrors the existing status quo of package uploads being the single source of truth and does not provide any view of staged branches in git. To an outside observer, branches used elsewhere for this purpose are not discoverable, and where
Vcs-Git is used to point to team-maintained packaging, these aren’t consistent across packages and generally don’t share the commit graph with git-ubuntu.
Ideally, best practice distribution development workflows in Ubuntu would be the same as best practice software development workflows in general, except where the nature of distribution development requires it to be different. A useful test for this is to consider to what extent it is possible to explain our workflows to non-distribution developers such that our ways seem reasonable without using history as an excuse.
git-ubuntu staged branches should therefore be presented in as close a way as possible as how an outside developer would expect to see them.
For now, Ubuntu must accept uploads made directly with dput and without git-ubuntu because so many of our existing workflows expect this. It isn’t practical to force this to change all at once. And Ubuntu developers must be able to confidently identify what is part of the official record (ie. what is actually published as a source package) from the git-ubuntu view. With staging, a consequence of these requirements is that the cutting edge may now appear in one of two places:
ubuntu/devel (especially if an upload has been made outside git-ubuntu), or the staging branch. In the case that a direct upload trumps the staging branch, the two branches will be diverged and this will need to be resolved by a maintainer of the staging branch manually.
It’s therefore unavoidably necessary to accommodate in our design the case that either of
ubuntu/devel and the staging branch may be the current development tip. The design of the git-ubuntu CLI may be able to help with this, but regardless, the simplest developer experience would be one where both of these branches exist in the same repository and therefore on the same git remote from the user’s perspective.
Using a different repository and making use of Launchpad’s namespacing, such as
~ubuntu-uploaders/ubuntu/+source/<package> was the original plan, but the plan was changed given the above considerations. There were also a few other concerns noted with this alternative:
When submitting an MP, Launchpad will choose the target repository automatically. This won’t work if it were ~ubuntu-uploaders.
It’s not possible for third parties to submit MPs against ~ubuntu-uploaders.
The default repository target is used for object sharing.
When going to code.launchpad.net in the web UI, a user might expect to see the staging branch in the same repository as “everything else”. But if the staging branch belongs to a different team, this won’t be possible.
The staging branch wouldn’t be able to be presented in the repository that is default for the package.
Advantages of staging branches being always present:
Easy (future) “git ubuntu clone” workflow: we can simply always start from
pkg/next/ubuntu/devel, the local branch will be called
next/ubuntu/develby default, and tooling will default to push back to there (for those with permission).
MPs can be filed against the next branch since it exists. They will then be marked automatically for merge.
Since uploaders will be able to push to next branches, they will eventually be able to manipulate merge proposals, which they cannot currently do with git-ubuntu’s read-only import branches. See LP: #2007731. However this isn’t implemented inside Launchpad yet.
Disadvantages of always being present
- We will need to run the importer against every repository to get these created, and every new repository and new series will need this branch updated, too. However, new repositories and new series already prompt importer runs, and in the common case that the branch pointer is identical to the imported branch, the overhead will be minimal.
Importer behaviour and HEAD
In the common case that nothing is staged, the staging branches should be fast-forwardable in the case that someone uploads without git-ubuntu, since they would move with the corresponding “official” branch pointer. Then we could always point
HEAD to the staging branches instead. Repointing
HEAD is planned in the future but but out of scope of this spec.
If a non-git-ubuntu upload occurs and the staging branch becomes non fast-forwardable, this may not be obvious to an Ubuntu developer. A risk is that they will not realise and revert the non-git-ubuntu upload by accident. This can be mitigated by a warning from the CLI, and may be prevented by version number collisions in some cases. But it is possible that a developer will not use these tools and a version number collision will not occur, so the error will arise. Before staging branches, this was unlikely since git-ubuntu developers typically use the “devel” tip which does usually fast-forward. Non fast-forwards are possible, but a non-git-ubuntu upload does not usually cause one.
Events on new release opening
When a new development release opens,
next/ubuntu/devel would get carried forwards automatically, which is what developers would expect. The importer will create
next/ubuntu/<stable>-proposed when it next runs, which in practice happens when all packages are copied forward to the new release as part of the release opening process. This triggers importer runs for everything because they are new source package publications that are treated as requiring importer runs.
Uploads into the archive must be performed by Ubuntu developers who have been individually approved by the Developer Membership Board. This requirement is enforced by ACLs in Launchpad that are managed by the DMB. An upload might have been prepared by the uploading Ubuntu developer themselves; it is also common for an approved uploader to “sponsor” someone else’s work after having reviewed it.
Socially, we expect the approved uploader who signed an upload to take final responsibility for that upload. This may involve additional work if delegating it was not successful, such as ensuring that proposed migration succeeds and dealing with any regressions or other mistakes or consequences.
By decoupling the preparation of branches with their future upload, there are implications for this mechanism of social responsibility. An uploader can’t reasonably be expected to take responsibility for branches that they did not themselves prepare or review. Requiring uploaders to review branches already approved and staged by others for future upload would seem wasteful. If at the point of upload the uploader reviewed and wanted review changes, then that would seem to defeat the point of having staged the branch for upload in the first place. And if they could not request review changes, then we’re back to it being unreasonable for the uploader to take responsibility for that part of the upload.
It therefore follows that the only reasonable way to maintain social responsibility for changes made to the Ubuntu archive is to move that responsibility to those approving pushes to the staged branches, instead of responsibility resting with the person who eventually performs the upload itself.
This requires the uploader to have confidence that all changes previously staged for upload have been staged by somebody who can carry that responsibility—it must be someone approved by the DMB to be able to upload to that particular source package for that particular series and pocket.
The most straightforward way to ensure this is to limit, by ACL, the ability to push to a given staging branch to the same set of people who can upload to the corresponding package, series and pocket. This will require support in Launchpad for git repository ACLs to be tied to upload permissions.
We will allow uploaders to force push to the staging branches since in the case of being trumped by a direct upload, uploaders may wish to rebase the staging branch to resolve the issue.
Tracing the responsible person
For this principle of social responsibility to work, It’s necessary to be able to determine the responsible person. In the case of the staging branches, this translates to determining the person who pushed a particular branch, as distinct from the person who authored the change or performed the commit. But git itself does not record this information.
Together with the Launchpad team, we propose that Launchpad will maintain and publish an audit trail that will record this information.
Dependencies on other work
Launchpad’s git repository ACLs
Launchpad’s git ACL design incorporates consideration for permissions based on package uploaders, but this was not yet implemented. Support for this feature is tracked in LP: #1993290. Implementation of this spec is blocked on implementation of this inside Launchpad.
Currently, git-ubuntu will need to maintain the same ACL entries for every single repository (currently there are 44k). It would be more convenient if we could set the ACL once to apply to all repositories owned by the team. Support for this feature is tracked in LP: #1993303. We may want to consider implementation of this spec to be blocked on implementation of this inside Launchpad.
The ability to determine who pushed to a staging branch is necessary for the social responsibility principle to work. The proposal for this mechanism is tracked in LP: #2017975 but this design hasn’t been committed to yet. The design itself should treated as blocking the implementation of this spec. However the implementation of this feature in Launchpad doesn’t necessarily block progress since, given that push will be gated by an ACL, we can rely on trust of a relatively small set of people in the meantime.