Getting Started¶
This walkthrough mirrors a real tool — shfmt, a single-binary shell formatter released on GitHub Releases — into an OCI registry. You will write a mirror.yml, validate it, dry-run it, sync it, and finally scaffold a mirror repository with generated CI workflows.
Install ocx-mirror¶
ocx --global add ocx.sh/ocx/mirror
Step 1: Describe the tool¶
A mirror spec answers three questions: where do releases come from, which asset belongs to which platform, and where should packages go. Create mirror.yml:
name: shfmt
target:
registry: ocx.sh # any OCI registry you can push to
repository: shfmt
source:
type: github_release
owner: mvdan
repo: sh
tag_pattern: "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"
assets:
linux/amd64:
- "shfmt_v.*_linux_amd64$"
darwin/arm64:
- "shfmt_v.*_darwin_arm64$"
# shfmt ships raw executables, not archives.
asset_type:
type: binary
name: shfmt
Three things to note:
tag_patternis a regex with a named(?P<version>...)capture group — it turns upstream git tags (v3.10.0) into package versions (3.10.0).- Each
assetsentry is a list of regexes matched against upstream asset filenames. Exactly one asset must match per platform; zero matches skips the platform for that version. asset_typedefaults toarchive(extract a tar/zip). Single-binary tools usebinarywith the executable name.
The full field list lives in the mirror.yml reference.
Step 2: Validate the spec¶
ocx-mirror package validate mirror.yml
Schema errors, invalid regexes, and missing capture groups are reported with exit code 65 — nothing touches the network.
Step 3: Dry run¶
check runs the full discovery pass — list upstream releases, resolve assets per platform, compare against the tags already in the target registry — without downloading or pushing anything:
ocx-mirror package check mirror.yml
The output is a table of (version, platform) pairs and what a real run would do, followed by a total / pushed / skipped / failed summary. Use --latest to restrict to the highest version, or --version 3.10.0 for one exact version.
Step 4: Mirror¶
ocx-mirror package sync mirror.yml --latest
sync downloads the matched assets, bundles them as OCX packages, and pushes one tag per (version, platform) — concurrently for downloads, sequentially for pushes so rolling tags cascade in semver order. Drop --latest to backfill every version the spec's filters admit.
Registry credentials
ocx-mirror reuses OCX's registry auth. Credentials from docker login <registry> are picked up automatically via the Docker credential fallback.
GitHub API rate limits
Release listing is unauthenticated by default (60 requests/hour). Set GITHUB_TOKEN to raise the quota to 5 000 requests/hour — required for backfilling release-heavy tools.
Tag re-points and digest pins
By default each build is published under a unique X.Y.Z_<timestamp> tag, so a re-publish never fully orphans a prior digest — safe for consumers that pin by @sha256:. Setting build_timestamp: none publishes bare X.Y.Z tags that re-point in place; combined with cascade, a rebuild can leave the old digest exposed to registry garbage collection. Read build_timestamp & GC-safe publishing before turning timestamps off.
Step 5: Scaffold a mirror repository¶
One-shot syncs work, but a mirror should run on a schedule and never publish a broken binary. pipeline generate ci renders complete GitHub Actions workflows that discover new versions, build bundles, smoke-test every (version, platform) pair on a real runner, and only push the green ones.
The generated pipeline needs two more spec sections — what to test and where to test it:
tests:
- name: version
command: shfmt --version
platforms:
linux/amd64:
runner: ubuntu-latest
darwin/arm64:
runner: macos-latest
Then, from the root of the mirror repository:
ocx-mirror package pipeline generate ci
This writes three workflows under .github/workflows/:
| File | Purpose |
|---|---|
mirror.yml |
The pipeline: discover → prepare → test → push → notify |
describe.yml |
Publishes catalog metadata (README + logo) to the registry |
verify-generated.yml |
Drift guard — fails CI when generated workflows are hand-edited |
The workflows are generated files: edit the spec, re-run ocx-mirror package pipeline generate ci, and commit the result. --check mode (used by the drift guard) exits 65 when the committed files no longer match the spec.
Finally, configure two repository secrets so the push job can log in to the target registry: OCX_MIRROR_REGISTRY_USER and OCX_MIRROR_REGISTRY_TOKEN. Without them the pipeline still runs — in test/validation mode with the registry push skipped.
Next steps¶
- Pin version windows, exclude broken releases per platform, and wire up Discord reports — mirror.yml reference.
- All subcommands and flags — CLI reference.
- Variables read by the pipeline subcommands — Environment reference.