imagefactory

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HashSelection

func HashSelection(sel Selection, baseName string) (string, error)

HashSelection computes the content-addressed schematic hash over the sorted-deduped selection IDs + base name. Pure, deterministic, no I/O. Returns "s-" + first 16 hex chars of SHA-256 (design/0046 #1, #2).

Version is deliberately NOT in the preimage — version is a separate axis (design/0046 #2): the same schematic hash identifies a family of images, one per base version that has been built.

func RenderDockerfile

func RenderDockerfile(rv ResolvedValues, base Base) (string, error)

RenderDockerfile renders the deterministic Dockerfile for a workspace image from frozen resolved values + a base. Pure function of (ResolvedValues, Base): identical inputs always render an identical Dockerfile byte-for-byte, so a content-addressed tag maps to exactly one reproducible build (design/0046 build-execution section).

The Dockerfile only ever layers onto the operator-approved base (base.Ref()). It cannot reference an arbitrary image, and every layer is derived from operator-authored catalog values (no user free text) — this is the structural guard against the injection surface.

func SeedCatalog added in v0.8.0

func SeedCatalog(ctx context.Context, store SeedCatalogStore) error

SeedCatalog idempotently upserts the seed data into the store. For each extension: if it already exists in the DB, it is NOT overwritten (runtime admin changes — retire, review flag — take precedence). Only NEW extensions are inserted. A transient DB error on GetExtension propagates (does NOT fall through to insert). Bases are always upserted (they're version-pinned and immutable per version).

func ValidateResolved

func ValidateResolved(rv ResolvedValues) error

ValidateResolved checks the resolved shape: if non-empty, every entry has a known type, a non-empty value, and (for file) an absolute non-traversal path + valid octal mode. Empty is valid — an extension-less config is a named alias for the base image (design permits it; the picker shows it). Used as a defensive check on data crossing the DB boundary.

func ValidateSelection

func ValidateSelection(sel Selection) error

ValidateSelection checks that the selection is non-empty and every ID matches the extension ID charset. It does NOT check existence in the catalog — that is the store's concern (ResolveSelection does it).

Types

type Base

type Base struct {
	Name      string `json:"name" yaml:"name"`
	Version   string `json:"version" yaml:"version"`
	Image     string `json:"image" yaml:"image"`
	Tag       string `json:"tag,omitempty" yaml:"tag,omitempty"`
	Digest    string `json:"digest,omitempty" yaml:"digest,omitempty"`
	IsDefault bool   `json:"isDefault" yaml:"isDefault"`
}

Base is a (name, version) row of an operator-approved base image. Composite-keyed: old versions persist (design/0046 #8).

func (Base) Ref

func (b Base) Ref() string

Ref returns the pullable reference. Digest wins over tag; if neither is set, just the bare image (caller's bug, but don't panic).

type Build

type Build struct {
	ID             string         `json:"id"`
	ConfigID       string         `json:"configId"`
	Hash           string         `json:"hash"`
	BaseName       string         `json:"baseName"`
	BaseVersion    string         `json:"baseVersion"`
	ResolvedValues ResolvedValues `json:"resolvedValues"`
	Architectures  []string       `json:"architectures"`
	ImageRef       string         `json:"imageRef,omitempty"`
	Digest         string         `json:"digest,omitempty"`
	Status         BuildStatus    `json:"status"`
	GHRunID        *int64         `json:"ghRunId,omitempty"`
	FailureReason  string         `json:"-"` // admin-only
	Explanation    string         `json:"explanation,omitempty"`
	TriggeredBy    *string        `json:"triggeredBy,omitempty"`
	StartedAt      time.Time      `json:"startedAt"`
	FinishedAt     *time.Time     `json:"finishedAt,omitempty"`
	CallbackToken  string         `json:"-"` // per-build secret; ConstantTimeCompare on callback
}

Build is one row of image_factory_builds. One row per API dispatch — transient retry happens inside the GH Actions workflow (design/0046 #12), so the API sees exactly one dispatch + one final result.

type BuildStatus

type BuildStatus string

BuildStatus enumerates the build lifecycle.

const (
	BuildDispatched BuildStatus = "dispatched"
	BuildSucceeded  BuildStatus = "succeeded"
	BuildFailed     BuildStatus = "failed"
)

type Config

type Config struct {
	ID             string         `json:"id"`
	Hash           string         `json:"hash"`
	Name           string         `json:"name"`
	Selection      Selection      `json:"selection"`
	ResolvedValues ResolvedValues `json:"resolvedValues"`
	BaseName       string         `json:"baseName"`
	BaseVersion    string         `json:"baseVersion"`
	Scope          ConfigScope    `json:"scope"`
	OwnerID        *string        `json:"ownerId,omitempty"`
	OrgID          *string        `json:"orgId,omitempty"`
	Status         ConfigStatus   `json:"status"`
}

Config is a saved user/org/platform config.

type ConfigScope

type ConfigScope string

ConfigScope enumerates the three friendly-name scopes (design/0046 #25).

const (
	ScopeMember   ConfigScope = "member"
	ScopeOrg      ConfigScope = "org"
	ScopePlatform ConfigScope = "platform"
)

type ConfigStatus

type ConfigStatus string

ConfigStatus is the config lifecycle pill (design/0046 #20).

const (
	StatusBuilding ConfigStatus = "building"
	StatusReady    ConfigStatus = "ready"
	StatusRejected ConfigStatus = "rejected"
)

type Extension

type Extension struct {
	ID              string        `json:"id"`
	Type            ExtensionType `json:"type"`
	Value           string        `json:"value"`
	FileSpec        *FileSpec     `json:"fileSpec,omitempty"`
	SupportedBases  []string      `json:"supportedBases"`
	Retired         bool          `json:"retired"`
	ReviewRequested bool          `json:"reviewRequested"`
	Description     string        `json:"description,omitempty"`
}

Extension is a catalog row. Immutable-once-published (design/0046 #7): the build-relevant fields (Type, Value, FileSpec, SupportedBases) do not change after creation. Only Retired, ReviewRequested, Description mutate.

type ExtensionType

type ExtensionType string

ExtensionType enumerates the catalog extension kinds. "run" and "env" are deliberately absent — they were the injection vectors (design/0046 #5).

const (
	ExtensionTypeApt  ExtensionType = "apt"
	ExtensionTypeMise ExtensionType = "mise"
	ExtensionTypeFile ExtensionType = "file"
)

type FileSpec

type FileSpec struct {
	Path string `json:"path"`
	Mode string `json:"mode,omitempty"` // octal string "0755"; empty → "0644"
}

FileSpec is the target for a type=file extension. Path must be absolute (no traversal); Mode defaults to "0644" when empty.

type KnownFailure

type KnownFailure struct {
	SelectionHash string    `json:"selectionHash"`
	Selection     Selection `json:"selection"`
	BaseName      string    `json:"baseName"`
	Explanation   string    `json:"explanation,omitempty"`
	FailureReason string    `json:"-"` // admin-only; never serialized to non-admins
	DetectedAt    time.Time `json:"detectedAt"`
	Retriable     bool      `json:"retriable"`
}

KnownFailure is a row of image_factory_known_failures — the blocklist.

type PlatformConfig

type PlatformConfig struct {
	Architectures []string `json:"architectures"`
}

PlatformConfig is the single-row platform-level factory config.

type ResolvedValue

type ResolvedValue struct {
	Type     ExtensionType `json:"type"`
	Value    string        `json:"value"`
	FileSpec *FileSpec     `json:"fileSpec,omitempty"`
}

ResolvedValue is one entry in the resolved_values JSONB. It is the cached projection of an Extension's build fields, frozen at config-save time. Shape pinned per design/0047.

type ResolvedValues

type ResolvedValues map[string]ResolvedValue

ResolvedValues maps extension ID → frozen resolved value. This is the exact JSONB shape stored on image_factory_configs.resolved_values and image_factory_builds.resolved_values.

func ResolveSelection

func ResolveSelection(sel Selection, exts map[string]Extension, baseName string) (ResolvedValues, error)

ResolveSelection joins a Selection against a set of catalog Extensions and returns the frozen ResolvedValues projection stored on the config (design/0046 #10). Errors if any ID is missing, retired, or unsupported on baseName. Pure: no I/O.

func (ResolvedValues) Selection

func (rv ResolvedValues) Selection() Selection

Selection recovers the extension IDs from the resolved values map, sorted. Used by the callback handler to reconstruct the selection for the known_failures row (the build stores resolved_values, not the selection).

type SeedCatalogData added in v0.8.0

type SeedCatalogData struct {
	Architectures []string             `json:"architectures" yaml:"architectures"`
	Bases         []Base               `json:"bases" yaml:"bases"`
	Extensions    []SeedExtensionEntry `json:"extensions" yaml:"extensions"`
}

SeedCatalogData is the parsed structure of catalog.seed.yaml.

func LoadSeed added in v0.8.0

func LoadSeed() (SeedCatalogData, error)

LoadSeed parses catalog.seed.yaml (embedded via go:embed). If an env var CATALOG_SEED_PATH is set, loads from that file instead (operator override). Returns the parsed seed data.

type SeedCatalogStore added in v0.8.0

type SeedCatalogStore interface {
	SetPlatformConfig(ctx context.Context, pc PlatformConfig) error
	UpsertBase(ctx context.Context, b Base) error
	GetExtension(ctx context.Context, id string) (Extension, error)
	PublishExtension(ctx context.Context, e Extension) error
}

SeedCatalogStore is the subset of the DB store needed to seed the catalog.

type SeedExtensionEntry added in v0.8.0

type SeedExtensionEntry struct {
	ID             string        `json:"id" yaml:"id"`
	Type           ExtensionType `json:"type" yaml:"type"`
	Value          string        `json:"value" yaml:"value"`
	FileSpec       *FileSpec     `json:"fileSpec,omitempty" yaml:"fileSpec,omitempty"`
	SupportedBases []string      `json:"supportedBases" yaml:"supportedBases"`
	Description    string        `json:"description,omitempty" yaml:"description,omitempty"`
}

SeedExtensionEntry is one extension in the seed file. The YAML uses camelCase for fileSpec (matching the JSON tag on FileSpec).

func (SeedExtensionEntry) ToExtension added in v0.8.0

func (s SeedExtensionEntry) ToExtension() Extension

ToExtension converts a seed entry to a domain Extension.

type Selection

type Selection []string

Selection is the user's pick: an unordered set of extension IDs. Hashing sorts, so input order does not affect the result.

Jump to

Keyboard shortcuts

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