No description
Find a file
saces 3df9c446d1 add sign pipeline step
Run each registry's signcmd on its downloaded packages between download
and publish. The command template replaces the {file} placeholder with
the package file path.

created with help from number affine coworker
2026-08-13 13:25:20 +02:00
cmd add sign pipeline step 2026-08-13 13:25:20 +02:00
deb Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
doc Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
github Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
rpm Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
utils Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
.gitignore Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
go.mod Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
go.sum Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
LICENSE Aller Anfang ist schwer. 2026-08-11 11:46:20 +02:00
README.md add sign pipeline step 2026-08-13 13:25:20 +02:00

paketier

⚠️ alpha software, still under development

paketier is a command-line tool written in Go. It matches software packages from a GitHub-compatible release page against Debian and RPM package registries, downloads the packages that are missing, and publishes them to the registries.

How it works

Automatic: ci

ci runs the whole pipeline in one step. It reads a YAML config file (paketier.yaml by default) that lists the source and the registries. The command fetches the source and registry indices, compares them, downloads the missing packages, and publishes them to the registries. See doc/*.example.yaml for an example config.

Manual pipeline

The tool also works in four steps. Each step reads the result of the previous step. All steps use the same config file and storage dir.

  1. dump reads the config file and downloads the source and registry indices.
  2. scan compares the indices. It writes the missing packages to scan.json.
  3. download downloads the missing packages to <storage dir>/packages.
  4. sign signs the downloaded packages.
  5. publish uploads the downloaded packages to the package registry.

Requirements

  • Go 1.24.4 or later.
  • A Forgejo access token to publish packages. The token goes into the credentials file that the config file references (see doc/*.example.yaml).

Build

Run these commands in the project root directory.

go build ./...
go vet ./...

The binary is built from cmd/paketier:

go build -o paketier ./cmd/paketier

Workflow

Automatic: ci

Run the whole pipeline with a single command:

paketier ci

ci reads the paketier.yaml config file in the current directory. Use the --config flag to pass another config file.

Flags for ci

Flag Default Purpose
--config paketier.yaml config file
--storage (see below) storage dir
--continue false log HTTP 404 errors and continue
--no-test false skip the registry index, upload all matching packages
--save false store the fetched registry indices to disk
--dry-run false compare only, do not download or publish

Without --storage, the storage dir is the directory of the config file plus the config file name without its extension. For the default paketier.yaml the storage dir is paketier/. Downloaded packages go to <storage dir>/packages.

With --save, ci writes the fetched indices to the storage dir as source_<type>.json and registry_<name>.json.

Manual pipeline

Run the commands in this order.

  1. Run paketier dump.
  2. Run paketier scan.
  3. Run paketier download.
  4. Run paketier sign.
  5. Run paketier publish.

Each command reads the config file (--config) and the storage dir (--storage). Without --storage, the storage dir is the directory of the config file plus the config file name without its extension.

sign runs the signcmd of each registry for its downloaded packages (see Signing).

publish authenticates against each registry with the credentials file that the config lists for that registry (see doc/*.example.yaml).

Commands

Command Alias Purpose
dump d download the source and registry indices
scan s find the missing packages
download l download the missing packages to <storage>/packages
sign sign the downloaded packages
publish p upload the downloaded packages to the registry

Every command that reads a value has a flag with a built-in default. You can override each default on the command line.

Helper commands

The CLI also provides helper commands:

Command Purpose
config print and validate the config file (--raw prints as-is)
manpage print the CLI help as a manpage
markdown print the CLI help as markdown
ghindex download the raw GitHub releases index
debindex download the raw Debian index
rpmindex download the raw RPM index

The *index commands are experimental. ghindex downloads the release list of an owner/project and writes the parsed JSON. --raw skips parsing, --latest fetches only the latest release, --output writes to a file instead of stdout. debindex downloads the package index of a Debian repository. It takes baseurl, distribution and component. --arch selects the architecture, --release fetches only the Release file. rpmindex downloads the primary index of a yum repository. It takes the repository root as repository url; --arch filters the parsed packages, --repomd fetches only repomd.xml.

Flags for dump

Flag Default Purpose
--config paketier.yaml config file
--storage (see below) storage dir

The tool builds the source and registry URLs from the config file.

dump writes the fetched indices to the storage dir as source_<type>.json and registry_<name>.json. Without --storage, the storage dir is the directory of the config file plus the config file name without its extension, the same default that ci --save uses.

Flags for scan

Flag Default Purpose
--config paketier.yaml config file
--storage (see below) storage dir
--name (empty) package name to compare
--version (empty) version range
--platform (empty) comma-separated architectures

scan reads the source index and the registry indices from the storage dir (source_<type>.json and registry_<name>.json, one per registry in the config file). It writes the missing packages to <storage dir>/scan.json.

An empty --name matches every package. An empty --version matches every version. An empty --platform uses the architectures that the registry already carries.

Debian and RPM use different names for the same machine. Debian uses amd64. RPM uses x86_64. Put both names into --platform to track both.

Examples:

paketier scan
paketier scan --name woodpecker-agent
paketier scan --name woodpecker-agent --version ">=3.17,<4.0"
paketier scan --platform amd64,x86_64

Version ranges

The --version flag accepts a semver-style range. An empty string matches every version.

Value Matches
3.17.0 exactly 3.17.0
=3.17.0 exactly 3.17.0
>3.17.0 higher than 3.17.0
>=3.17 3.17.0 or higher
<4.0 lower than 4.0.0
<=3.17.0 3.17.0 or lower
>=3.17,<4.0 from 3.17.0 to lower than 4.0.0

Comma-separated comparators form one range. They must all match.

Flags for sign

Flag Default Purpose
--config paketier.yaml config file
--storage (see below) storage dir

sign reads <storage dir>/scan.json and runs the signcmd of each registry for its packages (see Signing). It needs the downloaded packages in <storage dir>/packages.

Flags for publish

Flag Default Purpose
--config paketier.yaml config file
--storage (see below) storage dir

publish reads <storage dir>/scan.json, downloads nothing, and uploads each listed package to the registry that the config file names for it. The token is required. It comes from the credentials file that the config lists for the registry.

Signing

Each registry can have a signcmd in the config file. When set, sign (and the ci pipeline) runs this command for every downloaded package of that registry, replacing the placeholder {file} with the package file path. The command is run through /bin/sh. Registries without a signcmd are skipped.

Example:

registries:
  qubes-dom0-rpm:
    type: rpm
    name: woodpecker-cli-*.x86_64.rpm
    url: https://codeberg.org/api/packages/saces/rpm/qubpecker
    credentials: secrets.yaml
    signcmd: gpg --detach-sign --armor {file}

Publishing

The tool sends each package with an HTTP PUT request to the Forgejo API. It builds the upload URL from the registry URL in the config file.

  • Debian packages go to {registry url}/pool/{distribution}/{component}/upload.
  • RPM packages go to {registry url}/upload.

The token belongs to the user that owns the registry packages. The tool sends the token as HTTP basic authentication.

Notes

  • The tool skips draft and pre-release GitHub releases.
  • Without --platform, scan uses the architectures that the registry already carries.
  • The index files in the storage dir have fixed names: source_<type>.json, registry_<name>.json, scan.json.
  • The tool stops with an error when the registry already has the package version.
  • download runs serially. For many packages, the tool can take a long time.
  • download skips existing non-empty files, so an interrupted run can be resumed.