An open API service indexing awesome lists of open source software.

https://github.com/wheels-dev/apt-wheels


https://github.com/wheels-dev/apt-wheels

Last synced: 24 days ago
JSON representation

Awesome Lists containing this project

README

          

# apt-wheels

Source repository backing ****, the native Debian/Ubuntu
package repository for the [Wheels](https://wheels.dev) CFML framework CLI.

This repo holds the **workflow + scripts + landing page + signing key**. The apt metadata
tree (`dists/`) and the `.deb` pool (`pool/`) live in Cloudflare R2 (bucket `wheels-apt`),
served at https://apt.wheels.dev via R2 custom-domain. The receiver workflow regenerates
the metadata + uploads to R2 on every release. End users do:

```bash
# Stable channel
curl -fsSL https://apt.wheels.dev/wheels.gpg \
| sudo tee /usr/share/keyrings/wheels.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev stable main" \
| sudo tee /etc/apt/sources.list.d/wheels.list
sudo apt update && sudo apt install wheels

# Bleeding-edge — distinct package name (`wheels-be`) so it coexists with stable
echo "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev bleeding-edge main" \
| sudo tee /etc/apt/sources.list.d/wheels-be.list
sudo apt update && sudo apt install wheels-be
```

## How updates flow into this repo

Releases land in `wheels-dev/wheels` (or `wheels-dev/wheels-snapshots` for
bleeding-edge). The release workflow fires `repository_dispatch` here with
event type `wheels-released` and a `{version, channel}` payload. The receiver
at [`.github/workflows/wheels-released.yml`](.github/workflows/wheels-released.yml)
downloads the new `.deb` from the upstream GitHub Release, slots it into
`pool//w//`, regenerates `Packages.gz` / `Release` / `InRelease`
via `apt-ftparchive`, signs with GPG, then uploads the new `.deb` + regenerated `dists/` tree to R2.
R2's custom-domain edge serves the new files within seconds.

The receiver also supports `workflow_dispatch` for backfill / disaster-recovery
(re-add a missing version without waiting for a new release):

```bash
gh workflow run wheels-released.yml \
--repo wheels-dev/apt-wheels \
-f version=4.0.0 -f channel=stable
```

## Contents

| Path | Purpose |
|------|---------|
| `.github/workflows/wheels-released.yml` | Receiver workflow — fires on `repository_dispatch` from `wheels-dev/wheels`. |
| `scripts/regenerate-apt-metadata.sh` | Pure-bash wrapper around `apt-ftparchive` + `gpg`. Idempotent — re-reads the entire `pool/`, rewrites `dists/` from scratch. Safe to invoke by hand to repair a torn release. |
| `templates/aptftparchive.conf` | `apt-ftparchive` config (Origin, Label, Components, Architectures, compression). The wrapper script overrides Codename + Suite per-distribution at invocation time. |
| `index.html` | Plain-HTML landing page served at the apex. Shows the sources.list snippet so browsers hitting `apt.wheels.dev` see install instructions. |
| `wheels.gpg.placeholder` | Reminder file — **must be replaced with the real ASCII-armored public key** committed as `wheels.gpg` at the repo root before the first release publish. See "Operational setup" below. |

## Distribution layout (post-first-publish)

```
apt.wheels.dev/
├── wheels.gpg # ASCII-armored public key
├── dists/
│ ├── stable/
│ │ ├── Release
│ │ ├── Release.gpg
│ │ ├── InRelease # inline-signed Release (preferred)
│ │ └── main/binary-amd64/
│ │ ├── Packages
│ │ └── Packages.gz
│ └── bleeding-edge/
│ └── ... (mirror of the stable tree)
└── pool/
├── stable/
│ └── w/wheels/wheels__amd64.deb
└── bleeding-edge/
└── w/wheels-be/wheels-be__amd64.deb
```

## Package name & channel convention

| Channel | Package name | Pool path | `apt install` |
|---------|--------------|-----------|---------------|
| Stable | `wheels` | `pool/stable/w/wheels/` | `apt install wheels` |
| Bleeding-edge | `wheels-be` | `pool/bleeding-edge/w/wheels-be/` | `apt install wheels-be` |

Distinct package names mean stable and bleeding-edge can be installed
side-by-side on the same host — useful for users who want a working stable CLI
alongside a BE one for testing.

## Filename: `~`-form vs `.`-form

GitHub Releases silently rewrites `~` to `.` in uploaded asset filenames, so:

- On disk locally (nfpm output): `wheels-be_4.0.1~snapshot.1787_amd64.deb`
- GitHub Release URL: `wheels-be_4.0.1.snapshot.1787_amd64.deb`
- Pool path (this repo): `pool/bleeding-edge/w/wheels-be/wheels-be_4.0.1~snapshot.1787_amd64.deb`

The receiver workflow downloads using the `.`-form (the only form the GitHub
Release URL exposes), then renames to the canonical `~`-form before publishing
into the pool. The version field *inside* the `.deb` metadata always carries
`~`, so `dpkg --compare-versions` orders snapshot releases below the next GA
release correctly regardless of which form sits in `pool/`.

## Operational setup

Before this repo will publish a usable repository:

1. **GPG signing key** — generate a 4096-bit RSA key (or Ed25519) for
`Wheels Distribution `. Private key + passphrase go into
1Password under `op://Wheels/wheels-linux-repo-signing/` (Wheels project
vault on `my.1password.com` — **not** the PAI `op://Infrastructure/` vault).
Public key (ASCII-armored) replaces `wheels.gpg.placeholder` with a
committed `wheels.gpg` at the repo root. The same key is used by the
parallel [`yum-wheels`](https://github.com/wheels-dev/yum-wheels) bucket.

2. **Cloudflare R2 bucket** — create the `wheels-apt` bucket, attach the
`apt.wheels.dev` custom domain. R2 has no per-object size limit (vs.
Cloudflare Pages' 25 MiB) which is why we serve from R2 rather than Pages.

3. **CI secrets** at
:
- `WHEELS_REPO_GPG_PRIVATE_KEY` — ASCII-armored private key
- `WHEELS_REPO_GPG_PASSPHRASE` — passphrase
- `CLOUDFLARE_API_TOKEN` — token with `Workers R2 Storage:Edit` on this account

4. **Upstream dispatch token** — on `wheels-dev/wheels`, add
`LINUX_REPO_DISPATCH_TOKEN` (fine-grained PAT with `actions: write` on this
repo and on `wheels-dev/yum-wheels`). The release workflow's dispatch step
skips silently when this secret is unset, so the wiring is safe to land
before the secret exists.

5. **Smoke-test** — once the GPG key is in place and the secrets are set, run
the receiver manually to backfill the current GA release:

```bash
gh workflow run wheels-released.yml \
--repo wheels-dev/apt-wheels \
-f version=4.0.1 -f channel=stable
```

Then verify on a fresh Debian/Ubuntu host:

```bash
curl -fsSL https://apt.wheels.dev/wheels.gpg \
| sudo tee /usr/share/keyrings/wheels.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/wheels.gpg] https://apt.wheels.dev stable main" \
| sudo tee /etc/apt/sources.list.d/wheels.list
sudo apt update
sudo apt-cache policy wheels # should show the apt.wheels.dev origin
sudo apt install wheels
wheels --version
```

`apt update` reports `Get:N https://apt.wheels.dev stable InRelease` on
success. A `NO_PUBKEY` error means the public key at
`/usr/share/keyrings/wheels.gpg` doesn't match the key the bucket signed
with — verify with
`gpg --no-default-keyring --keyring /usr/share/keyrings/wheels.gpg --list-keys`.

## Upstream source-of-truth

This repo was seeded from the templates at
[`wheels-dev/wheels` → `tools/distribution-drafts/apt-repo/`](https://github.com/wheels-dev/wheels/tree/develop/tools/distribution-drafts/apt-repo).
Material changes to the workflow / scripts / config should generally land in
the upstream template too, so the next-time-we-rebuild-this story stays clean.
See [issue #2605](https://github.com/wheels-dev/wheels/issues/2605) for the
Phase 2 rollout plan.

## License

MIT — see [LICENSE](LICENSE).