Skip to content

sow create

Generate a flat RPM/DEB repository in an ordinary directory — the Plain mode entry point.

sow create turns a directory that already contains .rpm and .deb files into a flat repository by writing indexes next to the packages. It is the whole of Plain mode: no sow.yml, no SQLite, no Workspace discovery. This page covers the one-pass scan contract, the --pigsty completion gate, and RPM signing with --sign-with.

Synopsis

sow create [DIR] [-j N] [--pigsty] [-S KEY [--overwrite]] [-T DUR | -N] [--json]

DIR defaults to the current directory.

Description

create reads the top-level regular files in DIR and renders the index formats implied by what it finds: repodata/ when RPMs are present, Packages and Packages.gz when DEBs are present, both when the directory is mixed. All architectures come from the package headers — Plain mode has no architecture flag and no permit list.

Flat metadata only ever references packages in the same directory. RPM location is the bare basename and DEB Filename is ./<basename>, so both remain relative whether the directory is exposed as a file:// source or an HTTP root.

By default create does not delete, move, rename, re-sign or rewrite a single package byte. It only replaces index paths it owns; unknown files are left alone.

Options

Flag Description Default
-j, --jobs N Parallel workers for the single package hash/parse pass logical CPU count
--pigsty Enable Pigsty compatibility cleanup and completion marker off
-S, --sign-with KEY Sign unsigned RPMs with a 16/40/64-hex GPG key ID off
--overwrite Re-sign every RPM; requires --sign-with off
-T, --timeout DUR Maximum lock wait; 0 waits indefinitely 0
-N, --no-wait Fail immediately when the lock is held false
--json Emit the versioned JSON envelope false
-h, --help Show help

Scan rules

  • Only top-level regular files ending in .rpm or .deb are considered.
  • No recursion, no symlink following, no Workspace config.
  • Every valid version enters the index. Two files claiming the same logical coordinate with different content is a hard failure.
  • A directory with no supported package is rejected in default mode. --pigsty accepts an empty authoritative set so an interrupted all-package cleanup can converge and write its marker.
sow create /srv/empty
plain: scan /srv/empty: no supported top-level regular RPM or DEB packages

Package I/O and final validation

For the normal unsigned path, each selected package has exactly one full content pass. A worker opens it, computes SHA-256 once, parses its header/control metadata, and retains the complete parsed result. RPM XML and DEB Packages are rendered from that retained result; neither rendering nor generated metadata validation reopens package payloads. --jobs parallelizes this pass while canonical result ordering keeps output bytes independent of worker scheduling.

Immediately before publication, create relists the top-level package set and compares file identity, type/mode, size, and mtime with the post-scan snapshot. This is a cheap stat check, not a second hash. A changed set or stat returns integrity error 5 before any staged output is published. Deliberately preserving inode, size, and mtime while modifying bytes is outside the local cooperative-writer contract.

Explicit RPM signing is an exception: copying, signing, signature verification, and parsing the final signed RPM necessarily add reads for packages that are modified.

Deterministic output and idempotence

The rendered metadata is byte-stable for a given input set: gzip output is deterministic, repomd.xml carries <revision>0</revision> and timestamp 0. Running create twice on an unchanged directory rewrites nothing and reports noop=true:

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=false recovered=false

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=true recovered=false

The repo_complete gate

Default mode never creates repo_complete. If the marker already exists, create refuses to write indexes rather than leaving a stale marker claiming a build that no longer matches:

sow create /srv/pigsty
plain: marker gate /srv/pigsty/repo_complete: repo_complete exists; use --pigsty or remove it explicitly before rebuilding

Either re-run with --pigsty (which withdraws and republishes the marker in its documented order) or remove the marker yourself.

–pigsty

--pigsty enables three coupled compatibility actions in one invocation. Their publication order is marker-gated, but the operation is rebuilt on retry rather than recovered from a journal:

  1. Delete DEB packages whose parsed architecture is i386. RPMs are not removed merely for carrying an i386/i486/i586/i686 architecture.
  2. Delete RPM/DEB whose binary package name is exactly patroni and whose upstream version is exactly 3.0.4. RPM compares VERSION, ignoring epoch and release; DEB strips epoch and Debian revision first. 3.0.4+foo is not a match.
  3. After all indexes render successfully, write repo_complete: the SHA-256 of every remaining top-level RPM/DEB, sorted by basename byte order, formatted <sha256><two spaces><basename>.
sow create /srv/pigsty --pigsty
created /srv/pigsty: rpm=2 deb=0 signed=0 removed=2 marker=true noop=false recovered=false
cat /srv/pigsty/repo_complete
b4111ef2a51542eacc9bd1ebd080da02e53d400f9d172530c75a1e4ac06e7ead  centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm
d6f332ed157de1d42058ec785b392a1cc4b5836c27830af8fbf083cce29ef0ab  epel-release-7-5.noarch.rpm

Cleanup only touches top-level regular package files that parsed successfully and matched a rule. Directories and unknown files are never removed by glob.

The publication order matters for callers that gate on the marker: the existing repo_complete is withdrawn before indexes switch, matched packages are deleted only after replacement metadata is installed, and the new marker is written last. A caller must treat a missing marker as incomplete.

Marker semantics

Treat a missing repo_complete as “build in progress”. That is the contract --pigsty is designed around.

Signing RPMs

-S/--sign-with KEY is the explicit authorization to modify RPM bytes. KEY is exactly 16, 40 or 64 hexadecimal characters; an 0x prefix is not accepted. SOW normalizes it to uppercase and passes it to the environment’s rpm --addsign through the _gpg_name macro. The private key, passphrase, GPG home, pinentry and any extra RPM macros come from your environment — SOW never receives, persists or echoes a secret.

  • Default: only RPMs with no parseable embedded OpenPGP signature are signed. Anything already signed keeps its bytes.
  • --overwrite requires --sign-with and switches to rpm --resign over every retained RPM.
  • Signing happens on a private same-filesystem stage copy. Each result is re-parsed to confirm the embedded signature, the signature-neutral digest and NEVRA are unchanged, and rpm-md is generated from the final complete bytes.
  • At least one top-level RPM must remain after --pigsty cleanup, and rpm must be on PATH.
sow create /srv/flat -S 0123456789ABCDEF --overwrite
plain: sign rpm epel-release-7-5.noarch.rpm: rpm executable is required for --sign-with
sow create /srv/deb-only -S 0123456789ABCDEF
plain: sign rpm: --sign-with requires at least one retained top-level RPM package
sow create /srv/flat --overwrite
usage error: --overwrite requires --sign-with
sow create /srv/flat -S ZZZZ
usage error: --sign-with must be a 16, 40, or 64 hexadecimal GPG key ID/fingerprint

Locking, staging and overwrite rebuild

create takes a write lock on the target directory and honors --timeout/--no-wait. All metadata is written to a private stage and validated before publication begins. The lock coordinates SOW writers on the local machine; arbitrary external package mutation is unsupported.

Plain create does not create a durable operation journal, rollback pre-images, or recovery trash. Publication consists of several single-file renames, so a crash may leave a partially replaced set of derived files. Re-run sow create with the intended current options: it discards reserved stale Plain temporary state and rebuilds all indexes from the packages that currently exist. recovered is always false; a rerun is a fresh overwrite build, not replay.

A flat directory has no whole-repository generation pointer, and RPM plus DEB entry points cannot be swapped in one POSIX rename. Plain therefore does not promise cross-file instantaneous atomicity. Use repo_complete as the --pigsty gate, or use Managed mode when transactional recovery is required.

Examples

Index a mixed directory:

sow create /srv/flat
created /srv/flat: rpm=3 deb=1 signed=0 removed=0 marker=false noop=false recovered=false
ls /srv/flat
centos-release-6-0.el6.centos.5.x86_64.rpm
centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm
epel-release-7-5.noarch.rpm
libpq5_18.3-1_amd64.deb
Packages
Packages.gz
repodata

Machine-readable result:

sow create /srv/flat --json
{"schema":"sow.cli/v1","command":"create","ok":true,"repository":null,"operation":null,"result":{"dir":"/srv/flat","rpm":3,"deb":1,"kept":["centos-release-6-0.el6.centos.5.x86_64.rpm","centos-release-7-2.1511.el7.centos.2.10.x86_64.rpm","epel-release-7-5.noarch.rpm","libpq5_18.3-1_amd64.deb"],"removed":[],"marker":false,"noop":true,"recovered":false},"errors":[]}

Replace a Pigsty plain build with eight workers:

sow create /www/pigsty -j 8 --pigsty

Failure envelope:

sow create /srv/empty --json
{"schema":"sow.cli/v1","command":"create","ok":false,"repository":null,"operation":null,"result":{"dir":"","rpm":0,"deb":0,"kept":null,"removed":null,"marker":false,"noop":false,"recovered":false},"errors":[{"code":6,"class":"rejected","message":"operation rejected: plain: scan /srv/empty: no supported top-level regular RPM or DEB packages"}]}

Exit codes

Code Trigger
0 Indexes written, or unchanged input produced a no-op
1 Directory unreadable or missing, package parse failure, renderer failure, signing tool failure
2 Usage error — --overwrite without --sign-with, malformed key, --no-wait with a non-zero --timeout
4 Directory write lock held and --no-wait given or --timeout expired
5 Input set/stat changed before publication, or a controlled output path failed an integrity check
6 No supported package found, repo_complete gate hit, --sign-with on a DEB-only directory, coordinate conflict

See also