- docs/spec/release-packaging.md: decision-complete spec — compat matrix, Gitea 1.27.1 registry channel, nfpm toolchain, signing/key policy, release mechanics, package layout/ownership, maintainer-script contracts, initial config, make-install migration runbook. - docs/adr/0007: package delivery amends ADR 0004 (delivery/ownership only; runtime semantics inherited verbatim). 0004 status updated. - docs/research/: toolchain, gitea-registry, obs findings merged from research branches (assets of map tickets #34/#35/#36). - CONTEXT.md: Release + Rollback glossary terms (ticket #43).
117 lines
8.3 KiB
Markdown
117 lines
8.3 KiB
Markdown
# Research: Gitea 1.27 Debian + RPM package registry feasibility
|
||
|
||
Issue: [Fenris deb + rpm release plan](https://git.bongbetic.com/xavierk/Fenris/issues/33) →
|
||
[Research: Gitea 1.27 Debian + RPM package registry feasibility](https://git.bongbetic.com/xavierk/Fenris/issues/35)
|
||
Verified 2026-09-03 against live instance `https://git.bongbetic.com` (reports `1.27.1` via `/api/v1/version`)
|
||
and primary sources: docs.gitea.com 1.27 Debian/RPM registry pages and Gitea `v1.27.1` source (go-gitea/gitea tag).
|
||
|
||
**Verdict: feasible.** Every publish/consume path tested live with throwaway packages `fenris-regtest` (all deleted afterward; package list verified empty).
|
||
|
||
## 1. Publish paths (verified live, HTTP 201)
|
||
|
||
### Debian (`.deb`)
|
||
|
||
```bash
|
||
curl --user xavierk:$TOKEN --upload-file fenris_0.3.0_amd64.deb \
|
||
"https://git.bongbetic.com/api/packages/xavierk/debian/pool/{distribution}/{component}/upload"
|
||
```
|
||
|
||
- `distribution` and `component` are free-form path segments chosen at upload time (e.g. `bookworm/main`, `noble/main`). Gitea derives apt suites from what was uploaded — verified: same .deb published to `pool/bookworm/main` and `pool/noble/main` (both 201), both then served in `dists/bookworm/` and `dists/noble/` with correct `Suite:`/`Codename:` headers.
|
||
- Republish of identical name+version+distribution+component+architecture → **409 Conflict** (verified). Must delete first.
|
||
|
||
### RPM (`.rpm`)
|
||
|
||
```bash
|
||
# no group (flat repo)
|
||
curl --user xavierk:$TOKEN --upload-file fenris-0.3.0-1.el9.x86_64.rpm \
|
||
"https://git.bongbetic.com/api/packages/xavierk/rpm/upload"
|
||
# with group (distro tag, nestable)
|
||
curl --user xavierk:$TOKEN --upload-file fenris-0.3.0-1.fc40.x86_64.rpm \
|
||
"https://git.bongbetic.com/api/packages/xavierk/rpm/el9/upload" # e.g. el9, rocky/el9, fc40
|
||
```
|
||
|
||
- Group = free-form nesting used to partition repos per distro/track. Verified: publish to root group and `el9` group (both 201), duplicate → 409.
|
||
- Owner can be the user (`xavierk`) or an org; packages under a public owner are readable anonymously (verified: metadata fetches without auth succeeded).
|
||
|
||
## 2. Consumer setup (exact commands)
|
||
|
||
### apt clients
|
||
|
||
```bash
|
||
sudo mkdir -p /etc/apt/keyrings
|
||
sudo curl -o /etc/apt/keyrings/gitea-xavierk.asc \
|
||
https://git.bongbetic.com/api/packages/xavierk/debian/repository.key
|
||
echo "deb [signed-by=/etc/apt/keyrings/gitea-xavierk.asc] https://git.bongbetic.com/api/packages/xavierk/debian bookworm main" \
|
||
| sudo tee /etc/apt/sources.list.d/gitea.list # one line per distribution
|
||
sudo apt update
|
||
apt install fenris # or fenris=0.3.0
|
||
# private owner variant: https://{user}:{token}@git.bongbetic.com/api/packages/... in the URL
|
||
```
|
||
|
||
### dnf clients
|
||
|
||
```bash
|
||
sudo dnf config-manager --add-repo https://git.bongbetic.com/api/packages/xavierk/rpm/el9.repo
|
||
# private owner: add user:token into the baseurl inside /etc/yum.repos.d/gitea-xavierk-el9.repo afterwards
|
||
sudo dnf install fenris # or fenris-0.3.0
|
||
```
|
||
|
||
The served `.repo` (verified live) sets `gpgcheck=1` and points `gpgkey` at `…/rpm/repository.key`, so `dnf` auto-imports on first use.
|
||
|
||
## 3. Metadata signing: native, not passthrough
|
||
|
||
Gitea **signs generated metadata itself** with per-instance auto-generated PGP keys. Client-side signing config is limited to trusting the served keys.
|
||
|
||
- Debian: `dists/{suite}/InRelease` is clearsigned; `Release.gpg` detached sig also served. Key (RSA) fetched from `…/debian/repository.key`, uid literally `(Automatically generated Debian Registry Key; created …)`.
|
||
- RPM: `repodata/repomd.xml.asc` detached ASCII-armored signature, uid `(RPM Registry)`. Key from `…/rpm/repository.key`.
|
||
- Both verified with `gpg --verify` → **Good signature** (keys are self-generated; the "not certified" warning is expected and handled by the signed-by/keyring flow above).
|
||
- The apt `Release` also advertises `Acquire-By-Hash: yes` with MD5/SHA1/SHA256/SHA512 indexes of `Packages`/`.gz`/`.xz` (verified live). RPM repomd carries sha256 checksums for `primary/filelists/other.xml.gz`.
|
||
|
||
There is **no bring-your-own-signing-key config** for these registries in 1.27 — trust anchor is the instance's auto keys. For Fenris this is acceptable; TOFU over TLS via the key URLs above.
|
||
|
||
## 4. Multi-distro metadata
|
||
|
||
- Debian: distributions/suites are implicit — whatever `{distribution}` path segments appear on upload become `dists/{distribution}/` trees with `Suite:`/`Codename:` set to the segment. No server-side list to maintain; adding a new distro = upload with new segment + one more `deb …` sources line. Components likewise (`main`, etc.). Architectures come from each `.deb`'s control stanza (index served as `dists/{dist}/{component}/binary-{arch}/Packages`).
|
||
- RPM: same via `{group}` path segments (`el9`, `rocky/el9`, …); each group gets its own `repodata/`. No `basearch` filtering — clients pick the group; Gitea publishes whatever RPM arch was uploaded.
|
||
|
||
## 5. Version retention
|
||
|
||
- Default: **all versions retained indefinitely**; nothing auto-deletes. Old versions stay installable (`apt install fenris=0.2.9`, `dnf install fenris-0.2.9`).
|
||
- Republishing an existing name+version (deb: same dist/component/arch; rpm: same file name in group) → 409; overwrite requires delete-then-upload.
|
||
- Optional cleanup rules exist (per owner + package type): `KeepCount`, `KeepPattern`, `RemoveDays`, `RemovePattern`, `MatchFullName` (source: `models/packages/package_cleanup_rule.go`, executed by scheduled `CleanupTask` in `services/packages/cleanup/cleanup.go`). In 1.27.1 they are configurable **only in the web UI** (owner → Packages → Cleanup Rules); no v1 REST route (verified by route table grep of `routers/api/v1/api.go` — probes of `/api/v1/packages/{owner}/cleanuprules…` return 404/409-style errors).
|
||
- Deletes: format-specific `DELETE …/debian/pool/{dist}/{component}/{name}/{version}/{arch}` and `DELETE …/rpm/{group}/package/{name}/{version}/{arch}` (both verified, 204). Deleting last file removes the version. Generic fallback: `DELETE /api/v1/packages/{owner}/{type}/{name}/{version}`.
|
||
|
||
## 6. Release attachment: not supported
|
||
|
||
Gitea 1.27.1 has **no package↔release linkage**. Release assets (`…/releases/{id}/assets`) are standalone file uploads; the package model has no release field and no route links them (verified against `v1.27.1` source: `routers/api/v1/repo/release_attachment.go`, `models/packages/`). Options for Fenris releases:
|
||
|
||
1. Publish `.deb`/`.rpm` to the registry (real apt/dnf install UX) and reference the registry URLs in release notes.
|
||
2. Additionally upload tarballs/SHA256SUMS as plain release attachments.
|
||
3. Generic registry (`PUT /api/packages/{owner}/generic/{name}/{version}/{filename}`) if an untyped artifact store is needed.
|
||
|
||
## 7. Caveats for the release plan
|
||
|
||
- Owner choice matters: publish under an **org** (e.g. `fenris`) if multiple maintainers need write; `xavierk` user owner works today (token owner is admin).
|
||
- Metadata access follows owner visibility — public owner → anonymous consumers, no token in URLs (current state, verified). Keep owner public for frictionless installs, or embed `user:token` in sources/baseurl.
|
||
- apt distro naming should match OS release names (`bookworm`, `trixie`, `noble`) purely for client convention; server accepts anything.
|
||
- RPM groups should mirror `$distver` (e.g. `el9`, `fc40`) so `.repo` selection is obvious per target.
|
||
|
||
## 8. Test log (live, 2026-09-03)
|
||
|
||
| Step | Result |
|
||
|---|---|
|
||
| `PUT debian/pool/bookworm/main/upload` | 201 |
|
||
| `PUT debian/pool/noble/main/upload` (multi-dist) | 201 |
|
||
| `PUT debian` duplicate | 409 (expected) |
|
||
| `PUT rpm/upload` (no group) | 201 |
|
||
| `PUT rpm/el9/upload` (group) | 201 |
|
||
| `PUT rpm` duplicate | 409 (expected) |
|
||
| `GET debian/repository.key` / `rpm/repository.key` | PGP public keys (200) |
|
||
| `GET dists/bookworm/{Release,InRelease,Packages}` | correct; `gpg --verify` Good signature |
|
||
| `GET rpm{,/el9}/repodata/repomd.xml{,.asc}` | 200; Good signature |
|
||
| `GET rpm{,/el9}.repo` | generated repo files with `gpgcheck=1` |
|
||
| Cleanup-rules REST probes | 404 (not in v1 API — UI only) |
|
||
| `DELETE` all four test entries | 204 ×4; package list then empty |
|
||
|
||
Sources: [docs.gitea.com 1.27 Debian registry](https://docs.gitea.com/1.27/usage/packages/debian), [docs.gitea.com 1.27 RPM registry](https://docs.gitea.com/1.27/usage/packages/rpm), Gitea source tag `v1.27.1` (`routers/api/v1/api.go`, `models/packages/package_cleanup_rule.go`, `services/packages/cleanup/cleanup.go`), live instance `git.bongbetic.com`.
|