Releases¶
Releases follow the repository blueprint: nobody chooses a version or writes release notes by hand.
flowchart TB
subgraph request ["The release pull request"]
direction LR
prs["Pull requests<br>fix: · feat: · feat!:"] -->|merged into main| bot["chore: release x.y.z<br>version and changelog"]
end
subgraph build ["Once the maintainer merges it"]
direction LR
draft["Draft release<br>with its tag"] --> package["Package built<br>and checked"] --> sign["Signed with the<br>app's certificate"] --> assets["Signature, SBOM,<br>provenance"]
end
subgraph publication ["Publication"]
direction LR
published["Release published,<br>immutable"] --> store["Nextcloud App Store"] --> verify["Release verified,<br>and every week again"]
end
request --> build --> publication
The release pull request¶
Every pull request that changes something for users describes it under ## Unreleased in CHANGELOG.md. The release bot (.github/workflows/release.yml) keeps a pull request titled chore: release x.y.z up to date with main:
- The version follows from the titles of the pull requests merged since the last release:
fixmakes a patch,feata minor and!orBREAKING CHANGEa major version. Every merge decides it anew. A lineRelease-As: x.y.zin the description of a pull request sets it explicitly. - The changelog: the text of Unreleased becomes the section of the release; without one, the section lists the pull requests.
- The files of the version:
appinfo/info.xml(<version>and the screenshot URLs markedx-release-please-version),version.txtand.release-please-manifest.json.
The pull request needs the same required checks as every other one, the Docker end-to-end tests against every supported Nextcloud version included. A release of dependency updates alone merges itself; every other release waits for the maintainer to merge it.
Publication¶
Merging the release pull request creates the release as a draft, with its tag, and then:
- krankerl builds the package of the tagged commit;
scripts/check-package.shchecks what it holds. - The package is signed with the app's certificate (
occ integrity:sign-app) and checked again, and a detached SHA-512 signature is made and verified with the public key of the certificate. - The release gets the package
paperless_sync.tar.gz, its signaturepaperless_sync.tar.gz.sig, an SPDX SBOM, and the signed build provenance of every asset (provenance.sigstore.jsonandprovenance.intoto.jsonl). - The complete release is published; GitHub keeps it immutable from then on.
- The same package is submitted to the Nextcloud App Store.
- The release verification (
.github/workflows/verify-release.yml) checks the release as its users can: every attestation, the SBOM, the immutability, the signed commit, and the version in the App Store. It checks the latest release every week as well.
Secrets and recovery¶
The release environment allows only main. It holds APP_PRIVATE_KEY (the key of the app's certificate), APPSTORE_TOKEN and RELEASE_AUTOMATION_PRIVATE_KEY (the release app, whose client ID is the repository variable RELEASE_AUTOMATION_CLIENT_ID). Every token is short-lived and scoped to what its job needs.
If a job fails, re-run the failed jobs of the Release workflow: a draft is completed, and the App Store accepts the same version again without a second release. A published release is never replaced.
Registering the app in the App Store, needed once and again only for a new certificate, is the manual workflow Register app in Nextcloud App Store.