Hosted package repositories
A package attached to a GitHub release is installed once and then forgotten. Users who add your repository instead get every update through their own package manager. native-packages writes these repositories as static files into a directory you choose, such as a checkout of a GitHub Pages site, and signs them with your OpenPGP key. It only writes files: your release job commits and pushes the directory afterwards, as it would any website change.
1. Make a signing key
Use a key for this repository alone, without an expiry date you might miss, and keep its secret half out of every repository:
gpg --quick-gen-key 'Hello packages <packages@example.org>' ed25519 sign never
gpg --list-keys --with-colons packages@example.org | awk -F: '/^fpr/ { print $10; exit }'
The second command prints the key’s full fingerprint, which goes into the configuration. Locally, native-packages signs with the key your GnuPG agent already holds. In CI, export the secret key and store it as a secret:
gpg --armor --export-secret-keys FINGERPRINT
Pass it to the publish step as NATIVE_PACKAGES_GPG_KEY, and the key’s
passphrase, if it has one, as NATIVE_PACKAGES_GPG_PASSPHRASE.
native-packages removes both from its environment before running any other
program, imports the key into a temporary keyring that it deletes
afterwards, and checks every signature against the configured fingerprint
before writing it.
2. Configure the repositories
apt:
directory: ../hello-site/apt
url: https://example.org/apt
signing_key: 0123456789ABCDEF0123456789ABCDEF01234567
keep: 3
dnf:
directory: ../hello-site/rpm
url: https://example.org/rpm
signing_key: 0123456789ABCDEF0123456789ABCDEF01234567
flatpak:
# The app's id, runtime, command and permissions, as for any Flatpak.
repository:
directory: ../hello-site/flatpak
url: https://example.org/flatpak
title: Hello
signing_key: 0123456789ABCDEF0123456789ABCDEF01234567
| Field | Purpose |
|---|---|
directory |
Required. Where the repository is written, relative to the configuration file. |
url |
Required. The https:// URL at which that directory is served. |
signing_key |
Required. The signing key’s full fingerprint. |
keep |
APT and DNF only. How many versions of each package and architecture to keep. Defaults to 3. |
suite, component |
APT only. Default to stable and main. |
title, homepage |
Flatpak only. The repository’s name in software centers (defaults to the package name) and an https:// homepage. |
3. Publish
Build as usual, then publish the build to the repositories:
native-packages build --version 1.2.3
native-packages publish --from dist/packages/1.2.3 --to github,apt,dnf,flatpak
Each publication adds the build’s packages to what the directory already
holds, removes versions beyond keep, and rewrites and signs the indexes. A
file that is already published can only be published again unchanged. Then
commit and push the directory.
APT
publish --to apt copies each .deb into pool/main/h/hello/ and writes
dists/stable/ with Packages and Packages.gz for every architecture,
Release, a clearsigned InRelease and a detached Release.gpg. These are
written by native-packages itself, so no Debian tools are needed. Beside them
go the public key as hello.gpg and a hello.sources file. Users install
with:
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSLo /etc/apt/keyrings/hello.gpg https://example.org/apt/hello.gpg
sudo curl -fsSLo /etc/apt/sources.list.d/hello.sources https://example.org/apt/hello.sources
sudo apt update && sudo apt install hello
The .sources file trusts this key for this repository alone. Use it on
apt 1.1 and later (Debian 9, Ubuntu 16.04 and newer).
DNF
publish --to dnf copies each .rpm into packages/, runs createrepo_c
to write repodata/, and signs repodata/repomd.xml as
repodata/repomd.xml.asc. Beside them go the public key as hello.asc and
a hello.repo file. Users install with:
sudo curl -fsSLo /etc/yum.repos.d/hello.repo https://example.org/rpm/hello.repo
sudo dnf install hello
dnf asks once whether to trust the key, then checks the metadata’s signature
on every update (repo_gpgcheck=1). The packages themselves are signed only
if you sign them, with nFPM’s rpm.signature; hello.repo turns on
gpgcheck when every published RPM carries a signature. The same file works
for Zypper (zypper addrepo https://example.org/rpm/hello.repo). Install
createrepo_c (in the createrepo-c package on Debian and Ubuntu) on the
machine that publishes.
Flatpak
publish --to flatpak imports each Flatpak bundle into an OSTree
repository in the directory, signing its commits, then updates the
repository’s summary and static deltas with flatpak build-update-repo.
Each bundle’s commit stands alone, without the previous version as its
parent, so the repository serves each app’s newest commit and prunes the
rest; users cannot roll back to an older version through it. Beside it go
hello.flatpakrepo, for adding the repository, and ID.flatpakref, for
installing the app, both carrying the public key. Users install with:
flatpak install --user https://example.org/flatpak/org.example.Hello.flatpakref
and get updates from flatpak update. If flatpak.runtime_repo is set, a
missing runtime comes from there. Publishing needs flatpak and ostree.
This is your own repository, not Flathub. native-packages does not write or submit Flathub manifests, because Flathub does not accept AI-assisted manifests or submissions.
GitHub Pages
Serve the site from a branch or folder, and add an empty .nojekyll file
at its root so GitHub publishes the repository’s files as they are. A
release job can check out the site beside the app:
- uses: actions/checkout@v7
with:
repository: example/hello-site
path: hello-site
token: ${{ secrets.SITE_TOKEN }}
- run: native-packages publish --from dist/native-packages --to apt,dnf,flatpak
env:
NATIVE_PACKAGES_GPG_KEY: ${{ secrets.PACKAGES_GPG_KEY }}
- run: |
cd hello-site
git add -A
git commit -m "Publish hello $VERSION"
git push
With directories such as hello-site/apt in the configuration, the
repositories land in that checkout. GitHub Pages serves sites of up to 1 GB, so keep keep low
for large packages.