# List available recipes
default:
    @just --list

# Run the full test suite
test:
    uv run pytest

# Lint, format-check and type-check — the same gates CI used to run
check:
    uv run ruff check
    uv run ruff format --check
    uv run ty check

# Everything a change must pass before it merges
verify: check test

# The manifests the pre-baked ComfyUI image is built for. Not in this repo:
# they are the operator's own library, and only this machine has them, which
# is why docker/nodes.plan is committed rather than derived inside CI.
BAKE_MANIFESTS := "~/.local/share/curu/manifests/core.toml ~/.local/share/curu/manifests/realism_anima.toml"

# Regenerate docker/nodes.plan from those manifests, picking up the newest
# ComfyUI release and the frontend it pins. Reaches the network; the versions
# it writes are concrete, never the word "latest".
#
# Pin instead of following upstream by passing both:
#   just bake-plan '--comfyui-ref v0.30.0 --frontend-version 1.47.11'
bake-plan pins="":
    uv run curu-infra nodes bake-plan {{BAKE_MANIFESTS}} {{pins}} > docker/nodes.plan
    @just bake-tag

# Fail if docker/nodes.plan no longer matches the manifests. Run this after
# editing a manifest's custom_nodes: a stale plan means the published image
# bakes the wrong set, and nothing else notices until a cold start is slow.
#
# Offline and deterministic on purpose — it takes the ComfyUI/frontend versions
# from the plan itself rather than asking GitHub what is newest, so it cannot go
# red because somebody else cut a release. "Is there a newer ComfyUI?" is a
# different question, answered by `just bake-plan`.
bake-check:
    uv run curu-infra nodes bake-plan {{BAKE_MANIFESTS}} --check docker/nodes.plan

# The image tag the current plan calls for — compare against what ghcr has
bake-tag:
    @uv run python -c "import pathlib, curu_infra.bake_commands as b; print(b.bake_plan_hash(pathlib.Path('docker/nodes.plan').read_text()))"

# python-semantic-release 10.6.1 calls `Actor.name_email_regex`
# (semantic_release/cli/config.py:745); GitPython removed that attribute in
# 3.1.60. Unpinned, uvx resolves the newest GitPython and EVERY release
# command dies with "type object 'Actor' has no attribute 'name_email_regex'"
# -- an internal traceback that names neither package, which is why 0.59.1
# was the last release to land while #148 and #149 sat merged-but-unshipped.
# 10.6.1 is the newest PSR, so there is no upstream fix to move to yet.
PSR := "uvx --from python-semantic-release --with 'gitpython<3.1.60' semantic-release"

# Show what the next release would be, and change nothing.
# Always run this before `release`.
release-preview:
    @{{PSR}} version --print

# Replaces the old `.github/workflows/release.yml`. That workflow used PyPI
# Trusted Publishing, which broke (issue #140) and left `main` tagged at a
# version that did not exist on PyPI — a failure mode this recipe avoids by
# publishing from the same machine that cut the tag.
#
# Requires PYPI_TOKEN in the environment:
#     set -a && . ~/.pypi && set +a && just release
#
# `verify` runs first and a failure stops the release — the local suite is
# the real gate for this project, so nothing ships that has not passed it.
#
# Bump the version from commit history, tag, build and publish to PyPI
release: verify
    #!/usr/bin/env bash
    set -euo pipefail
    if [ -z "${PYPI_TOKEN:-}" ]; then
        echo "PYPI_TOKEN is not set. Load it first:" >&2
        echo "    set -a && . ~/.pypi && set +a" >&2
        exit 1
    fi
    if [ -n "$(git status --porcelain)" ]; then
        echo "Working tree is dirty — commit or stash before releasing." >&2
        exit 1
    fi
    # --no-vcs-release: PSR otherwise tries to create a GitHub Release and
    # aborts the whole recipe on 401 when it has no GH token -- after it has
    # already bumped, tagged and built. Caught cutting 0.59.0. GitHub
    # Releases were an artefact of the Actions workflow this recipe replaced;
    # nothing consumes them now, and PyPI is the real distribution point.
    {{PSR}} version --no-vcs-release
    rm -rf dist
    uv build
    UV_PUBLISH_TOKEN="${PYPI_TOKEN}" uv publish
    git push --follow-tags
    # No `@` prefix here. `just` strips a leading `@` only from a plain
    # recipe line; this recipe has a `#!` shebang, so the whole body is one
    # bash script and `@echo` is executed literally -- "@echo: command not
    # found", exit 127. Caught releasing 0.59.2: PyPI had both artefacts and
    # the tag was pushed, yet the recipe reported failure. A release that
    # succeeds and says it failed invites someone to "retry" it, and the
    # retry is what actually breaks things.
    echo "Released. Verify with:"
    echo "    python3 -c \"import urllib.request,json; print(json.load(urllib.request.urlopen('https://pypi.org/pypi/curu-infra/json'))['info']['version'])\""

# Was `.github/workflows/smoke.yml`, running nightly at 03:00 UTC. That
# schedule provisioned a real pod every night whether or not anything had
# changed — a standing cost for a signal nobody was reading. Spec 029 US2
# always specified this local path (`RUNPOD_API_KEY`-gated, skips cleanly
# without it); removing the schedule makes it the only trigger.
#
# Run it when provisioning changes, not on a timer:
#     set -a && . ~/.env && set +a && just smoke
#
# Live provisioning smoke test — provisions a REAL RunPod pod and BILLS
smoke:
    uv run pytest tests/integration/test_smoke.py -v
