Lifecycle of a change

Software in Ubuntu is delivered via packages and there might be various reasons to change them. A change might appear as a fix to a bug, as an update to a new version of a package or customizing the build and runtime configuration to the needs of all Ubuntu users.

Getting such a change into Ubuntu involves various steps to enable worldwide collaboration and to ensure the quality of an upload containing that change to the Ubuntu Archive.

The diagrams here show the path a change takes, each stage then refined in later sections, some with optional paths where applicable.

This overview of the involved mechanisms allows a contributor to know what topic to study next and can be considered a different point of view to the content aligned to the lifecycle of a change and its way into Ubuntu.

        flowchart LR
    BugReport["<a href='#lc-bug-report-and-triage'>Bug Report<br/>&amp; Triage</a>"]
    PrepareTest["<a href='#lc-prepare-fix'>Prepare<br/>&amp; Test</a>"]
    Review["<a href='#lc-review'>Propose<br/>&amp; Review</a>"]
    Upload["<a href='#lc-upload'>Sponsorship<br/>or Upload</a>"]
    Migration["<a href='#lc-proposed-migration'>Proposed<br/>Migration</a>"]
    SRU["<a href='#lc-sru'>Stable Release<br/>Update (SRU)</a>"]

    BugReport --> PrepareTest
    PrepareTest --> Review
    Review --> Upload
    Upload --> Migration
    Upload -.-> |"active release"| SRU
    SRU --> Migration

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class BugReport,PrepareTest,Review,Migration,Upload,SRU ubuntuStyle
    

Bug reporting and triage

Almost every change begins with filing a bug on Launchpad. A well-written report states how to reproduce the problem, which package is affected, and what the expected behaviour is. Ideally it includes references to related upstream changes or reports. Once filed, the bug is triaged: confirmed as real, assigned the appropriate importance, given a status, tagged (see Bug tags and Bug types), and routed to the right package. The bug serves as the central record that links all subsequent work (patch, merge proposal, upload).

        flowchart LR
    File["<a href=https://documentation.ubuntu.com/project/contributors/qa-and-testing/report-a-bug/>File bug on Launchpad</a><br/>with reproduction steps<br/>and system details"] --> Triage
    Triage{"<a href=https://documentation.ubuntu.com/project/contributors/bug-triage/>Triage</a>:<br/>enough information?<br/>right package?<br/>assignee?"}
    PrepareFix(["<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/lifecycle-of-a-change/#preparing-and-testing-the-fix>Prepare change</a>"])
    Triage -.-> |"needs more info"| File
    Triage --> |"actionable"| PrepareFix

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class File ubuntuStyle

    classDef gateStyle fill:#D0E8FF,stroke:#3366CC,stroke-width:2px
    class Triage gateStyle

    classDef terminalStyle fill:#E0E0E0,stroke:#666666,stroke-width:2px
    class PrepareFix terminalStyle
    

Preparing and testing the change

The most common type of change is a targeted patch: the fix is written, the debian/changelog entry is updated, and the change is proposed against a git-ubuntu branch.

Other change types include merges from a newer Debian version of the package (Merges & syncs), syncs where the Debian package is copied verbatim (Merges & syncs), and new packages — which, if destined for main or restricted, must also pass a Main Inclusion Review.

Before proposing the change for review, it should be proven to build correctly and not break existing functionality. The recommended approach is a PPA build on Launchpad — the same infrastructure that builds official Ubuntu packages. Once built, PPA packages can be shared with the bug reporter for smoke-testing, and automated DEP-8 tests can be run against them — see Automatic package testing: autopkgtest for how these tests are defined and integrated into packages. Building locally with sbuild is also an option for iterative debug cycles.

        flowchart LR
    Fix["Prepare change:<br/><a href=https://documentation.ubuntu.com/project/contributors/bug-fix/fix-a-bug-in-a-package/>patch</a> · <a href=https://documentation.ubuntu.com/project/contributors/merging/>merge</a> · <a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/merges-and-syncs/>sync</a>"]
    Fix --> Build
    Build["<a href=https://documentation.ubuntu.com/project/contributors/bug-fix/build-packages-in-a-ppa/>PPA build</a> on Launchpad<br/>(all architectures)"]
    Build -.-> |"build fails"| Fix
    Build --> |"build OK"| Test
    Test["Run <a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/automatic-package-testing-autopkgtest/>autopkgtests</a><br/>against <a href=https://documentation.ubuntu.com/project/contributors/bug-fix/build-packages-in-a-ppa/>PPA</a> packages"]
    Test -.-> |"test regressions"| Fix
    ProposeChange(["<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/lifecycle-of-a-change/#proposing-and-reviewing>Propose change</a>"])
    Test --> |"ready"| ProposeChange

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class Fix,Build,Test ubuntuStyle
    
    classDef terminalStyle fill:#E0E0E0,stroke:#666666,stroke-width:2px
    class ProposeChange terminalStyle
    

Proposing and reviewing

Once the fix builds and tests pass, it is submitted as a merge proposal (MP) against the relevant git-ubuntu branch on Launchpad. The merge proposal links the fix to the bug, describes what was done and why, and refers to a PPA and test results as far as they already exist. Reviewers leave comments, the contributor iterates until the merge proposal is approved.

A reviewer (typically a developer with upload rights) checks the proposal against a review checklist. This covers correctness of the fix, packaging correctness (debian/changelog, version string, copyright), DEP-3 patch headers, and adherence to Debian and Ubuntu policy. Trivial or low-risk changes may receive a lighter-weight review.

        flowchart LR
    MP["<a href=https://documentation.ubuntu.com/project/contributors/patching/submit-a-merge-proposal/>Submit Merge Proposal</a><br/>on Launchpad"] --> Review
    Review{"<a href=https://documentation.ubuntu.com/project/contributors/advanced/review-a-merge-proposal/>Code review</a>"}
    Review -.-> |"changes requested"| Revise["Revise change</a>"]
    Revise --> Review
    Upload(["<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/lifecycle-of-a-change/#sponsorship-and-upload>Upload</a>"])
    Review --> |"approved"| Upload

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class MP,Revise ubuntuStyle

    classDef gateStyle fill:#D0E8FF,stroke:#3366CC,stroke-width:2px
    class Review gateStyle
    
    classDef terminalStyle fill:#E0E0E0,stroke:#666666,stroke-width:2px
    class Upload terminalStyle
    

Sponsorship and uploads

Once approved, the change must be uploaded to the Ubuntu Archive.

Direct upload access is granted only after a developer has demonstrated consistent packaging quality (see the Uploader journey).

Until then, a contributor finds a sponsor — a developer who reviews the change, signs the .changes file with their own key, and runs dput on the contributor’s behalf.

Ideally the uploader is the person who did the review of the merge proposal, there is no need to be reviewed twice. Otherwise they might apply their own review before signing and uploading.

If you want to apply for upload rights later, you should keep track of your work and who has were your sponsors, as sponsors become endorsers for your application.

        flowchart LR
    Rights{"<a href=https://documentation.ubuntu.com/project/who-makes-ubuntu/developers/#path-to-upload-rights>Upload rights</a><br/>for this package?"}
    Rights --> |"no: <a href=https://documentation.ubuntu.com/project/contributors/uploading/find-a-sponsor/>Find sponsor</a>"| Sponsor
    Sponsor["sponsor <a href=https://documentation.ubuntu.com/project/contributors/advanced/review-a-merge-proposal/>re-reviews</a>"]
    Sponsor --> |"sponsor approves"| Upload
    Rights --> |"yes"| Upload
    Upload(["<a href=https://documentation.ubuntu.com/project/contributors/uploading/>Upload to Ubuntu Archive</a>"])

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class Sponsor ubuntuStyle

    classDef gateStyle fill:#D0E8FF,stroke:#3366CC,stroke-width:2px
    class Rights gateStyle

    classDef terminalStyle fill:#E0E0E0,stroke:#666666,stroke-width:2px
    class Upload terminalStyle
    

Proposed migration

All accepted uploads land in the -proposed pocket ([codename]-proposed) first. From there, Launchpad builds binary packages for every supported architecture. A series of automated quality gates must then be passed before the package is allowed to migrate to the -release or -updates pocket and thereby reach users. Any regression must be investigated as it otherwise stays stuck in -proposed.

To migrate from the staging area, the update must:

  • Build Successfully The source package must not fail to build from source (FTBFS) on any architecture. In case of failure, the uploader must investigate the build log and upload a corrected source package.

  • Pass Autopkgtest (DEP-8) Automated functional tests in debian/tests/ inside the changed package and all its reverse-dependencies must pass.

  • Pass Archive consistency check The tooling then verifies that the new binaries are installable together with everything else in the archive, that all required dependencies are present at the right version, and that nothing else becomes uninstallable as a side-effect. Any findings are reported as Issues preventing migration.

Progress for the current development release is visible on the update excuses page.

See How to resolve a migration issue for practical guidance on tackling such blockers.

        flowchart TD
    Build{Builds}
    Fix["Please fix & re-upload"]
    Autopkg{"<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/automatic-package-testing-autopkgtest/>Autopkgtests</a>"}
    InvestigateAPKGT["<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/proposed-migration/autopkgtest-regressions/>Investigate regressions</a>"]
    ArchiveChecks{"<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/proposed-migration/issues-preventing-migration/>Archive consistency</a><br/>(<a href=https://ubuntu-archive-team.ubuntu.com/proposed-migration/update_excuses.html>Britney</a>)"}
    Done(["Migrates"])
    
    Build -.-> |"<a href=https://documentation.ubuntu.com/project/how-ubuntu-is-made/processes/proposed-migration/failure-to-build-from-source-ftbfs/>FTBFS"</a>| Fix
    Build --> |"all builds OK"| Autopkg
    
    Autopkg -.-> |"regressions"| InvestigateAPKGT
    Autopkg --> |"all pass"| ArchiveChecks
    
    InvestigateAPKGT -.-> |"Real problem"| Fix
    InvestigateAPKGT --> |"Transient issues, now resolved"| ArchiveChecks

    ArchiveChecks -.-> |"blocked by dependencies"| Fix
    ArchiveChecks --> |"all checks OK"| Done

    classDef ubuntuStyle fill:#FFDAB9,stroke:#E95420,stroke-width:2px
    class Fix,InvestigateAPKGT ubuntuStyle

    classDef gateStyle fill:#D0E8FF,stroke:#3366CC,stroke-width:2px
    class Build,Autopkg,ArchiveChecks gateStyle

    classDef terminalStyle fill:#E0E0E0,stroke:#666666,stroke-width:2px
    class Done terminalStyle
    

Stable release updates (SRU)

Updates for the current Ubuntu development version can directly be released. But after publication of an Ubuntu release (such as 26.04), its package archive is frozen for updates. Bug fixes, security patches, and important improvements can still be provided for users through the Stable Release Update (SRU) process — but only under strict requirements designed to avoid regressions by an update.

A lot of the machinery is shared, an SRU upload passes through the same Proposed migration as any other. At the beginning the SRU team reviews and accepts from the -unapproved queue, and at the end the SRU team verifies the case to be ready to then releases it to -updates.

See SRU pipeline for the flow of these extra steps.