Metadata-Version: 2.4
Name: ocx-mirror-sdk
Version: 0.6.0
Summary: Python SDK for building ocx-mirror generator scripts
Project-URL: Homepage, https://github.com/ocx-sh/ocx-mirror-sdk
Project-URL: Repository, https://github.com/ocx-sh/ocx-mirror-sdk
Project-URL: Issues, https://github.com/ocx-sh/ocx-mirror-sdk/issues
Project-URL: OCX Project, https://github.com/ocx-sh/ocx
Author: The OCX Authors
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: github-releases,mirror,oci,ocx,registry
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Archiving :: Packaging
Classifier: Typing :: Typed
Requires-Python: >=3.13
Requires-Dist: httpx>=0.28
Provides-Extra: dev
Requires-Dist: coverage>=7.6; extra == 'dev'
Requires-Dist: datamodel-code-generator>=0.26; extra == 'dev'
Requires-Dist: jsonschema>=4; extra == 'dev'
Requires-Dist: pyright>=1.1; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.9; extra == 'dev'
Provides-Extra: docs
Requires-Dist: griffe>=1.5; extra == 'docs'
Requires-Dist: mkdocs-git-revision-date-localized-plugin>=1.2; extra == 'docs'
Requires-Dist: mkdocs-include-markdown-plugin>=7; extra == 'docs'
Requires-Dist: mkdocs-material<10,>=9.5; extra == 'docs'
Requires-Dist: mkdocs-section-index>=0.3; extra == 'docs'
Requires-Dist: mkdocstrings[python]<0.30,>=0.27; extra == 'docs'
Requires-Dist: pymdown-extensions>=10.7; extra == 'docs'
Description-Content-Type: text/markdown

# ocx-mirror-sdk

[![CI](https://github.com/ocx-sh/ocx-mirror-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/ocx-sh/ocx-mirror-sdk/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/ocx-sh/ocx-mirror-sdk/branch/main/graph/badge.svg)](https://codecov.io/gh/ocx-sh/ocx-mirror-sdk)
[![Docs](https://github.com/ocx-sh/ocx-mirror-sdk/actions/workflows/docs.yml/badge.svg)](https://ocx-sh.github.io/ocx-mirror-sdk/)

Python SDK for authoring [`ocx-mirror`](https://github.com/ocx-sh/ocx) generator scripts.

`ocx-mirror` is OCX's tool for ingesting upstream tool releases (e.g. CPython, Bun, shellcheck) and republishing them as OCI artifacts. When upstream releases live somewhere `ocx-mirror` cannot crawl directly, a small Python *generator* emits a `url_index` JSON document. This SDK provides the typed building blocks for those generators.

📖 **Full documentation: <https://docs.ocx.sh/sdk/mirror/>**

## Install

Published on [PyPI](https://pypi.org/project/ocx-mirror-sdk/). In a
[PEP 723](https://peps.python.org/pep-0723/) inline-metadata script
(recommended for one-file generators):

```python
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = ["ocx-mirror-sdk~=0.6.0"]
# ///

from ocx_mirror_sdk import IndexBuilder, github
```

Or in a project:

```bash
uv add ocx-mirror-sdk
```

Pre-1.0, minor releases may break — `~=` pins the patch series. Git-tag and
wheel-asset installs remain available; see the
[install guide](https://docs.ocx.sh/sdk/mirror/getting-started/install/).

## Quickstart

```python
from ocx_mirror_sdk import IndexBuilder, github

# Fetch releases (REST default; switch to GraphQL on big repos)
releases = github.list_releases("shellcheck/shellcheck")

# Or explicitly:
releases = github.list_releases("indygreg/python-build-standalone", backend=github.Backend.GRAPHQL)

# Build a url_index document
builder = IndexBuilder()
for r in releases:
    if r.prerelease or r.draft:
        continue
    builder.add_version(
        r.tag_name.lstrip("v"),
        assets={a.name: a.browser_download_url for a in r.assets},
    )
builder.emit()  # writes JSON to stdout
```

Releases hosted on GitLab use the sibling `gitlab` namespace — same `Release`
objects, same `IndexBuilder`:

```python
from ocx_mirror_sdk import gitlab

# gitlab.com (default); pass host="https://gitlab.example.com" for self-hosted
releases = gitlab.list_releases("gitlab-org/gitlab-runner")
```

More worked examples live under [`examples/`](examples/) and at <https://docs.ocx.sh/sdk/mirror/recipes/>.

## Public API

| Symbol | Purpose |
|---|---|
| `IndexBuilder` | Typed builder for the `url_index` JSON manifest |
| `github.list_releases` | Iterate GitHub releases — single router over REST and GraphQL; takes a `"owner/repo"` slug |
| `gitlab.list_releases` | Iterate GitLab releases — REST; gitlab.com or self-hosted via `host=`; takes a `"namespace/project"` slug |
| `github.Backend` | `StrEnum`: `REST` / `GRAPHQL` backend selector (GitHub) |
| `Asset`, `Release` | Source-agnostic typed views of release assets and releases |
| `extract_urls` | Pull download URLs from release notes |
| `FileCache`, `configure` | Disk-backed HTTP response cache; SDK-wide root override |

### Error hierarchy

Catch `OcxMirrorError` to recover from any SDK failure, or pick a subclass:

| Class | Raised when |
|---|---|
| `ConfigurationError` | `GITHUB_TOKEN` missing, client init failed |
| `TransportError` | Base for network errors |
| `HttpStatusError` | HTTP 4xx/5xx (carries `.status_code`, `.url`, `.response_text`) |
| `HttpTimeoutError` | Request timed out |
| `ApiResponseError` | Malformed JSON, GraphQL `errors` array, repo not found |
| `SchemaError` | Release payload missing or wrongly-typed field |
| `CacheError` | On-disk cache IO or corruption (NOT cache miss) |

Original lower-layer exceptions are preserved on `__cause__`.

## Schema

The `url_index` format is owned by the `ocx-mirror` Rust binary. Its JSON Schema is published at <https://ocx.sh/schemas/url-index/v1.json>. This SDK ships a generated `_schema.py` (under `src/ocx_mirror_sdk/`) regenerated via `task codegen`.

## Development

This repo dogfoods OCX. Install OCX once:

```bash
curl -sSL https://setup.ocx.sh | sh
```

Then everything runs through `ocx run`:

```bash
ocx run -- task verify     # format + lint + types + tests
ocx run -- task test
ocx run -- task codegen    # regenerate _schema.py from the published schema
ocx run -- task docs:serve # live-preview the docs site at localhost:8000
```

OCX bootstraps `task` and `uv`; `uv` pulls Python linters (`ruff`, `pyright`) from `[project.optional-dependencies] dev`, and docs tooling from `[project.optional-dependencies] docs`.

## Stability

Pre-1.0. Breaking changes ship without migration shims. When the upstream `url_index` schema bumps to `/v2.json`, this SDK ships a new major version.

## Coverage

`task verify` enforces ≥80% line + branch coverage on `src/ocx_mirror_sdk` (generated `_schema.py` excluded). Run `task cov:html` and open `htmlcov/index.html` to inspect uncovered lines locally. The threshold lives in `[tool.coverage.report] fail_under` in `pyproject.toml`.

## License

Apache-2.0 — see `LICENSE`.
