projects

package
v2.8.1 Latest Latest
Warning

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

Go to latest
Published: Aug 19, 2026 License: BSD-3-Clause Imports: 65 Imported by: 0

Documentation

Overview

Package projects provides utilities for managing Docker Compose projects and their metadata.

Index

Constants

View Source
const (
	// ArcaneIconLabel is the full reverse-DNS label key for fallback service-level icons.
	ArcaneIconLabel = "com.getarcaneapp.arcane.icon"
	// ArcaneIconLightLabel is the full reverse-DNS label key for light service-level icons.
	ArcaneIconLightLabel = "com.getarcaneapp.arcane.icon-light"
	// ArcaneIconDarkLabel is the full reverse-DNS label key for dark service-level icons.
	ArcaneIconDarkLabel = "com.getarcaneapp.arcane.icon-dark"
)
View Source
const (
	GlobalEnvFileName                     = ".env.global"
	EffectiveEnvFileName                  = ".env"
	GitSourceEnvFileName                  = ".env.git"
	OverrideEnvFileName                   = "project.env"
	ProjectEnvModeDirect   ProjectEnvMode = "direct"
	ProjectEnvModeOverride ProjectEnvMode = "override"
)
View Source
const (
	// ProjectTagMaxLength is the maximum number of Unicode characters in a tag name.
	ProjectTagMaxLength = 64
	// ProjectTagsPerSourceLimit is the maximum number of UI or Compose tags on one project.
	ProjectTagsPerSourceLimit = 50
)
View Source
const (
	ErrProjectWorkspaceRevisionConflict        = errors.Sentinel("project workspace changed; refresh it and try again")
	ErrProjectWorkspaceOutsideProjectDirectory = errors.Sentinel("path is outside project directory")
	ErrProjectWorkspaceProtectedPath           = errors.Sentinel("protected project configuration cannot be modified through the workspace")
	ErrProjectWorkspaceSymlinkPath             = errors.Sentinel("symlink workspace paths are not supported")
)
View Source
const ArcaneTrashPrefix = ".arcane-trash-"

ArcaneTrashPrefix is the prefix Arcane uses when quarantining (soft-deleting) a project's files, e.g. ".arcane-trash-<name>-<unix>". Trash directories are Arcane-managed and must never be discovered or imported as user projects.

View Source
const DefaultComposeFileName = "compose.yaml"

DefaultComposeFileName is the compose filename Arcane writes when a project has no existing compose file.

View Source
const DefaultComposeOverrideFileName = "compose.override.yaml"

DefaultComposeOverrideFileName is the override filename Arcane writes when a project gains an override but has none on disk. It is deliberately composeOverrideFileCandidates[1] ("compose.override.yaml"), NOT [0] ("compose.override.yml"), so the default override extension matches Arcane's ".yaml" base default (DefaultComposeFileName = "compose.yaml"). Do not "fix" this to [0]: `docker compose` has no preference between the two, and keeping ".yaml" avoids a mismatched base/override extension pair.

View Source
const ErrProjectDirExists = errors.Sentinel("project directory already exists")

ErrProjectDirExists is returned by CreateExactDir when the target directory already exists. Callers that must not auto-rename (e.g. GitOps creates, which must never mint "-N" duplicate projects on a broken binding) use this to fail loudly instead of suffixing.

View Source
const ProjectWorkspaceUseScanDepth = -1

Variables

View Source
var ProjectFileCandidates = append(
	append(slices.Clone(composeFileCandidates), composeOverrideFileCandidates...),
	".env",
)

ProjectFileCandidates enumerates known project files: base compose names, compose override names, and .env. It is used as a membership set for recognizing and skipping known files during discovery; it is NOT the base-detection order (see composeFileCandidates for that).

Functions

func ApplyLenientLoaderOptions added in v2.3.2

func ApplyLenientLoaderOptions(ctx context.Context, opts *loader.Options, composeFile string)

ApplyLenientLoaderOptions configures a compose loader.Options for tolerant loading: structural validation and consistency checks are skipped, and unresolvable ${VAR} references are substituted with a placeholder instead of an empty string (which would otherwise produce invalid volume/bind specs like ":/path"). Callers use this when a .env file may not yet exist or may not define every variable the compose file references.

The placeholder is applied at the substitution level, not the lookup level: the lookup must keep reporting undefined variables as missing so compose-go resolves default operators (${VAR:-default}, ${VAR-default}) normally. Reporting the placeholder as the variable's value would suppress those defaults and feed the placeholder into fields like ports host entries.

func ApplyOverrideFileChange added in v2.8.0

func ApplyOverrideFileChange(ctx context.Context, projectsRoot, dir string, overrideContent *string) error

ApplyOverrideFileChange writes, clears, or leaves the override file alone depending on whether overrideContent is nil, blank, or set.

func ApplyProjectWorkspaceChanges added in v2.8.0

func ApplyProjectWorkspaceChanges(projectPath string, changes []project.WorkspaceFileChange, uploads map[int][]byte, opts ProjectWorkspaceApplyOptions) error

ApplyProjectWorkspaceChanges applies changes in order without internal rollback: on any error, earlier changes remain on disk. Callers own atomicity — ProjectService.UpdateProjectWorkspace wraps every save in BackupProjectUpdateScope / RestoreProjectUpdateBackup, and project creation removes the whole directory on failure.

func BuildAdditiveOverrideEnvContent

func BuildAdditiveOverrideEnvContent(gitContent, localContent string) (string, error)

BuildAdditiveOverrideEnvContent derives override content from a pre-git local .env file. Like other generated env helpers, the result is normalized and does not preserve comments or original key ordering.

func BuildArgsFromCompose added in v2.1.0

func BuildArgsFromCompose(args map[string]*string) map[string]string

BuildArgsFromCompose flattens compose build args into a string map, dropping nil values.

func BuildEffectiveEnvContent

func BuildEffectiveEnvContent(gitContent, overrideContent string) (string, error)

BuildEffectiveEnvContent merges git and override env sources into the effective .env content written to disk. Keys present in both layers are rewritten in place on the Git line, preserving ordering and inline comments; override-only keys are appended after the Git content. When the in-place rewrite cannot be verified to parse identically to plain concatenation (e.g. multiline values), the override is appended verbatim instead so duplicate keys resolve to the override value.

func BuildImagePullPlan added in v2.3.2

func BuildImagePullPlan(project *composetypes.Project) map[string]ImagePullStep

BuildImagePullPlan builds a deduplicated image pull plan covering non-build service images, pre_start hook images, and type:image volume sources.

func BuildImageRefsFromComposeProject added in v2.5.0

func BuildImageRefsFromComposeProject(project *composetypes.Project) []string

BuildImageRefsFromComposeProject returns the image references produced by services with build directives, including Compose's default image name when a service does not declare image explicitly.

func BuildLocalImageTag added in v2.1.0

func BuildLocalImageTag(projectID, projectName, serviceName string) string

BuildLocalImageTag derives a deterministic local image tag for a built service.

func BuildOverrideEnvContent

func BuildOverrideEnvContent(gitContent, effectiveContent string) (string, error)

BuildOverrideEnvContent derives the editable override file from git-backed and effective env content. Content that already contains only real overrides is returned verbatim; derived or cleaned output uses Arcane's canonical format.

func BuildPlatformsFromCompose added in v2.1.0

func BuildPlatformsFromCompose(svc composetypes.ServiceConfig) []string

BuildPlatformsFromCompose returns the build platforms for a service, falling back to the service platform when no build platforms are declared.

func CleanupRemovedFiles

func CleanupRemovedFiles(ctx context.Context, projectsRoot, projectPath string, oldFiles, newFiles []string) error

CleanupRemovedFiles deletes files that were in the old sync but are not in the new sync. It only removes files that were previously synced (tracked in oldFiles). Empty directories are removed after file deletion. This is a best-effort operation - errors are logged but don't cause failure.

func ComposeContentProjectName added in v2.0.3

func ComposeContentProjectName(composeContent string) string

ComposeContentProjectName returns the normalized top-level `name:` from compose YAML content, or "" when the key is absent or unusable. Interpolated names (containing `${`) are treated as absent so the backend and the frontend name lock behave identically; parse errors are ignored here because the full compose validation reports them with proper context.

func ComposeDown

func ComposeDown(ctx context.Context, proj *types.Project, removeVolumes bool) error

func ComposeFileCandidates

func ComposeFileCandidates() []string

ComposeFileCandidates returns the supported base compose filenames in Arcane's detection order. A copy is returned so callers can't mutate package state.

func ComposeLogs

func ComposeLogs(ctx context.Context, projectName string, out io.Writer, follow bool, tail, since string, timestamps bool) error

func ComposeOverrideFileCandidates added in v2.4.0

func ComposeOverrideFileCandidates() []string

ComposeOverrideFileCandidates returns the supported compose override filenames in detection order. A copy is returned so callers can't mutate package state.

func ComposePs

func ComposePs(ctx context.Context, dockerHost string, proj *types.Project, services []string, all bool) ([]api.ContainerSummary, error)

ComposePs lists a project's compose containers. dockerHost is the config-resolved docker host used to reuse the shared read-only compose client; empty falls back to a one-shot environment-resolved client.

func ComposeRestart

func ComposeRestart(ctx context.Context, proj *types.Project, services []string) error

func ComposeStop

func ComposeStop(ctx context.Context, proj *types.Project, services []string) error

func ComposeUp

func ComposeUp(ctx context.Context, proj *types.Project, services []string, removeOrphans bool, forceRecreate bool, recreateVolumes bool, authConfigs map[string]registry.AuthConfig, waitTimeout time.Duration) error

func CreateExactDir added in v2.3.0

func CreateExactDir(ctx context.Context, projectsRoot, basePath, name string, perm os.FileMode) (path, folderName string, err error)

CreateExactDir creates basePath (the sanitized project directory) under projectsRoot WITHOUT any "-N" collision suffixing. It returns ErrProjectDirExists when the directory already exists, leaving the caller to decide how to proceed.

func CreateUniqueDir

func CreateUniqueDir(ctx context.Context, projectsRoot, basePath, name string, perm os.FileMode) (path, folderName string, err error)

CreateUniqueDir creates a unique directory within the allowed projectsRoot, suffixing "-N" until an unused name is found.

func DefaultSwarmStackEnvTemplate

func DefaultSwarmStackEnvTemplate() string

func DefaultSwarmStackTemplate

func DefaultSwarmStackTemplate() string

func DetectComposeFile

func DetectComposeFile(dir string) (string, error)

func DetectComposeOverrideFile added in v2.4.0

func DetectComposeOverrideFile(dir string) string

DetectComposeOverrideFile returns the path to the highest-preference compose override file present in dir, following compose-go's preference order, or "" when none exists. When multiple override files are present it returns the highest-preference match and logs a warning, mirroring compose-go behavior.

func DirectorySyncContentsChanged

func DirectorySyncContentsChanged(ctx context.Context, projectPath string, syncFiles []SyncFile, oldSyncedFiles []string, composeFileName string) (bool, error)

func EnsureDefaultTemplates

func EnsureDefaultTemplates(ctx context.Context, configuredTemplatesDir string) error

func EnsureEnvFile

func EnsureEnvFile(ctx context.Context, projectsRoot, dirPath string) error

func EnsureServiceImage added in v2.8.0

func EnsureServiceImage(projectID, projectName, serviceName string, svc composetypes.ServiceConfig) (string, composetypes.ServiceConfig, bool)

EnsureServiceImage gives a build-only service a deterministic local image tag so the built image has a stable name to deploy from. It reports whether the service config was changed.

func EnsureTemplateDir

func EnsureTemplateDir(ctx context.Context, templatesDir, base string) (dir, composePath, envPath string, err error)

func ExistingOrDefaultOverrideName added in v2.8.0

func ExistingOrDefaultOverrideName(dir string) string

ExistingOrDefaultOverrideName returns the override file name already present in dir, falling back to DefaultComposeOverrideFileName.

func FormatDockerPorts added in v2.8.0

func FormatDockerPorts(ports []container.PortSummary) []string

FormatDockerPorts renders container port summaries in the same shape as FormatPorts, for containers Arcane sees outside a compose project.

func FormatPorts added in v2.8.0

func FormatPorts(publishers []api.PortPublisher) []string

FormatPorts renders compose port publishers as "published:target/proto" (or "target/proto" when the port is not published).

func GetHostPathForContainerPath added in v2.2.0

func GetHostPathForContainerPath(ctx context.Context, dockerCli *client.Client, containerPath string) (string, error)

GetHostPathForContainerPath attempts to discover the host-side path for a given container path by inspecting the container itself. This is useful for Docker-in-Docker scenarios where the application needs to know host paths for volume mapping. It returns an empty string when the path is not covered by any of Arcane's mounts.

func GetProjectsDirectory

func GetProjectsDirectory(ctx context.Context, projectsDir string) (string, error)

func GetTemplatesDirectory

func GetTemplatesDirectory(ctx context.Context, templatesDir string) (string, error)

func HasComposeRootKeysInFile

func HasComposeRootKeysInFile(path string) (bool, error)

func ImageRefsFromComposeConfigs

func ImageRefsFromComposeConfigs(services []composetypes.ServiceConfig) []string

ImageRefsFromComposeConfigs returns unique, non-empty image references from compose service configs while preserving first-seen order.

func ImageRefsFromComposeServices

func ImageRefsFromComposeServices(services composetypes.Services) []string

ImageRefsFromComposeServices returns unique, non-empty image references from a compose service map in stable service-name order.

func ImageRefsFromRuntimeServices

func ImageRefsFromRuntimeServices(services []projecttypes.RuntimeService) []string

ImageRefsFromRuntimeServices returns unique, non-empty image references from runtime service DTOs while preserving first-seen order.

func IsAlwaysPullPolicy added in v2.3.2

func IsAlwaysPullPolicy(policy string) bool

IsAlwaysPullPolicy reports whether policy means an unconditional pull. Refresh windows (daily/weekly/every_N) are not "always": they pull only once their window has elapsed, matching compose v5.5.0.

func IsFilesystemSnapshotDirName added in v2.4.0

func IsFilesystemSnapshotDirName(name string) bool

IsFilesystemSnapshotDirName reports whether name is a well-known filesystem snapshot/trash directory (see filesystemSnapshotDirNames).

func IsGitOpsScratchDirName added in v2.3.0

func IsGitOpsScratchDirName(name string) bool

IsGitOpsScratchDirName reports whether name is an Arcane GitOps scratch directory: the hidden staging/backup temp dirs (".gitops-sync-stage-*", ".gitops-backup-*") or the legacy name-embedded backup form ("<name>.gitops-backup-<digits>"). These are Arcane-internal working directories and must never be imported as user projects.

func IsInternalScratchDirName added in v2.3.0

func IsInternalScratchDirName(name string) bool

IsInternalScratchDirName reports whether name is any Arcane-managed scratch directory that must never be discovered or imported as a user project: the project-update preview/backup temp dirs, the quarantine/trash dirs (see ArcaneTrashPrefix), or the GitOps sync-stage/backup dirs (see IsGitOpsScratchDirName). This is the single source of truth for the discovery walker and the DB cleanup pass.

func IsProjectDirectoryEntry

func IsProjectDirectoryEntry(entry os.DirEntry, path string, followSymlinks bool) bool

IsProjectDirectoryEntry reports whether a directory entry should be treated as a project directory. Regular directories are always accepted. Symlinked directories are accepted only when enabled.

func IsProjectDirectoryPath

func IsProjectDirectoryPath(path string, followSymlinks bool) (bool, error)

IsProjectDirectoryPath reports whether an existing path should be treated as a project directory. Regular directories are always accepted. Symlinked directories are accepted only when enabled.

Discovery stays on os.*: with followSymlinks enabled it deliberately follows symlinked project directories whose targets live outside the projects directory, which the root-confined acfs API cannot do.

func IsProjectFile

func IsProjectFile(filename string) bool

IsProjectFile reports whether filename is a known project file or a plausible custom YAML filename worth watching for compose discovery.

func IsSafeSubdirectory

func IsSafeSubdirectory(baseDir, subdir string) bool

IsSafeSubdirectory returns true if subdir is a subdirectory of baseDir (absolute, normalized)

func IsWindowsDrivePath

func IsWindowsDrivePath(candidate string) bool

IsWindowsDrivePath returns true if the path looks like a Windows drive path (e.g., "C:/path")

func LabelsFromCompose added in v2.1.0

func LabelsFromCompose(labels composetypes.Labels) map[string]string

LabelsFromCompose copies compose labels into a plain map, returning nil when empty.

func ListGlobalComposeContainers

func ListGlobalComposeContainers(ctx context.Context, dockerClient client.APIClient, dockerHost string) ([]container.Summary, error)

ListGlobalComposeContainers lists every container carrying a compose project label. This is a plain ContainerList — no compose service is needed — so dockerClient should be the process-wide Docker client singleton. When nil (callers wired without one, e.g. tests), a compose client for the config-resolved dockerHost is used instead.

func LoadComposeProject

func LoadComposeProject(
	ctx context.Context,
	composeFile string,
	projectName string,
	projectsDirectory string,
	autoInjectEnv bool,
	pathMapper *PathMapper,
	envOverride EnvMap,
	configureLoader func(*loader.Options),
	lenient bool,
) (project *composetypes.Project, err error)

LoadComposeProject loads a compose project from composeFile. envOverride and configureLoader are optional and may be nil. When lenient is true, undefined ${VAR} references are tolerated: instead of substituting them with an empty string (which produces invalid volume/bind specs like ":/path"), they are replaced with a placeholder value so structural validation can succeed. This is useful during GitSync validation where a .env file may not yet exist.

func LoadComposeProjectFromContent added in v2.6.0

func LoadComposeProjectFromContent(ctx context.Context, opts projecttypes.ComposeContentOptions) (project *composetypes.Project, err error)

LoadComposeProjectFromContent loads a Compose project from in-memory source content.

func LoadComposeProjectFromDir

func LoadComposeProjectFromDir(ctx context.Context, dir, projectName, projectsDirectory string, autoInjectEnv bool, pathMapper *PathMapper) (*composetypes.Project, string, error)

func MarshalImageRefsJSON added in v2.3.2

func MarshalImageRefsJSON(refs []string) string

MarshalImageRefsJSON serializes image references to JSON, returning an empty string when there are no refs or encoding fails.

func MergeBuildTags added in v2.1.0

func MergeBuildTags(primaryImage string, composeTags []string) []string

MergeBuildTags combines a primary image tag with compose tags, trimming blanks and de-duplicating while preserving order (primary first).

func NormalizeBuildSelections added in v2.8.0

func NormalizeBuildSelections(services []string) map[string]struct{}

NormalizeBuildSelections turns a user-supplied service list into a lookup set, dropping blanks. An empty set means "every service".

func NormalizeDeployPullPolicy added in v2.3.2

func NormalizeDeployPullPolicy(policy string) string

NormalizeDeployPullPolicy returns a supported deploy pull policy or empty string.

func NormalizeProjectName added in v2.3.2

func NormalizeProjectName(name string) string

NormalizeProjectName returns compose-go's normalized project name. If compose-go returns empty, the input is returned unchanged.

func NormalizeProjectTag added in v2.8.0

func NormalizeProjectTag(name string) (string, error)

NormalizeProjectTag validates and normalizes a project tag name.

func NormalizeProjectTagColor added in v2.8.0

func NormalizeProjectTagColor(color projecttypes.TagColor) (projecttypes.TagColor, error)

NormalizeProjectTagColor validates and normalizes a project tag color.

func NormalizeProjectTags added in v2.8.0

func NormalizeProjectTags(names []string) ([]string, error)

NormalizeProjectTags validates, normalizes, and de-duplicates project tags.

func NormalizePullPolicy added in v2.3.2

func NormalizePullPolicy(policy string) string

NormalizePullPolicy normalizes compose pull policy aliases.

func ParseEnvContent

func ParseEnvContent(content string) []env.Variable

ParseEnvContent parses environment variables from .env file content

func ParseImageRefsJSON added in v2.3.2

func ParseImageRefsJSON(raw string) []string

ParseImageRefsJSON parses a JSON array of image references, returning nil for empty or invalid input.

func PathContainsSnapshotDirectory added in v2.4.0

func PathContainsSnapshotDirectory(relPath string) bool

PathContainsSnapshotDirectory reports whether any segment of relPath is a filesystem snapshot/trash directory name, i.e. the path points into a point-in-time copy rather than a live project directory.

func PrepareDeployServiceConfig added in v2.8.0

func PrepareDeployServiceConfig(projectID, projectName, serviceName string, svc composetypes.ServiceConfig) (composetypes.ServiceConfig, string, bool)

PrepareDeployServiceConfig resolves the image a service deploys as, naming build-only services via EnsureServiceImage. It reports whether the service config was changed.

func ProtectedProjectFilePaths added in v2.1.0

func ProtectedProjectFilePaths(composeFileName string) map[string]bool

func ReadComposeOverrideContent added in v2.4.0

func ReadComposeOverrideContent(dir string) string

ReadComposeOverrideContent returns the content of the highest-preference compose override file present in dir, or "" when none exists or it cannot be read. It is a best-effort read intended for change detection.

func ReadFolderComposeTemplate

func ReadFolderComposeTemplate(baseDir, folder string) (string, *string, string, bool, error)

ReadFolderComposeTemplate stays on os.* for its reads: template folders are user-managed on disk, so compose/env files may be symlinks resolving outside any confinement root, which acfs cannot follow.

func ReadProjectFiles

func ReadProjectFiles(projectPath, composePath string) (composeContent, envContent string, err error)

ReadProjectFiles stays on os.*: compose and env files may be symlinks resolving outside any confinement root, and projectPath can be an imported project outside the projects directory; acfs cannot follow either.

func ReadProjectWorkspace added in v2.8.0

func ReadProjectWorkspace(projectPath string, maxDepth int, skipDirectories, composeFileName string, maxEntries int, maxFileSizeBytes int64) ([]workspacetypes.FileEntry, string, bool, error)

func RemapEscapedRelativeSources added in v2.6.0

func RemapEscapedRelativeSources(
	ctx context.Context,
	pathMapper projecttypes.VolumeSourcePathMapper,
	project *composetypes.Project,
	containerWorkingDir string,
	rawSources map[string]string,
	includeFileResources bool,
)

RemapEscapedRelativeSources rewrites daemon-side paths that compose-go resolved against the container-side project directory but that TranslateVolumeSources could not map, because they escaped every mounted directory.

compose-go absolutizes relative paths against ConfigDetails.WorkingDir, which for Arcane is a container path. A source like `../../data` can therefore land outside the projects mount, where prefix translation has nothing to match and leaves the path untouched — so the daemon creates it at the wrong place on the host, unlike `docker compose up` run in the project directory. Relativity is not recoverable from the resolved path (an intentionally absolute source is an identical string by then), so the raw pre-resolution paths are passed in as rawSources, keyed by VolumeSourceKey and by "<kind>:<name>".

Anything TranslateVolumeSources already rewrote is left alone, so nested independently-bind-mounted directories keep resolving through the mount table.

func RemoveProjectFile

func RemoveProjectFile(ctx context.Context, projectsRoot, dirPath, fileName string) error

func RemoveStaleComposeFiles

func RemoveStaleComposeFiles(ctx context.Context, projectPath, composeFileName string, syncedFiles []string) error

func ResolveBuildContext added in v2.3.2

func ResolveBuildContext(workingDir string, svc composetypes.ServiceConfig, serviceName string) (string, error)

ResolveBuildContext resolves a service build context against workingDir. The service config must have a non-nil Build field.

func ResolveComposeOverride added in v2.4.0

func ResolveComposeOverride(exists func(name string) bool, read func(name string) (string, error)) (fileName string, content string, found bool, err error)

ResolveComposeOverride finds the highest-preference compose override file among the supported candidates using the exists probe, then loads it via read. It centralizes override discovery so non-filesystem sources (e.g. a Git working tree accessed through a validated client) reuse the same preference order. When multiple candidates exist it warns and uses the highest-preference match, mirroring DetectComposeOverrideFile. found is false (with empty name/content) when no candidate exists.

func ResolveConfiguredContainerDirectory

func ResolveConfiguredContainerDirectory(configuredPath, defaultPath string) string

func ResolveDirectoryIdentityInternal

func ResolveDirectoryIdentityInternal(path string) (string, error)

ResolveDirectoryIdentityInternal stays on os.*: it resolves symlinks that may point anywhere on the host to establish directory identity, which the root-confined acfs API cannot do.

func ResolveDockerfilePath added in v2.3.2

func ResolveDockerfilePath(svc composetypes.ServiceConfig) string

ResolveDockerfilePath returns the configured Dockerfile path or Dockerfile. The service config must have a non-nil Build field.

func ResolveEffectiveOverrideForValidation added in v2.8.0

func ResolveEffectiveOverrideForValidation(dir string, overrideContent *string) (*string, string)

ResolveEffectiveOverrideForValidation resolves the override content compose validation should see: the on-disk override when the caller supplied none, nothing when the caller explicitly blanked it, otherwise the caller's content.

func ResolveHostPath added in v2.1.0

func ResolveHostPath(mounts []HostMount, containerPath string) mo.Option[string]

ResolveHostPath returns the host-side path for containerPath by selecting the longest-prefix mount whose Destination contains it and appending the trailing relative segment to that mount's Source. It returns None when no mount contains the path.

func ResolvePathWithinDir added in v2.6.0

func ResolvePathWithinDir(baseDir, path string) (string, error)

ResolvePathWithinDir resolves path and rejects paths that escape baseDir.

func ResolveRelativeProjectPaths

func ResolveRelativeProjectPaths(project *composetypes.Project, workdir string)

func RestoreProjectUpdateBackup added in v2.4.0

func RestoreProjectUpdateBackup(ctx context.Context, projectDir string, backup *ProjectUpdateBackup) error

RestoreProjectUpdateBackup rolls the scoped parts of projectDir back to the state captured in backup. Files are restored in place (preserving inodes so container bind mounts stay valid) and out-of-scope files are never touched.

func RollbackRenamedProjectDirectory added in v2.3.2

func RollbackRenamedProjectDirectory(ctx context.Context, oldPath, newPath string) (pathsMissing bool, err error)

RollbackRenamedProjectDirectory restores a project directory rename when possible.

func SanitizeImageComponent added in v2.1.0

func SanitizeImageComponent(value string) string

SanitizeImageComponent lowercases a value and replaces characters that are invalid in an image reference component with '-'.

func SanitizeProjectName

func SanitizeProjectName(name string) string

func SelectedImageRefs added in v2.8.0

func SelectedImageRefs(compProj *composetypes.Project, servicesToUpdate []string) []string

SelectedImageRefs returns the distinct pullable image refs of the selected services — build services are excluded, since their images come from a build.

func ServiceSelected added in v2.8.0

func ServiceSelected(selected map[string]struct{}, name string) bool

ServiceSelected reports whether name is in the selection built by NormalizeBuildSelections. An empty selection selects everything.

func ShouldPullDeployImage added in v2.8.0

func ShouldPullDeployImage(decision DeployImageDecision, exists bool, lastTagged time.Time) bool

ShouldPullDeployImage reports whether a deploy must pull, given the resolved pull decision, whether the image is already present locally, and when the local image was last tagged (zero when unknown). Refresh policies mirror compose v5.5.0's `up`: a present image is re-pulled only once its window has elapsed since the engine's last-tag time.

func Slugify

func Slugify(in string) string

func UlimitsFromCompose added in v2.1.0

func UlimitsFromCompose(ulimits map[string]*composetypes.UlimitsConfig) map[string]string

UlimitsFromCompose renders compose ulimits as Docker-style "soft:hard" (or single) strings.

func ValidateIncludePathForWrite

func ValidateIncludePathForWrite(projectDir, includePath string) (string, error)

ValidateIncludePathForWrite ensures the include path is safe for write operations Returns the validated absolute path to prevent recomputation after validation Only allows writing within the project directory

func VolumeSourceKey added in v2.6.0

func VolumeSourceKey(service, target string) string

VolumeSourceKey identifies a service bind mount across two loads of the same Compose input. Bind targets are unique per service, so the target is a stable key even though the source is exactly what differs between the loads.

func WithTransientValidationEnvFile added in v2.3.2

func WithTransientValidationEnvFile(ctx context.Context, projectPath string, effectiveEnvContent *string, run func() error) (err error)

WithTransientValidationEnvFile temporarily writes a project .env file while running compose validation, then restores the original file state.

func WriteComposeFile

func WriteComposeFile(ctx context.Context, projectsRoot, dirPath, content string) error

WriteComposeFile writes a compose file to the specified directory. It detects existing compose file names (docker-compose.yml, compose.yaml, etc.) and uses the existing name if found, otherwise defaults to compose.yaml projectsRoot is the allowed root directory to prevent path traversal attacks

func WriteComposeOverrideFile added in v2.4.0

func WriteComposeOverrideFile(ctx context.Context, projectsRoot, dir string, content *string, fileName string) error

WriteComposeOverrideFile writes content as the override file named fileName in dir when content is non-nil, and removes every other override candidate so a renamed or deleted override never leaves a stale copy behind. When content is nil, all supported override files are removed. projectsRoot bounds writes to prevent path traversal.

func WriteFileWithPerm

func WriteFileWithPerm(filePath, content string, perm os.FileMode) error

WriteFileWithPerm is a generic file writer with custom permissions

func WriteManagedEnvFile added in v2.3.2

func WriteManagedEnvFile(ctx context.Context, projectsDirectory, projectPath, fileName string, unreadable bool, content string) error

WriteManagedEnvFile writes (or, for project.env, removes) one of the three env-merge bookkeeping files — fileName must be EffectiveEnvFileName, GitSourceEnvFileName, or OverrideEnvFileName. If the existing file is permission-locked, the write is skipped and a warning logged instead: its contents can't be verified, and a locked file is typically unwritable too, so attempting the write would abort the whole caller.

func WriteProjectFile

func WriteProjectFile(ctx context.Context, projectsRoot, dirPath, fileName, content string) error

func WriteProjectFiles

func WriteProjectFiles(ctx context.Context, projectsRoot, dirPath, composeContent string, envContent *string) error

WriteProjectFiles writes both compose and env files to a project directory. An empty .env file is always created to prevent compose-go from failing when the compose file references env_file: .env projectsRoot is the allowed root directory to prevent path traversal attacks

func WriteSyncedDirectory

func WriteSyncedDirectory(ctx context.Context, projectsRoot, projectPath string, files []SyncFile) ([]string, error)

WriteSyncedDirectory writes multiple files to a project directory. It validates all paths are within the project directory and creates subdirectories as needed. Returns the list of written file paths.

func WriteTemplateFile

func WriteTemplateFile(filePath, content string) error

WriteTemplateFile writes a template file (like .compose.template or .env.template)

func WriteTemplateFiles

func WriteTemplateFiles(composePath, envPath, composeContent, envContent string) (*string, error)

Types

type ArcaneComposeMetadata

type ArcaneComposeMetadata struct {
	// ProjectIcon contains fallback, light, and dark icon values for the project.
	ProjectIcon IconSet
	// ProjectURLS are additional URLs related to the project (e.g., documentation, homepage).
	ProjectURLS []string
	// ProjectTags are normalized tags managed by Compose metadata.
	ProjectTags []projecttypes.TagOption
	// ProjectTagsAuthoritative reports whether every Compose tag was parsed successfully.
	ProjectTagsAuthoritative bool
	// ServiceIconSets maps service names to their fallback, light, and dark icon values.
	ServiceIconSets map[string]IconSet
}

ArcaneComposeMetadata represents Arcane-specific configuration extracted from a Compose file.

func ParseArcaneComposeMetadata

func ParseArcaneComposeMetadata(ctx context.Context, composeFilePath, projectsDirectory string, autoInjectEnv bool) (ArcaneComposeMetadata, error)

ParseArcaneComposeMetadata reads a Docker Compose file and extracts Arcane-specific metadata. When projectsDirectory is set, Arcane's project env loading is used so .env.global is available.

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(ctx context.Context, dockerHost string, authConfigs map[string]registry.AuthConfig, prompt compose.Prompt) (*Client, error)

NewClient builds a compose client. dockerHost, when non-empty, pins the docker CLI to that daemon endpoint instead of letting it resolve one from the environment.

func (*Client) Close

func (c *Client) Close() error

type DeployImageDecision added in v2.3.2

type DeployImageDecision struct {
	Build         bool
	PullAlways    bool
	PullIfMissing bool
	// PullIfStale pulls when the image is missing or its last-tag time is
	// older than StaleAfter (pull_policy daily/weekly/every_N).
	PullIfStale             bool
	StaleAfter              time.Duration
	FallbackBuildOnPullFail bool
	RequireLocalOnly        bool
}

DeployImageDecision describes how deploy should handle a service image.

func DecideDeployImageAction added in v2.3.2

func DecideDeployImageAction(svc composetypes.ServiceConfig, pullPolicyOverride string) DeployImageDecision

DecideDeployImageAction decides whether deploy should build, pull, or require local images.

type DiscoveredProjectDir

type DiscoveredProjectDir struct {
	DirName string
	Path    string
}

func DiscoverProjectDirectories

func DiscoverProjectDirectories(root string, followSymlinks bool, maxDepth int) ([]DiscoveredProjectDir, error)

type EnvLoader

type EnvLoader struct {
	// contains filtered or unexported fields
}

func NewEnvLoader

func NewEnvLoader(projectsDir, workdir string, autoInjectEnv bool) *EnvLoader

func (*EnvLoader) LoadEnvironment

func (l *EnvLoader) LoadEnvironment(ctx context.Context) (envMap EnvMap, injectionVars EnvMap, err error)

LoadEnvironment loads and merges environment variables from all sources: 1. Allowlisted process environment (TZ) 2. Global .env.global file (from projects directory) 3. Project-specific .env file (from workdir) The rest of the Arcane process environment is intentionally excluded so its own variables never leak into compose interpolation of managed projects.

type EnvMap

type EnvMap = map[string]string

func BuildValidationEnvironment added in v2.8.0

func BuildValidationEnvironment(projectsDirectory, projectPath string, effectiveEnvContent *string) (EnvMap, error)

BuildValidationEnvironment resolves the env a compose file is validated against. It matches the project-visible env sources without inheriting the Arcane process environment, which may contain unrelated secrets.

func ParseProjectEnvContent

func ParseProjectEnvContent(content string, contextEnv EnvMap) (EnvMap, error)

ParseProjectEnvContent parses project .env content from a string using compose-go's dotenv parser with variable expansion. Lookups resolve from contextEnv (previously loaded vars) only; the Arcane process environment is intentionally never consulted so its variables don't leak into project env.

func ParseProjectEnvFile

func ParseProjectEnvFile(path string, contextEnv EnvMap) (EnvMap, error)

ParseProjectEnvFile parses a project .env file with variable expansion using the provided context map (e.g. process env). Returns nil without error when the file does not exist. Only the specified file is read — global env files are intentionally not loaded here.

Stays on os.*: env files may be symlinks resolving outside any confinement root (a supported setup), which acfs cannot follow.

func ParseValidationEnvContent added in v2.8.0

func ParseValidationEnvContent(content string, contextEnv EnvMap) (EnvMap, error)

ParseValidationEnvContent parses env content, resolving interpolations against contextEnv rather than the process environment.

func ParseValidationEnvFile added in v2.8.0

func ParseValidationEnvFile(path string, contextEnv EnvMap) (EnvMap, error)

ParseValidationEnvFile parses one env file against contextEnv. A missing file is not an error.

Stays on os.*: env files may be symlinks resolving outside any confinement root (a supported setup), which acfs cannot follow.

type HostMount added in v2.1.0

type HostMount struct {
	Destination string // container-side mount path, e.g. "/app/data/projects/homeassistant"
	Source      string // host-side path, e.g. "/home/user/homeassistant"
}

HostMount is one container→host mount (bind or named volume) from Arcane's own container. It is used for longest-prefix host-path resolution in Docker-in-Docker setups, where independently bind-mounted project directories each map to their own host path rather than a single projects-root prefix.

func GetCurrentContainerMounts added in v2.2.0

func GetCurrentContainerMounts(ctx context.Context, dockerCli *client.Client) ([]HostMount, error)

GetCurrentContainerMounts inspects Arcane's own container and returns its bind and named-volume mounts as HostMount entries. It returns no mounts when Arcane is not running in a container (or the daemon is unreachable). This is the basis for Docker-in-Docker host-path resolution.

type IconSet

type IconSet = iconcatalog.IconSet

func FindArcaneIconSet

func FindArcaneIconSet(labels any) IconSet

FindArcaneIconSet attempts to locate Arcane icon labels within service labels. It supports both map[string]string and []string label formats.

type ImagePullMode added in v2.3.2

type ImagePullMode int

ImagePullMode describes when Arcane should pull an image for a project.

const (
	// ImagePullModeNever skips pulling the image.
	ImagePullModeNever ImagePullMode = iota
	// ImagePullModeIfMissing pulls only when the image is missing locally.
	ImagePullModeIfMissing
	// ImagePullModeRefresh pulls when the image is missing locally or was last
	// tagged longer ago than the policy's refresh window.
	ImagePullModeRefresh
	// ImagePullModeAlways pulls even when the image is present locally.
	ImagePullModeAlways
)

type ImagePullStep added in v2.8.1

type ImagePullStep struct {
	Mode         ImagePullMode
	RefreshAfter time.Duration
}

ImagePullStep is one entry of an image pull plan: the mode plus, for ImagePullModeRefresh, the window after which a new pull is due.

func ResolveServiceImagePullMode added in v2.3.2

func ResolveServiceImagePullMode(svc composetypes.ServiceConfig) ImagePullStep

ResolveServiceImagePullMode resolves compose pull_policy into Arcane's pull step.

type IncludeFile

type IncludeFile struct {
	Path         string `json:"path"`
	RelativePath string `json:"relative_path"`
	Content      string `json:"content"`
}

func ParseIncludes

func ParseIncludes(composeFilePath string, envMap EnvMap, includeContent bool) ([]IncludeFile, error)

ParseIncludes reads a compose file and extracts all include directives. envMap is used to expand variables (e.g., ${VAR}) in include paths.

Include handling stays on os.*: include files are allowed to live outside the project directory (#3556), which the root-confined acfs API cannot reach.

func ParseIncludesFromContent

func ParseIncludesFromContent(composeFilePath string, content []byte, envMap EnvMap, includeContent bool) ([]IncludeFile, error)

ParseIncludesFromContent extracts include directives from compose content using composeFilePath as the base path.

type MissingIncludeStubLoader added in v2.3.2

type MissingIncludeStubLoader struct {
	// contains filtered or unexported fields
}

MissingIncludeStubLoader creates temporary stub compose files for missing include paths during validation.

func NewMissingIncludeStubLoader added in v2.3.2

func NewMissingIncludeStubLoader(projectPath string) *MissingIncludeStubLoader

NewMissingIncludeStubLoader creates a loader for validation stubs under projectPath.

func (*MissingIncludeStubLoader) Accept added in v2.3.2

func (l *MissingIncludeStubLoader) Accept(path string) bool

func (*MissingIncludeStubLoader) Cleanup added in v2.3.2

func (l *MissingIncludeStubLoader) Cleanup()

Cleanup removes any temporary validation stub files created by the loader.

func (*MissingIncludeStubLoader) Dir added in v2.3.2

func (l *MissingIncludeStubLoader) Dir(path string) string

func (*MissingIncludeStubLoader) Load added in v2.3.2

func (l *MissingIncludeStubLoader) Load(ctx context.Context, filePath string) (string, error)

type PathMapper

type PathMapper struct {
	// contains filtered or unexported fields
}

PathMapper handles translation between container and host paths

func NewPathMapper

func NewPathMapper(containerDir, hostDir string) *PathMapper

NewPathMapper creates a new path mapper

func NewPathMapperForConfiguredDirectory added in v2.6.0

func NewPathMapperForConfiguredDirectory(ctx context.Context, configuredPath, defaultDir string, dockerClient *client.Client) *PathMapper

NewPathMapperForConfiguredDirectory resolves an optional container-to-host directory mapping and falls back to Docker mount discovery when no explicit host path is configured.

func NewPathMapperFromMounts added in v2.1.0

func NewPathMapperFromMounts(mounts []HostMount) *PathMapper

NewPathMapperFromMounts creates a path mapper that resolves each source against the given container mount table by longest-prefix match, instead of a single container→host prefix. This is used for Docker-in-Docker auto-discovery so that an independently bind-mounted project directory maps to its real host path.

func (*PathMapper) ContainerToHost

func (pm *PathMapper) ContainerToHost(containerPath string) (string, error)

ContainerToHost translates a container path to host path

func (*PathMapper) IsNonMatchingMount

func (pm *PathMapper) IsNonMatchingMount() bool

func (*PathMapper) TranslateVolumeSources

func (pm *PathMapper) TranslateVolumeSources(project *composetypes.Project, translateFileResources bool) error

TranslateVolumeSources translates bind mount sources in a Compose project. File-backed configs and secrets are translated only when the Docker host, rather than Arcane itself, will read those files.

type ProjectEnvMode

type ProjectEnvMode string

type ProjectEnvState

type ProjectEnvState struct {
	Mode             ProjectEnvMode
	EditableFileName string
	EditableContent  string
	EffectiveContent string
	DirectContent    string
	GitContent       string
	OverrideContent  string
	HasEffective     bool
	HasGitSource     bool
	HasOverride      bool
	// The *Unreadable fields report a file that exists on disk but could not be
	// read because of a permission error (e.g. a chmod 000 or foreign-owned
	// file reachable through a bind mount). Such a file is treated as absent
	// for merge purposes, and callers persisting env state must not attempt to
	// write or remove it — its contents are unknown, so writing could either
	// fail (bricking the caller) or silently clobber operator intent.
	EffectiveUnreadable bool
	GitSourceUnreadable bool
	OverrideUnreadable  bool
}

func ReadProjectEnvState

func ReadProjectEnvState(projectPath string) (ProjectEnvState, error)

type ProjectUpdateBackup added in v2.4.0

type ProjectUpdateBackup struct {
	BackupDir     string
	TopLevelFiles bool
	FileEntries   []string    // regular file contents copied into BackupDir
	DirEntries    []string    // directories copied recursively
	AbsentEntries []string    // did not exist at backup time -> removed on restore
	RenamedDirs   [][2]string // undone via inverse rename on restore
	Skipped       []string    // unreadable, skipped; preserved on restore
	// contains filtered or unexported fields
}

ProjectUpdateBackup records what BackupProjectUpdateScope copied so RestoreProjectUpdateBackup can put the project directory back without touching anything outside the update's scope.

func BackupProjectUpdateScope added in v2.4.0

func BackupProjectUpdateScope(ctx context.Context, projectDir, backupDir string, scope ProjectUpdateBackupScope) (*ProjectUpdateBackup, error)

BackupProjectUpdateScope copies the parts of projectDir named by scope into backupDir. Unreadable files are skipped (recorded in Skipped) so an unrelated foreign-owned file cannot block a save, matching the tolerant semantics of the old whole-directory backup.

The backup/restore engine stays on os.Root rather than acfs: restoring a project .env means recreating its symlink (which may point outside the project directory), and acfs deliberately refuses symlink mutation.

type ProjectUpdateBackupScope added in v2.4.0

type ProjectUpdateBackupScope struct {
	// TopLevelFiles backs up every top-level regular file in the project
	// directory. Compose/env persistence only ever writes top-level files.
	TopLevelFiles bool
	// Paths are normalized project-relative paths a file change can create,
	// overwrite or delete.
	Paths []string
	// RenamedDirs holds {src, dest} pairs for a rename/move of an existing
	// directory. These are rolled back with an inverse rename instead of a
	// copy, so a huge directory move never triggers a full copy.
	RenamedDirs [][2]string
}

ProjectUpdateBackupScope describes exactly what a project update can mutate, so the pre-update backup copies only those paths instead of the whole project directory (which may contain huge container data directories).

func (ProjectUpdateBackupScope) IsEmpty added in v2.4.0

func (s ProjectUpdateBackupScope) IsEmpty() bool

type ProjectWorkspaceApplyOptions added in v2.8.0

type ProjectWorkspaceApplyOptions struct {
	ExpectedRevision string
	MaxDepth         int
	MaxEntries       int
	SkipDirectories  string
	ComposeFileName  string
	MaxFileSizeBytes int64
}

type SyncFile

type SyncFile struct {
	RelativePath string // Path relative to the project directory
	Content      []byte
	// Executable preserves the source's +x bit so lifecycle hooks and other
	// repo-committed scripts arrive runnable in the project workspace.
	Executable bool
}

SyncFile represents a file to be written during directory sync

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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