step

package
v0.1.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

build_gate_image_resolver.go implements the Build Gate image catalog resolver.

The resolver selects runtime images for Build Gate containers when Stack Gate is enabled. It loads rules from multiple sources with precedence ordering:

  1. Default gates catalog (gates/gates.yaml) - lowest precedence
  2. Mig-level image overrides - highest precedence

Resolution uses "most specific match wins" semantics:

  • Tool-specific rules (specificity 3) beat tool-agnostic rules (specificity 2)
  • Same-specificity ties with different images are configuration errors

gate_docker.go implements the Docker-based GateExecutor.

This executor is the canonical source of gate validation results: it runs a language-specific image with the workspace mounted at /workspace, captures logs and resource usage, and returns BuildGateStageMetadata. Concerns are split across sibling files: mounts (gate_docker_mounts.go), log streaming (container_log_streamer.go + gate_docker_logs.go), env-driven resource limits (gate_docker_env.go), and result normalization (gate_docker_metadata.go). Stack detection + image resolution live in gate_plan_resolver.go.

Index

Constants

View Source
const (

	// GateWorkspaceOutDir is a workspace-local host directory mounted
	// into gate containers as /out for deterministic artifact collection.
	GateWorkspaceOutDir = ".ploy-gate-out"
)

Variables

View Source
var ErrGateFailed = errors.New("build gate failed")

ErrGateFailed is returned when the pre-mig Build Gate fails.

Functions

func SeedInDirFromStaging

func SeedInDirFromStaging(manifest contracts.StepManifest, stagingDir, inDir string) error

SeedInDirFromStaging copies materialized Hydra in entry content from the staging directory into inDir so that the single /in mount can expose both pre-seeded content and runtime-provided cross-phase files.

func SeedOutDirFromStaging

func SeedOutDirFromStaging(manifest contracts.StepManifest, stagingDir, outDir string) error

SeedOutDirFromStaging copies materialized Hydra out entry content from the staging directory into outDir so that the single /out mount covers both pre-seeded content and container writes.

func SeedTmpDirFromStaging

func SeedTmpDirFromStaging(manifest contracts.StepManifest, stagingDir, tmpDir string) error

SeedTmpDirFromStaging copies materialized Hydra tmp entry content from the staging directory into tmpDir so that the single /tmp mount exposes all tmp files while keeping them outside durable repo artifacts.

func WithExecutionLogWriter

func WithExecutionLogWriter(ctx context.Context, w io.Writer) context.Context

WithExecutionLogWriter stores an optional live log sink in context so lower-level executors (for example gate runtime) can stream container output through the standard node log uploader path.

func WithGateContainerLabels

func WithGateContainerLabels(ctx context.Context, labels map[string]string) context.Context

WithGateContainerLabels attaches container labels to gate execution context. Labels are copied and merged with existing gate labels in the context.

func WithGateRuntimeImageObserver

func WithGateRuntimeImageObserver(ctx context.Context, obs GateRuntimeImageObserver) context.Context

WithGateRuntimeImageObserver attaches an observer to the context used for gate execution. The observer is called with the resolved runtime image before container execution starts.

func WithGateShareDir

func WithGateShareDir(ctx context.Context, shareDir string) context.Context

WithGateShareDir attaches an optional host share directory to gate execution context. When set, gateExecutor mounts it at /share.

Types

type ContainerHandle

type ContainerHandle string

ContainerHandle identifies a prepared container by its ID.

type ContainerMount

type ContainerMount struct {
	Source   string
	Target   string
	ReadOnly bool
}

ContainerMount describes a host path mount.

type ContainerResourceUsage

type ContainerResourceUsage struct {
	CPUConsumedNs     int64
	DiskConsumedBytes int64
	MemConsumedBytes  int64
}

ContainerResourceUsage captures per-job container resource consumption.

type ContainerResult

type ContainerResult struct {
	ExitCode    int
	StartedAt   time.Time
	CompletedAt time.Time
	ContainerID string
	InspectJSON []byte
}

ContainerResult captures container exit metadata.

type ContainerRuntime

type ContainerRuntime interface {
	Create(ctx context.Context, spec ContainerSpec) (ContainerHandle, error)
	Start(ctx context.Context, handle ContainerHandle) error
	Wait(ctx context.Context, handle ContainerHandle) (ContainerResult, error)
	Logs(ctx context.Context, handle ContainerHandle) ([]byte, error)
	Remove(ctx context.Context, handle ContainerHandle) error
}

ContainerRuntime executes containers.

func NewContainerRuntime

func NewContainerRuntime(opts ContainerRuntimeOptions) (ContainerRuntime, error)

NewContainerRuntime constructs a Docker-backed container runtime. It uses client.FromEnv to read DOCKER_HOST and related environment variables, and WithAPIVersionNegotiation to auto-negotiate API version with the daemon.

type ContainerRuntimeOptions

type ContainerRuntimeOptions struct {
	// PullImage controls whether the runtime refreshes the image before container
	// creation.
	PullImage bool
	// Network is optional Docker network name (empty => default bridge).
	Network string
	// RegistryAuthConfigFile is a Docker auth config JSON file path
	// (DOCKER_AUTH_CONFIG format). When set, each image pull reads current
	// credentials from this file.
	RegistryAuthConfigFile string
	// RegistryAuthRefreshSocket is an optional Unix socket owned by the host.
	// When set, an auth failure on image pull asks the host to refresh registry
	// auth before retrying the same pull once.
	RegistryAuthRefreshSocket string
	// RegistryAuthConfigJSON is a Docker auth config JSON payload (DOCKER_AUTH_CONFIG
	// format). When set, image pulls use matching registry credentials.
	RegistryAuthConfigJSON string
}

ContainerRuntimeOptions holds configuration for Docker runtime.

type ContainerSpec

type ContainerSpec struct {
	Image      string
	Command    []string
	WorkingDir string
	Env        map[string]string
	Mounts     []ContainerMount
	Labels     map[string]string
	// Optional resource limits (0 => unlimited)
	LimitNanoCPUs    int64
	LimitMemoryBytes int64
	// Optional disk limit for writable layer (bytes; 0 => unlimited).
	// When set, Docker runtime may pass a storage option (driver dependent).
	LimitDiskBytes int64
	// Optional raw storage size option string passed to Docker (e.g., "10G").
	// Set only when the operator provided PLOY_BUILDGATE_LIMIT_DISK_SPACE.
	StorageSizeOpt string
}

ContainerSpec describes a container execution request.

type DiffGenerator

type DiffGenerator interface {
	Generate(ctx context.Context, workspace string) ([]byte, error)
}

DiffGenerator generates diffs between states.

func NewFilesystemDiffGenerator

func NewFilesystemDiffGenerator() DiffGenerator

NewFilesystemDiffGenerator creates a DiffGenerator backed by a temporary git index snapshot.

type GateExecutor

type GateExecutor interface {
	Execute(ctx context.Context, spec *contracts.StepGateSpec, workspace string) (*contracts.BuildGateStageMetadata, error)
}

GateExecutor validates build artifacts. The primary implementation is gateExecutor (gate_docker.go) which runs validation containers locally via the container runtime.

func NewGateExecutor

func NewGateExecutor(rt ContainerRuntime) GateExecutor

NewGateExecutor constructs a GateExecutor that uses the provided ContainerRuntime to run build commands.

type GateRuntimeImageObserver

type GateRuntimeImageObserver func(ctx context.Context, image string)

GateRuntimeImageObserver is a hook that is called once the gate runtime image is resolved (after stack detection / stack gate checks), and before the gate container is started.

type PatchStats

type PatchStats struct {
	FilesChanged int
	LinesAdded   int
	LinesRemoved int
}

PatchStats holds line-level statistics derived from a unified diff.

func CountPatchStats

func CountPatchStats(patchBytes []byte) PatchStats

CountPatchStats parses a unified diff and returns file and line delta counts. It counts `+` lines (excluding `+++ ` file headers) as additions and `-` lines (excluding `--- ` file headers) as removals. Each `diff --*` header marks one changed file.

type Request

type Request struct {
	// RunID threads the workflow run identifier for correlation/labels.
	// Container labels and telemetry use this value via LabelRunID.
	RunID types.RunID
	// JobID threads the workflow job identifier for correlation/labels.
	// Container labels and telemetry use this value via LabelJobID.
	JobID     types.JobID
	Manifest  contracts.StepManifest
	Workspace string
	OutDir    string
	// InDir is an optional directory mounted at /in for cross-phase inputs.
	InDir string
	// ShareDir is an optional directory mounted at /share for run-scoped
	// shared inputs/outputs across job stages.
	ShareDir string
	// TmpDir is an optional per-job directory seeded from Manifest.Tmp and
	// mounted at /tmp for writable, non-artifact temporary files.
	TmpDir string
	// StagingDir is an optional path to a directory containing pre-materialized
	// Hydra resources. Each In/Out/Home/Tmp entry is mounted from StagingDir/<shortHash>.
	StagingDir string
}

Request describes a step execution request.

type Result

type Result struct {
	ExitCode int
	// Docker container identity and inspect output when a container was run.
	ContainerID          string
	ContainerInspectJSON []byte
	// Per-stage timings captured during execution.
	Timings            StageTiming
	Gate               *contracts.BuildGateStageMetadata
	ContainerResources *ContainerResourceUsage
}

Result contains the outcome of a step execution.

func RunGateOnly

func RunGateOnly(ctx context.Context, r *Runner, req Request) (Result, error)

RunGateOnly executes only the gate validation phase without container execution. This helper allows the node agent orchestration layer to reuse gate logic for post-mig gates without invoking a mig container.

Execution stages:

  1. Hydration — Prepare the workspace via WorkspaceHydrator when configured.
  2. Build Gate — Run static validation using GateExecutor when enabled.

Unlike Runner.Run, this function:

  • Does NOT create or start any containers.
  • Does NOT generate diffs.
  • Returns immediately after gate validation (pass or fail).

The returned Result contains:

  • Gate metadata (if gate was executed).
  • Timings for hydration and gate phases only.
  • ExitCode is always 0 (no container was executed).

On gate failure, returns ErrGateFailed so callers can detect failures.

type Runner

type Runner struct {
	Workspace  WorkspaceHydrator
	Containers ContainerRuntime
	Gate       GateExecutor
	LogWriter  io.Writer // Optional: streams logs to server as gzipped chunks.
}

Runner executes workflow steps.

Execution Stages (Pre-mig Gate per Call)

Runner.Run processes each step call through the following stages in order:

  1. Hydration — Prepare the workspace by fetching repository sources via WorkspaceHydrator. Errors here abort the run immediately.

  2. Pre-mig Build Gate — When Gate is enabled (Manifest.Gate.Enabled), run static validation on the workspace before executing the mig container. If the gate fails, Runner.Run returns ErrGateFailed without executing container stages.

  3. Container Execution — Create, start, and wait on the container via ContainerRuntime. Logs are forwarded to LogWriter if present. Container cleanup is owned by node-runtime pre-claim disk-pressure flow.

Gate Ownership Contract

Runner supports an optional pre-mig gate when Manifest.Gate.Enabled=true. This capability exists for direct invocations (e.g., standalone testing) where Runner manages its own gate lifecycle.

However, nodeagent step execution MUST pass manifests with Gate.Enabled=false. The nodeagent orchestration layer owns all gate lifecycle management via the gate job chain, which handles:

  • A single pre-run gate before the step loop begins.
  • Per-step post-mig gates after each container execution.

Passing Gate.Enabled=true from nodeagent would cause duplicate pre-mig gates (one from the nodeagent, one from Runner.Run) and break the single-gate- per-run invariant. The nodeagent is the authoritative gate orchestrator.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context, req Request) (Result, error)

Run executes a step and returns the result.

type StageTiming

type StageTiming struct {
	HydrationDuration types.Duration
	ExecutionDuration types.Duration
	GateDuration      types.Duration
	DiffDuration      types.Duration
	PublishDuration   types.Duration
	TotalDuration     types.Duration
}

StageTiming captures duration of each execution stage.

type WorkspaceHydrator

type WorkspaceHydrator interface {
	Hydrate(ctx context.Context, manifest contracts.StepManifest, workspace string) error
}

WorkspaceHydrator prepares a workspace for execution.

func NewFilesystemWorkspaceHydrator

func NewFilesystemWorkspaceHydrator(fetcher hydration.GitFetcher) (WorkspaceHydrator, error)

NewFilesystemWorkspaceHydrator creates a new workspace hydrator backed by the given fetcher.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL