warmhub 0.14.2

Historical changelog shipped with this version. It may include earlier releases.

Changelog — warmhub

0.14.2

Repository export v3.

Breaking

Checkpoint generation is retired — take an export instead

What it was. Producing a fresh snapshot of a repository meant asking the server to mint a checkpoint, polling status until the job completed, requesting a short-lived signed URL, downloading the archive outside the SDK, and verifying the bytes locally.

What it is now. One call that streams verified rows. Framing, canonical decode, per-row schema, ordering and the trailer are checked as the bytes arrive, and a response that dies mid-body is re-requested from the last verified row into the same reader — so a stream that arrived in four pieces still verifies against one digest and one record count.

Migration.

Before:

import time

import httpx

from warmhub import verify_repository_checkpoint_archive

status = client.repo.checkpoint.generate("acme", "catalog")
while status.state != "complete":
    time.sleep(1)
    status = client.repo.checkpoint.status(
        "acme", "catalog", checkpoint_id=status.checkpoint_id
    )

access = client.repo.checkpoint.get_access(
    "acme", "catalog", checkpoint="latest", artifact="archive"
)
# Signed URL: fetched WITHOUT the WarmHub bearer token.
with open("catalog.zip", "wb") as handle:
    handle.write(httpx.get(access.url).content)
verify_repository_checkpoint_archive("catalog.zip")

After:

from warmhub import open_repository_export

export = open_repository_export(client, "acme", "catalog")
for row in export.rows:
    print(row["kind"], row["durableId"], row["version"])

# `reader.trailer` is None until `rows` is fully consumed: the export is
# verified at that moment, not before.
print(export.reader.record_count, export.reader.content_sha256)

If you were relying on generate returning before the download — starting the build in one process and fetching it in another, or on another machine — that shape survives as the token flow:

import time

from warmhub import (
    RepositoryExportPending,
    open_repository_export_token,
    request_repository_export,
)

# Process A: ask for it, get a ticket, do not read a byte.
ticket = request_repository_export(client, "acme", "catalog")
print(ticket.export_token, ticket.at_repo_seq)

# Process B: redeem it. A redemption answered 202 comes back as
# RepositoryExportPending; poll it yourself.
while True:
    opened = open_repository_export_token(
        client, "acme", "catalog", ticket.export_token
    )
    if not isinstance(opened, RepositoryExportPending):
        break
    print("still", opened.state)  # "pending" or "running"
    time.sleep(1)

for row in opened.rows:
    ...

The token is redeemable only at that repository's URL, by a principal holding checkpoint read there.

Why. Export format v3 replaces checkpoint generation outright; the whole generation pipeline is deleted server-side (PR #10020).

repo.checkpoint.generate and .retry are gone

What it was. generate(org_name, repo_name, at_least_repo_seq=...) minted a checkpoint and retry(org_name, repo_name, checkpoint_id) re-ran a failed one, on the unbound namespace and on the repository-bound one, in both the sync and async clients. The payload builders checkpoint_generate_payload and checkpoint_retry_payload are gone from warmhub.namespaces.repo_checkpoint with them.

What it is now. repo.checkpoint is a read plane: status, latest, get_access. The procedures behind generate and retry are no longer served, and the repo:checkpoint-generate scope no longer buys anything.

Migration.

Before:

fresh = client.repo.checkpoint.generate("acme", "catalog", at_least_repo_seq=42)
recovered = client.repository("acme/catalog").repo.checkpoint.retry(
    failed.checkpoint_id
)

After:

from warmhub import open_repository_export

# `at_repo_seq` pins the fence the way `at_least_repo_seq` asked for a floor.
# Omit it and the server pins current and echoes it back in the header.
export = open_repository_export(client, "acme", "catalog", at_repo_seq=42)
for row in export.rows:
    ...

There is no retry to replace: an export is a read, so a failed one is re-read, not re-queued — and the reader resumes a broken transport on its own, up to resume_limit (4 by default).

Why. Same retirement (PR #10020).

Failed checkpoint statuses now all answer next_action="none"

What it was. RepositoryCheckpointFailedDeadlineExceeded and RepositoryCheckpointFailedAttemptsExhausted carried next_action="retry"; RepositoryCheckpointFailedInvalidSource carried next_action="generate". Each named a method a reader could call.

What it is now. All three carry next_action="none". The dataclasses keep their names and their failure_code values, so pattern-matching on failure_code is unaffected; only branching on next_action changes.

Migration.

Before:

if status.state == "failed":
    if status.next_action == "retry":
        client.repo.checkpoint.retry("acme", "catalog", status.checkpoint_id)
    elif status.next_action == "generate":
        client.repo.checkpoint.generate("acme", "catalog")
    elif status.next_action == "contact_support":
        report(status.checkpoint_id, status.failure_code)

After:

if status.state == "failed":
    if status.next_action == "contact_support":
        report(status.checkpoint_id, status.failure_code)
    else:
        # Terminal. Take an export instead of reviving the checkpoint.
        export = open_repository_export(client, "acme", "catalog")
        for row in export.rows:
            ...

Why. Each retired value pointed at a route the server no longer serves; leaving them in the vocabulary would send readers to a 404 (PR #10020).

Added

Behavior worth knowing

Still supported

The checkpoint read plane is untouched: repo.checkpoint.status, .latest, .get_access, and verify_repository_checkpoint_archive all continue to work against archives already in object storage. Nothing mints new ones.

Deprecation horizon: those read surfaces are supported until the announced cleanup, 30 days after GA. Their removal is a separately-filed post-GA change. Move download-and-verify workflows to the export surface before then.