Skip to main content

papeete-deploy

Resolves a papeete-product's declared version queries against a real registry, and deploys the result. Split out of papeete-product because resolving a query and orchestrating containers both need to touch reality (a registry, a Docker daemon) in ways a product's own identity contract deliberately refuses — see ADR-PD-0001 and papeete-product's own ADR-PP-0002.

papeete-deploy resolve    PRODUCT.YAML [--registry {local,acr}] [--acr-name NAME]
papeete-deploy deploy     PRODUCT.YAML [--registry {local,acr}] [--acr-name NAME]
                          [--actor-source {local,git}] [--actor-root PATH]
                          [--actor-git-url URL] [--actor-git-ref REF]
papeete-deploy undeploy   PRODUCT.YAML

resolve prints each actor's resolved tag, no Docker/kubectl involved — the smaller claim, useful in CI or for a human to check where a product would land before spending the time to deploy it. deploy resolves the same way, then makes the product real wherever its declared environment.type says: local starts every actor via Docker Compose; k8s applies each actor's own kustomize overlay to a real cluster (see "Deploying to k8s" below). undeploy tears down whatever deploy started.

pip install papeete-deploy

Framed as deployment, scoped narrowly for now

A product's declared query — label (a ciType) + version ("latest", a short SHA, or an npm-range) — has to be folded against something real to mean anything. This package resolves it by listing the tags a registry already has for an actor's name, filtering by label/branch, and taking the newest match — never by consulting git. That mirrors how a real, production deployment pipeline solves the exact same problem (reviewed while designing this), and it's why this package needs neither an actor's folder nor a papeete-version dependency on git: only registry access, which deploying anything needs anyway.

That's also why this repo is named and framed as deployment, not "runtime" — resolving a query against a registry and making a product real somewhere specific (an environment, its own registry, eventually its own target: local Docker today, a k8s cluster or Terraform-provisioned infra later) is a deployment concern. Only the narrowest slice of that is built right now: a local-Docker registry backend and local Docker Compose orchestration. Multiple environments, an Azure Container Registry backend, k8s, Terraform, DB-migration jobs — real territory this package is meant to grow toward, explicitly not implemented yet (see ADR-PD-0001's consequences).

Registries

from papeete_deploy.registry import LocalDockerRegistry, AcrRegistry
  • LocalDockerRegistry — every tag the local Docker daemon already has for a name. Fully implemented and tested; the default (--registry local).
  • AcrRegistry — every tag an Azure Container Registry has, via az acr repository show-tags. Sketched to the same protocol, not wired into CI, not exercised by any test — no ACR access from this environment to verify against. --registry acr --acr-name NAME selects it.

Which registry an actor's declared environment (required on every product.yaml) should map to is not yet automatic--registry/--acr-name are explicit CLI flags for now. See ADR-PD-0001.

Discovery is Docker's — or Kubernetes' — not invented here

papeete-deploy deploy against environment.type: local starts every actor on one shared Docker Compose project, each container named after its name (normalized). Compose's own embedded network DNS resolves that name for every other actor on the same project — no registry lookup, no sidecar, nothing new built for it. Every actor's server is expected to listen on a fixed port, 8080, published to a host-assigned ephemeral port so a caller outside Docker can reach it too.

Against environment.type: k8s, each actor's own Kubernetes Service (part of its deploy/k8s/base/, below) gives the same by-name discovery via cluster DNS instead — again, nothing this package invents.

Deploying to k8s

An actor's folder MAY carry deploy/k8s/base/ + deploy/k8s/overlays/<recipe>/ — a plain, actor-authored kustomize layout (papeete-actor's ADR-PA-0025). The base Deployment's container image must be named exactly the actor's own normalized name, with no tag — the hook this package uses to inject the resolved image and version at deploy time, without ever editing the actor's own files (k8s.py's wrapper kustomization, same never-mutate-the-source discipline deploy.py's _compose_file() uses for Compose).

papeete-deploy deploy PRODUCT.YAML

recipe (declared per actor in product.yaml, papeete-product's own ADR-PP-0003) says which overlay; environment. k8sName (the kubectl context) and environment.name (the namespace, created if missing, never deleted) say where. Every actor's deploy folder and overlay is validated to exist before any of them is applied — a missing overlay fails loudly with nothing partially deployed, never a silent partial rollout.

Only verified against Docker Desktop's Kubernetes, whose node shares the host's local image store — a cluster with its own separate image store would need those images pushed somewhere reachable first, which this package does not do.

Locating each actor's deploy folder

Each actor's deploy folder is located, never passed on the command line — three tiers, most to least specific (ADR-PD-0003):

  1. A per-actor override — only in a papeete-deploy.yaml next to product.yaml:
    actorDeployOverrides:
      - actor: customer
        type: local
        path: ../wherever/customer          # the actor's own folder, directly
      - actor: waiter
        type: git
        url: https://example.com/waiter-deploy.git
        ref: main
        subpath: waiter                      # optional; defaults to the actor's own name
    
  2. A global sourcepapeete-deploy.yaml's actorDeploySource, layered under env vars (PAPEETE_DEPLOY_ACTOR_SOURCE/_ROOT/_GIT_URL/_GIT_REF), layered under --actor-source/--actor-root/--actor-git-url/--actor-git-ref (CLI wins):
    actorDeploySource:
      type: git                              # or: local, with an optional `root`
      url: https://example.com/deploy-repo.git
      ref: main                              # optional
    
    For type: git, each actor's deploy folder is expected at <url>/<actor-name>/deploy — one shared repo, cloned once (git clone --depth 1) and reused for every actor that shares it.
  3. The zero-config default — a sibling folder of product.yaml, named exactly the actor's own declared name, containing deploy/k8s/overlays/<recipe> — exactly this repo's own examples/ layout, which is why the worked example below needs no flags at all.

The worked example

examples/ is a complete, runnable product: a customer actor and a waiter actor (copied from papeete-product's own worked example, so this repo's tests are self-contained, each also carrying a deploy/k8s/ folder), plus two product.yaml variants — same actors, different environment — declaring label: alpha, version: latest for both: productDocker.yaml (environment.type: local) and productK8s.yaml (environment.type: k8s, targeting the docker-desktop context, recipe: develop per actor).

# stand-in for `papeete-actor build`, tagging in the real {semver}-{label}-{shortSha} shape:
docker build -t customer:0.1.0-alpha-e2e0001 examples/customer
docker build -t waiter:0.1.0-alpha-e2e0001 examples/waiter

# local, via Docker Compose:
papeete-deploy resolve examples/productDocker.yaml
papeete-deploy deploy examples/productDocker.yaml
curl http://localhost:PORT/order        # PORT printed by deploy
papeete-deploy undeploy examples/productDocker.yaml

# local k8s (Docker Desktop's Kubernetes), via kustomize — no flags: examples/'s own layout
# (product.yaml next to customer/ and waiter/) already matches the default convention:
papeete-deploy deploy examples/productK8s.yaml
kubectl -n papeete-deploy-example get deploy,svc -l papeete-deploy/product=table-service
papeete-deploy undeploy examples/productK8s.yaml

tests/test_e2e_deploy.py spawns productDocker.yaml for real via Docker Compose and asserts both actors are reachable from outside Docker and that the customer discovers the waiter by name from inside it. tests/test_e2e_k8s.py does the same against a real k8s cluster (kubectl config current-context, its own throwaway product/namespace rather than productK8s.yaml, so repeated test runs never collide) — deploys via deploy.deploy(..., actor_source=...) pointed at this repo's own examples/, waits for both pods Ready, execs into the customer pod to prove it reaches the waiter by Service name, then undeploys and confirms the namespace survives:

uv run --extra dev pytest -m e2e          # needs a Docker daemon; k8s cases also need a context
uv run --extra dev pytest -m "not e2e"    # the fast, offline structural suite

Releasing

Tag-triggered, via PyPI Trusted Publishing (OIDC). No API token is stored anywhere — GitHub mints a short-lived OIDC token per run and PyPI trades it for an upload token. There is nothing to rotate and nothing to leak.

git tag v0.1.1 && git push origin v0.1.1     # .github/workflows/release.yml does the rest

One-time setup — not done yet

papeete-deploy is already claimed on PyPI (uploaded manually, outside this repo's CI), so this is the existing-project flow, not the pending-publisher one papeete-actor's README documents for a project that doesn't exist yet — someone with owner/maintainer rights on the PyPI project has to do this from its own settings page, which nothing here can do on your behalf:

1. A publisher on PyPI's existing-project settings. At https://pypi.org/manage/project/papeete-deploy/settings/publishing/, add a GitHub publisher:

Field Value
Owner papeete-hub
Repository name papeete-deploy
Workflow name release.yml
Environment name pypi

All four must match exactly — PyPI checks the OIDC claims against them and rejects the upload otherwise. release.yml already declares permissions: id-token: write and environment: pypi, which is what makes those claims present.

2. The pypi GitHub environment. No secrets needed in it — it exists so the OIDC claim carries an environment name for PyPI to match. Protection rules are worth considering, since a release is irreversible: PyPI never allows re-uploading a version, even after a delete. Required reviewers, and restricting deployments to tags matching v*, are the two that earn their keep.

Until step 1 is done, release.yml will run but its publish to PyPI step will fail (no trusted publisher recognizes this workflow's OIDC token yet).

What a release does — and doesn't — verify

The workflow builds and publishes; it does not install the built wheel into a clean environment first (the pattern papeete-actor's release workflow uses to prove its contract survived packaging). papeete-deploy has no analogous contracts command, and — more importantly — a clean-env install would currently fail anyway: papeete-product, a real dependency, has no PyPI release of its own yet (ci.yml's own comment works around that with a sibling checkout + [tool.uv.sources] path override — a local-dev-only resolution aid that doesn't apply to pip install papeete-deploy from PyPI). Anyone installing papeete-deploy from PyPI today hits that same gap. Closing it means papeete-product needs its own PyPI release first — tracked as an open item, not solved here.

Licence

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

papeete_deploy-0.1.1.tar.gz (42.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

papeete_deploy-0.1.1-py3-none-any.whl (20.0 kB view details)

Uploaded Python 3

File details

Details for the file papeete_deploy-0.1.1.tar.gz.

File metadata

  • Download URL: papeete_deploy-0.1.1.tar.gz
  • Upload date:
  • Size: 42.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for papeete_deploy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a7bc2c22873e5c06c0b8c1f8838487ce35a7775c18592b8263fb0dbad0ee941c
MD5 a8ecccb9e889e9be1d42f0771d2e323f
BLAKE2b-256 63aa29f2306b928c03dff97c545f37871126daac061857ee500de22f091fedc5

See more details on using hashes here.

File details

Details for the file papeete_deploy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: papeete_deploy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 20.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for papeete_deploy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fff150d5803bf39168cdad7b81f56b6f35f1bc77f98ad8a9df0b3a9344ea9eb9
MD5 80aae19e8463f028d7cb5f842d5b277d
BLAKE2b-256 177888df83567cca2876bd287fcc40a96c362ab84e43ed661a6c0713e1d8580f

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page