Skip to content

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_pattern is a regex with a named (?P<version>...) capture group — it turns upstream git tags (v3.10.0) into package versions (3.10.0).
  • Each assets entry 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_type defaults to archive (extract a tar/zip). Single-binary tools use binary with 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