Skip to content

Migration Patterns

Most tools you will publish to OCX already ship binaries somewhere — a Homebrew formula, a GitHub Release zip, an APT repository, or a vendor's signed installer. The publisher work is reformatting those artefacts into OCI image manifests without re-bundling content. This page covers the recurring migration patterns and points at the ocx_mirror tool that automates them.

The ocx_mirror Pipeline

ocx_mirror is the automation backbone every in-tree mirror uses to keep public OCX packages in sync with their upstreams. It reads a YAML spec describing where to find upstream releases, which assets to grab per platform, and how to map them onto OCX metadata, then drives a download → bundle → push pipeline through the same ocx_lib publisher API the ocx package commands use.

The advantage over running the commands manually: the pipeline batches --cascade resolution across platforms in one publisher invocation, runs the upstream → archive transformation deterministically, and exposes a versions.new_per_run knob (Option<usize>, unlimited when omitted) so a single CI run doesn't spam the registry with a backlog of upstream releases. The cross-release layer-reuse pattern is a hand-publisher technique — ocx_mirror always uploads each platform's archive afresh.

A minimal spec that wraps a single upstream tool:

yaml
name: mytool
target:
  registry: ocx.sh
  repository: mytool

source:
  type: github_release
  owner: example-org
  repo: mytool
  tag_pattern: "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"

assets:
  linux/amd64:
    - "mytool-.*-linux-x86_64\\.tar\\.gz"
  linux/arm64:
    - "mytool-.*-linux-aarch64\\.tar\\.gz"

asset_type:
  type: archive
  strip_components: 1

metadata:
  default: metadata.json

cascade defaults to true; set cascade: false only for repositories that should not advance rolling tags. A non-trivial worked example covers the multi-platform asset matrix, version-range filtering, and per-platform metadata overrides for a single upstream:

yaml
name: cmake
target:
  registry: ocx.sh
  repository: cmake

source:
  type: github_release
  owner: Kitware
  repo: CMake
  tag_pattern: "^v(?P<version>\\d+\\.\\d+\\.\\d+)$"

# Multi-platform asset matrix: each platform lists one or more regexes;
# the union must resolve to exactly one asset filename per release.
assets:
  linux/amd64:
    - "cmake-.*-linux-x86_64\\.tar\\.gz"      # >= 3.20.0
    - "cmake-.*-Linux-x86_64\\.tar\\.gz"      # < 3.20.0
  darwin/arm64:
    - "cmake-.*-macos-universal\\.tar\\.gz"   # universal fat binary
  windows/amd64:
    - "cmake-.*-windows-x86_64\\.zip"         # >= 3.20.0
    - "cmake-.*-win64-x64\\.zip"              # < 3.20.0

asset_type:
  type: archive
  strip_components: 1

# Per-platform metadata overrides for a non-trivial upstream.
metadata:
  default: metadata.json
  platforms:
    darwin/arm64: metadata-darwin.json
    windows/amd64: metadata-windows.json

# Version-range filtering: only mirror releases >= 3.31.0, max 10 per run.
versions:
  min: "3.31.0"
  new_per_run: 10

skip_prereleases: true
cascade: true

Repackaging GitHub Releases

Most modern open-source binary tools ship via GitHub Releases. The mirror pipeline's github_release source type reads the release feed, downloads the assets matching the platform regex, and pushes them through the same ocx_lib publisher API used by ocx package create / push. Hand-driven publishers can mimic the same flow with a few curl calls and a script — the wins from going through ocx_mirror are deterministic ordering across runs and the dedicated digest-verification slot (verify.github_asset_digest).

The platform regex matrix is the part publishers usually re-derive every time. Two CMake-style worked examples covering the asset-naming-changed-mid-release case:

yaml
linux/amd64:
  - "cmake-.*-linux-x86_64\\.tar\\.gz"      # >= 3.20.0
  - "cmake-.*-Linux-x86_64\\.tar\\.gz"      # < 3.20.0

The pipeline applies every regex and requires the union of matches to resolve to exactly one asset filename per release; ambiguous matches abort with Ambiguous. Two regexes that match the same upstream filename are fine; two regexes that match different filenames are an error. See the CMake worked example above for the full matrix.

Repackaging Homebrew Formulae

Homebrew formulae are Ruby DSL programs, not declarative manifests — every formula encodes its own download URL, build steps, and install hook. There is no general-purpose "import a Homebrew formula" path. The migration pattern is to read the formula's bottle do … root_url … sha256 … block (for binary bottles) or the formula's url + sha256 (for source releases), mirror those bytes into a github_release-shaped pipeline (or fetch directly), and write OCX metadata.json covering the env entries Homebrew would have set in its post-install script.

Binary Homebrew bottles unpack as <formula>/<version>/bin/..., so strip_components: 2 matches the typical layout. Pair that with a PATH env entry marked "visibility": "public" (the default is private) so consumers actually see the binaries on PATH. Source-built Homebrew formulae do not migrate cleanly without rebuilding, since OCX is a binary package manager.

Attaching Description Metadata

Once the package is in the registry, publishers attach a README, logo, title, search keywords, and short description via ocx package describe. The fields land as an OCI image manifest pushed under the dedicated internal tag __ocx.desc, so the description travels with every registry export and powers the package catalog. The round-trip is cheap to verify: ocx package info reads the same fields back, with --save-readme writing the published markdown to disk for diffing.

sh
ocx package describe \
  --readme README.md \
  --logo logo.svg \
  --title "mytool" \
  --description "A small example tool" \
  --keywords cli,linting \
  mytool
ocx package info mytool
Attaching package descriptions and reading them back

Re-publish runs do not auto-refresh descriptions

The ocx_mirror pipeline pushes packages, not descriptions — ocx package describe is a separate hand-driven step. Re-run it whenever your README or logo changes; otherwise the registry keeps the previously published __ocx.desc manifest.

See Also