Metadata-Version: 2.3
Name: pinexq-cli
Version: 0.3.11
Summary: pinexq command-line interface
Author: Sebastian Höfer, Sebastian Boerakker
Author-email: Sebastian Höfer <hoefer@data-cybernetics.com>, Sebastian Boerakker <boerakker@data-cybernetics.com>
License: MIT
Requires-Dist: typer>=0.12.5
Requires-Dist: copier>=9.10.2
Requires-Dist: docker>=7.1.0
Requires-Dist: dotenv>=0.9.9
Requires-Dist: pinexq-client>=1.3.0
Requires-Dist: pydantic>=2.12.0
Requires-Dist: rich>=14.2.0
Requires-Dist: tomli>=2.3.0
Requires-Dist: importlib>=1.0.4
Maintainer: Sebastian Höfer, Sebastian Boerakker
Maintainer-email: Sebastian Höfer <hoefer@data-cybernetics.com>, Sebastian Boerakker <boerakker@data-cybernetics.com>
Requires-Python: >=3.13
Description-Content-Type: text/markdown

# pinexq CLI

A minimal command-line interface for creating and running pinexq workers.

Install (editable):

- Using uv or pip, from the project root:
  - `uv pip install -e .` or `pip install -e .`

Usage:

- Global options must precede the subcommand.
- Authentication: Provide `--api-key` or set the `PINEXQ_API_KEY` environment variable. The CLI prefers the flag over the env var.

## Deploying on Docker Desktop (containerd image store)

Fresh Docker Desktop installs since version 4.34.0 default to the **containerd
image store**. This changes what a local image looks like, and both differences
are expected and supported by `pinexq deploy`:

- An image ID is the manifest (or index) digest rather than the config digest.
- A `docker build` produces an OCI image index that also carries provenance and
  SBOM attestation manifests. This is normal; the deploy pushes it and registers
  the digest the registry reports.

Check which store is active:

```bash
docker info --format '{{ .DriverStatus }}'   # containerd store shows "io.containerd..."
```

Note that `docker system prune` **without `-a`** does not remove tagged images —
so if a bad deploy is fixed by "prune and retry", it is the retry's fresh push
that fixes it, not the prune.

## Troubleshooting: `ImagePullBackOff` right after a deploy

If a function pod fails to pull the image you just deployed, work through this
checklist. The full analysis is in
[`docs/research/2026-08-28-imagepullbackoff-after-successful-push.md`](docs/research/2026-08-28-imagepullbackoff-after-successful-push.md).

1. **Re-run the deploy with `--verbose`.** The push stream is printed chunk by
   chunk, so a swallowed registry error (`denied`, `unauthorized`, `blob upload
   unknown`) becomes visible. Since the fix in this repo, a failed push aborts the
   deploy instead of registering an unpullable digest.

2. **Read the pod events and map the error text to a cause:**

   ```bash
   kubectl describe pod <pod>
   ```

   - `failed to resolve reference ... not found` → the registered digest is not a
     manifest the registry holds for this repository (the classic bug).
   - `failed to authorize ... 401 Unauthorized` → registry token / pull-secret
     problem, not a missing image.
   - `could not fetch content descriptor ... not found` / `httpReadSeeker: failed
     open` → the manifest exists but a referenced blob is missing.
   - A `localhost:6443` / mirror address in the message is the k3s Spegel mirror
     being tried first; the upstream registry error follows it.

3. **Inspect the local image digests** (compare against what was registered):

   ```bash
   docker image inspect --format '{{json .RepoDigests}}' <name:tag>
   docker image inspect --format '{{ .Id }}' <name:tag>
   docker buildx imagetools inspect <registry>/<context>/<function>:<version>
   ```

4. **Ask the registry directly** whether the digest is present:

   ```bash
   # with a token from the registry realm
   curl -sI -H "Authorization: Bearer $TOKEN" \
     https://<registry>/v2/<context>/<function>/manifests/<digest>
   # or, if available:
   skopeo inspect --raw docker://<registry>/<context>/<function>@<digest>
   crane manifest <registry>/<context>/<function>@<digest>
   ```

5. **Check the registry pod** for restarts or `manifest unknown` log lines around
   the time of the deploy:

   ```bash
   kubectl -n <ns> logs deploy/registry --since=1h | grep -iE 'manifest|blob|unknown'
   kubectl -n <ns> get pod -l app.kubernetes.io/name=registry
   ```