uv is becoming really useful, but I noticed there is no apt repo/PPA of it. I decided I wanted to try publishing an apt package repository via Github Pages. The LLMs got me started, but there were a lot of little things to work out, so here’s a complete recipe.
Create a new repo and create a file .github/workflows/debian-apt-repo.yml
with the
content below. This downloads the uv
tool as an example, so you will need to
adjust those sections.
You will also need to create unencrypted GPG key (commands below), and:
- Add it to the repository “Settings” -> “Secrets and varitables” -> “Actions”.
- Create an “environment”, I called mine “Main”.
- Add a “secret” for “GPG_PRIVATE_KEY” with the contents of the armored (ASCII) private key (the “private.asc” in my example).
- Once your private key is in github, delete the key off your system. This means that only github has the key.
- Add a “variable” for “KEY_ID” and paste in the key fingerprint.
- Save the binary public key to the repo as “pubkey.gpg”.
Also create an “index.html” which will be shown if someone goes to the top of the repo with a browser.
If your build fails, try following this: “First deployment with GITHUB_TOKEN”
Commit these to your repo and push, and in the “Actions” tab it should build and
publish the apt repo to https://<YOURNAME>.github.io/<REPONAME>
For an example repo using this, see “linsomniac/uvrepo”
Simon Willison simultaneously published a “recipe for simple site publishing and an example of scraping and publishing an Atom feed”
The Github Workflow
File .github/workflows/debian-apt-repo.yml
:
name: Build & Publish Debian Package
on:
push:
branches:
- main
jobs:
build-and-publish:
permissions:
pages: write
contents: write
environment: Main
runs-on: ubuntu-latest
env:
DISTRIBUTION: any
COMPONENT: main
ARCHITECTURE: amd64
RELEASE: 1
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
KEY_ID: ${{ vars.KEY_ID }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y reprepro dpkg-dev curl jq gnupg debsigs
- name: Import GPG key
run: |
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
echo "$KEY_ID:6:" | gpg --batch --import-ownertrust --pinentry-mode=loopback
- name: Determine latest uv version
id: get-version
run: |
TAG=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | jq -r '.tag_name')
VERSION=${TAG#v}
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Download latest uv binary
run: |
DOWNLOAD_URL=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | jq -r '.assets[] | select(.name | contains("x86_64-unknown-linux-gnu")) | select(.name | endswith(".tar.gz")) | .browser_download_url')
curl -L "$DOWNLOAD_URL" -o uv.tar.gz
mkdir dl
tar xf uv.tar.gz -C dl
- name: Build Debian package
run: |
PKG_VERSION="${{ steps.get-version.outputs.version }}-$RELEASE"
PKG="uv_${PKG_VERSION}_${ARCHITECTURE}.deb"
mkdir -p pkg/DEBIAN pkg/usr/bin
echo "Package: uv" >pkg/DEBIAN/control
echo "Version: ${PKG_VERSION}" >>pkg/DEBIAN/control
echo "Section: utils" >>pkg/DEBIAN/control
echo "Priority: optional" >>pkg/DEBIAN/control
echo "Architecture: ${ARCHITECTURE}" >>pkg/DEBIAN/control
echo "Maintainer: GitHub Actions <actions@github.com>" >>pkg/DEBIAN/control
echo "Description: UV CLI binary" >>pkg/DEBIAN/control
fakeroot bash -c "install -o root -g root -m 755 dl/*/uv* pkg/usr/bin ; dpkg-deb --build pkg $PKG"
- name: Sign Debian package
run: |
debsigs -v --gpgopts="--batch --no-tty --pinentry-mode=loopback" --sign=origin --default-key="$KEY_ID" uv*.deb
- name: Create APT repository with reprepro
run: |
mkdir -p repo/conf
echo "Origin: GitHub" >repo/conf/distributions
echo "Label: GitHub UV" >>repo/conf/distributions
echo "Suite: stable" >>repo/conf/distributions
echo "Codename: ${DISTRIBUTION}" >>repo/conf/distributions
echo "Components: ${COMPONENT}" >>repo/conf/distributions
echo "Architectures: ${ARCHITECTURE}" >>repo/conf/distributions
echo "SignWith: $KEY_ID" >>repo/conf/distributions
reprepro -Vb repo includedeb ${DISTRIBUTION} "uv_${{ steps.get-version.outputs.version }}-${RELEASE}_${ARCHITECTURE}.deb"
cp pubkey.gpg repo/pubkey.gpg
cp index.html repo/
- name: Deploy APT repository to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./repo
Creating the GPG Key
rm -rf repo-key ; mkdir repo-key ; chmod 700 repo-key
echo "batch" >repo-key/gpg.conf
echo "pinentry-mode loopback" >>repo-key/gpg.conf
gpg --full-generate-key --homedir repo-key --passphrase ''
gpg --list-keys --with-keygrip --homedir repo-key
gpg --homedir repo-key --armor --export-secret-keys [EMAIL_ADDR] >private.asc
gpg --homedir repo-key --export [EMAIL_ADDR] >public.gpg