sow create
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
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
.rpmor.debare 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.
--pigstyaccepts an empty authoritative set so an interrupted all-package cleanup can converge and write its marker.
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:
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:
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:
- Delete DEB packages whose parsed architecture is
i386. RPMs are not removed merely for carrying ani386/i486/i586/i686architecture. - Delete RPM/DEB whose binary package name is exactly
patroniand whose upstream version is exactly3.0.4. RPM comparesVERSION, ignoring epoch and release; DEB strips epoch and Debian revision first.3.0.4+foois not a match. - 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>.
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.
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.
--overwriterequires--sign-withand switches torpm --resignover 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
--pigstycleanup, andrpmmust be onPATH.
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:
Machine-readable result:
Replace a Pigsty plain build with eight workers:
Failure envelope:
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
- Plain Flat Repositories — the design behind
create - Quick Start — five-minute flat repository walkthrough
- Repository Layout — what the flat tree looks like
- Sign Your Repository — generating and using a signing key