4.1 KiB
Publish Internals
Audience: developers and LLM coding agents changing internal/publish.
Purpose
internal/publish plans and executes publication for one validated source bundle and one destination bundle path. It owns destination comparison mapping, output selection, URL planning, managed cleanup selection, replacement safety, and destination state projection.
Inputs And Outputs
Inputs are a source bundle, source backend, destination backend, pipeline id, destination id, destination bundle path, path mapping mode, publish policy, transform policy, optional link policy, transformer resolver, transfer policy, distributor version, and force flag.
Output from planning is a Plan with action, reason, destination identity, selected outputs, optional existing state, optional primary URL, and force metadata. Execution writes selected source outputs, generated outputs, and .distributor.json for executable publish or replacement actions.
Boundaries
The package does not parse CLI flags, load config files, open concrete adapters, discover source bundles, select fixed-path bundle candidates, register transforms, or render command output. The app layer supplies validated request data and concrete dependencies.
External destination state semantics are documented in docs/integrations/destination-state.md.
Config Fields Used
The package consumes already-defaulted config values for destination publish, transform, links, transfer, and path mapping mode. It uses config.ValidatePublishTransformPolicy for publish/transform consistency.
Adapters Used
The package depends on internal/storage.Backend for source and destination IO, and on a narrow transformer resolver interface for generated outputs. It does not import concrete storage adapters or concrete transform implementations.
State And Manifest Behavior
Planning inspects destination state through internal/state, compares it with the source manifest, and maps comparison outcomes plus transfer policy into actions: publish_new, replace_older, force_replace, skip_same, skip_destination_newer, fail_conflict, or fail_unmanaged.
Execution writes destination state after selected outputs are written. Destination state includes copied source output metadata, generated output metadata, embedded source manifest, link metadata when configured, pipeline id, destination id, and publication timestamp.
Skip And Resume Behavior
skip_same and skip_destination_newer execute as no-ops. Normal replacement removes only managed output paths from existing state plus .distributor.json; this allows retries without broad deletion. Failed writes trigger cleanup of outputs written during that failed attempt where practical.
Forced replacement is explicit per request and deletes the bounded destination bundle path before writing new outputs and state.
Failure Behavior
Planning fails for incomplete requests, invalid publish/transform policy, output path collisions, invalid destination state, unmanaged destination content without force, conflict outcomes not allowed by transfer policy, unresolved transforms, invalid Markdown output selection, and invalid link URL planning.
Execution fails on delete, read, transform output, write, state validation, state serialization, or context errors. Execution refuses actions that are not executable publish or replacement actions.
Tests To Inspect
internal/publish/*_test.gointernal/app/run_test.gointernal/state/*_test.gointernal/transform/markdown/*_test.go
Architectural Invariants
- Planning is deterministic for the same request and destination state.
- Destination bundle paths are caller-supplied and backend-root-relative.
- URL generation uses URL path semantics and never infers public URLs from backend config.
- Normal replacement deletes only managed paths recorded in existing state plus
.distributor.json. - Forced replacement deletes only within the supplied destination bundle path.
- Destination state is written after selected outputs are written.
- Transform resolution stays behind a caller-supplied interface.
- Unmanaged content is claimed only by explicit force.