pyssg is pre-1.0 and under active development - APIs, config, and themes may change.
pyssg.deploy.base

3 min read

pyssg.deploy.base

Public contracts for deploy targets.

A deploy target is the integration with a single hosting provider (GitHub Pages, Cloudflare Pages, Netlify, ...). Targets only declare facts about themselves and execute the actual upload; everything that is provider-agnostic (loading config, building, hashing, skip detection, persisting last-deploy state, friendly output) lives in :mod:pyssg.deploy.pipeline.

This module is stdlib-only on purpose: it is the boundary between the engine and the periphery, so it must not pull in optional dependencies like httpx.

The :class:DeployTarget Protocol describes the surface every target must expose. The orchestrator constructs a :class:DeployContext for each run and calls target.deploy(ctx); the target returns a :class:DeployResult on success or raises :class:DeployError with a user-actionable message on unrecoverable failure.

class DeployError(PyssgError)

A deploy run failed in a way the user needs to act on.

Targets raise this for missing credentials, network errors, provider API errors, or anything else that aborts the upload. The pipeline catches it, prints the message via the friendly console formatter, and exits with a non-zero status; the message itself MUST be self-contained (what failed, and if possible, what to do about it).

class DeployResult

The outcome of a single target.deploy(ctx) call.

url is the canonical user-facing URL for the freshly deployed site (or the existing one if skipped). deployment_id is whatever the provider uses to identify this revision (a git commit sha for GitHub Pages, a UUID for Cloudflare Pages, etc.); the pipeline stores it so the next run can show "no changes since ". files_skipped counts files the provider already had (content-addressed upload) and did not require re-uploading.

skipped is True only when the whole deploy was a no-op because the output tree hash matched the previous deploy; in that case the file/byte counters are zero and elapsed_seconds is the time spent computing the hash, not pushing.

class DeployContext

Everything a target needs to know to run one deploy.

site_dir and out_dir are absolute resolved paths. target_config is the dict the user wrote under Config.deploy[target_name]; the target is responsible for reading the keys it declared in :meth:DeployTarget.required_config_keys and may also read optional ones.

dry_run means "do everything except mutate the remote": the target should still validate auth, compute what would be uploaded, and return a :class:DeployResult with sensible counters, but MUST NOT push.

force is informational: by the time a target sees the context, the pipeline has already decided not to skip. Targets that themselves implement fine-grained skipping (e.g. per-file digest match) may consult this flag.

class DeployTarget(Protocol)

Per-provider integration surface.

Implementations are typically stateless singletons; the pipeline holds the state (cache, last-deploy record) and passes it through ctx.

name is the registry key and the CLI subcommand name; it MUST be a short, hyphenated identifier (github-pages, cloudflare, netlify).

DeployTarget.required_env(self) -> list[str]

Environment variables that must be set before deploy is called.

The pipeline checks these up front and fails with a clear message listing the missing ones; the target itself can assume they are present when deploy runs. Return an empty list if the target reads no env (e.g. GitHub Pages, which uses git's own credential machinery).

DeployTarget.required_config_keys(self) -> list[str]

Keys that must appear in ctx.target_config.

Optional keys are not listed here; the target reads them with .get(...) and supplies its own defaults. The pipeline reports missing required keys with a uniform message before invoking the target.

async DeployTarget.deploy(self, ctx: DeployContext) -> DeployResult

Push the built site to the provider.

Implementations should be idempotent for the same out_dir content (content-addressed upload), MUST honor ctx.dry_run, and MUST raise :class:DeployError (not a bare Exception) on any user-actionable failure.