Documentation
¶
Overview ¶
Package linker owns the decision half of registering a directory as a lerd site. Every caller that links a project — the CLI, the parked-directory watcher, the web UI and the MCP server — resolves the same plan here and differs only in the capabilities it grants through a Policy.
Index ¶
- func FilterConflictingDomains(desired []string, ownPath string, allSites []config.Site) (kept, removed []string)
- func FreeSiteName(desired, path string) string
- func HostProxyGate(command string, disabled, skipConfirm, approved, canPrompt bool) (proceed, ask bool, reason string)
- func IsReservedDomain(domain string) bool
- func OwningWorktree(dir string) (*config.Site, string, bool)
- func ResolveDomains(desired []string, baseName, ownPath, tld string) (kept, removed []string)
- func ResolveFramework(dir string, allowStoreFallback bool) (string, bool)
- type Deps
- type Mode
- type NopReporter
- type Plan
- type Policy
- type Prompter
- type Reporter
- type Result
- type Skip
- type Step
- type TextReporter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FilterConflictingDomains ¶
func FilterConflictingDomains(desired []string, ownPath string, allSites []config.Site) (kept, removed []string)
FilterConflictingDomains splits desired into the domains ownPath may claim and those a different site already holds. The check is strict: a domain is a conflict regardless of TLS scheme, because DNS and browser caches don't disambiguate by scheme reliably. Order is preserved so a surviving preferred domain stays primary. Re-linking the same path is not a conflict.
func FreeSiteName ¶
FreeSiteName returns the first available site name for a path. An unused name is returned as-is, as is one already held by the same path (a re-link). A name held by a different path gets "-2", "-3", … until one is free.
func HostProxyGate ¶
func HostProxyGate(command string, disabled, skipConfirm, approved, canPrompt bool) (proceed, ask bool, reason string)
HostProxyGate is the decision for whether lerd may supervise a dev-server command on the host. It is pure so the policy is testable without a terminal or a config file. proceed reports that the command may run without asking; ask reports that a confirmation is still owed; reason explains a refusal. A site only ever runs a command the user has consented to.
func IsReservedDomain ¶
IsReservedDomain reports whether a domain is reserved for lerd's own use.
func OwningWorktree ¶
OwningWorktree returns the site dir is a git worktree of, so a checkout under a registered project is not registered again as a site of its own.
func ResolveDomains ¶
ResolveDomains filters the desired domain list against the live registry and returns the list to register. When every desired domain is conflicted it falls back to a freshly generated `<baseName>.<tld>`, suffixed until it is free in both the name and domain axes. The .lerd.yaml on disk is never touched; the discrepancy lives only in the registration, and the dropped domains come back in removed so the caller can report them.
Types ¶
type Deps ¶
type Deps struct {
// EnsureFPMQuadlet builds a PHP version's image and unit.
EnsureFPMQuadlet func(version string) error
// ReconcileRuntimeQuadlets clears a per-site FrankenPHP or custom-FPM unit
// the site no longer uses.
ReconcileRuntimeQuadlets func(site config.Site)
// StartHostProxyWorker supervises a host-proxy site's dev command.
StartHostProxyWorker func(site config.Site, proxy *config.ProxyConfig)
// SyncIDEDataSource points a JetBrains project at the site's database and
// reports whether it wrote anything.
SyncIDEDataSource func(siteRoot string) bool
}
Deps are the side effects a link needs that live above this package. A nil field means the caller cannot do that thing, and the step is skipped.
type NopReporter ¶
type NopReporter struct{}
NopReporter discards everything, for callers that report on their own.
func (NopReporter) Line ¶
func (NopReporter) Line(string)
func (NopReporter) Step ¶
func (NopReporter) Step(string) Step
func (NopReporter) Val ¶
func (NopReporter) Val(s string) string
func (NopReporter) Warn ¶
func (NopReporter) Warn(string, ...any)
type Plan ¶
type Plan struct {
// Dir is the directory being linked, as given.
Dir string
// Site is the registration that will be written.
Site config.Site
// Mode is how Site will be served.
Mode Mode
// Skip is set when the directory will not be registered at all, and
// SkipDetail explains it in words the caller can print.
Skip Skip
SkipDetail string
// WorktreeParent and WorktreeBranch are set alongside SkipWorktree: the
// site that owns this checkout, and the branch it holds.
WorktreeParent *config.Site
WorktreeBranch string
// Project is the parsed .lerd.yaml, or nil when the project has none.
Project *config.ProjectConfig
// DroppedDomains lists domains another site already owns, which were
// filtered out of Site.Domains.
DroppedDomains []string
// PHPSuggestion names a PHP version that suits the framework better than
// the one Site carries. Empty when the chosen version is already the best
// installed one, or when the policy forbids building images.
PHPSuggestion string
// PHPMin and PHPMax are the framework's supported range, for the message
// that accompanies a clamped version. Both empty when unconstrained.
PHPMin, PHPMax string
// FrameworkLabel names the framework in user-facing messages, and is
// "unknown (public: <dir>)" when detection found none.
FrameworkLabel string
// FrankenPHPDeclined records that the project asked for FrankenPHP but no
// image exists for its PHP version, so the site falls back to FPM.
FrankenPHPDeclined bool
// ProxyCommand is the host dev-server command awaiting consent, empty when
// the site runs none.
ProxyCommand string
}
Plan is everything a link decided before it changed anything. Resolve produces it from the directory, the global config and the policy; Apply carries it out.
func Resolve ¶
Resolve decides everything about linking dir as a site without changing anything. It reads the directory, the project's .lerd.yaml and the site registry, and returns the plan Apply carries out. A plan whose Skip is set registers nothing.
func (*Plan) Registered ¶
Registered reports whether the plan will actually register a site.
type Policy ¶
type Policy struct {
// Name overrides the site name and primary domain. Empty derives both from
// the directory name.
Name string
// AssumeYes records consent given outside a prompt, such as `lerd link
// --yes` or the click that started a link from the web UI.
AssumeYes bool
// Prompt, when non-nil, may ask the user to resolve a decision. Nil means
// no question can be asked, whether or not a terminal is attached.
Prompt Prompter
// ProjectWrites allows writing into the project directory: the .php-version
// and .node-version pins, and the .lerd.yaml domain and framework writeback.
ProjectWrites bool
// Services allows installing and starting the services the project declares
// and the ones its framework requires.
Services bool
// Certs allows the site to be registered as secured, which issues a
// certificate for it.
Certs bool
// RepoCommands allows running commands the repository authored: a
// host-proxy dev server and inline service containers. Consent for these
// still goes through the usual gate; this only says the caller is a context
// where running them is on the table at all.
RepoCommands bool
// ImageBuild allows building or pulling a PHP image the host does not have.
ImageBuild bool
// SkipRegistered stops the link when the directory is already a registered
// site, rather than re-linking it.
SkipRegistered bool
// DeferPublish leaves the reloads that publish a link — systemd, the quadlet
// rewrite, the container hosts file and nginx — to the caller, so a batch of
// links does that work once at the end instead of once per project.
DeferPublish bool
}
Policy is the capability set a caller grants a link. The zero value is the most restrictive one: it registers a site and serves it, and does nothing else. Each field widens that.
func CLIPolicy ¶
CLIPolicy is the policy for a user-invoked `lerd link`: everything is permitted, and prompt decides whether questions can be asked.
func WatcherPolicy ¶
func WatcherPolicy() Policy
WatcherPolicy is the policy for `lerd park` and the parked-directory watcher. It runs unattended against every subdirectory of a parked tree, so it reads the project's committed configuration but never asks a question, never writes into the project, and never runs anything the repository authored.
type Prompter ¶
type Prompter interface {
// Confirm asks a yes/no question and reports the answer.
Confirm(question string, defaultYes bool) bool
// Choose asks the user to pick one of options and returns its index.
Choose(title string, options []string) (int, error)
}
Prompter resolves a question a link cannot answer on its own. Callers with a terminal supply one; every other caller passes nil, and the link takes the conservative branch instead of blocking on input that will never arrive.
type Reporter ¶
type Reporter interface {
// Step announces work that is starting and returns the handle that closes it.
Step(label string) Step
// Line reports something worth showing that is not a step.
Line(msg string)
// Warn reports a problem that did not stop the link.
Warn(format string, a ...any)
// Val styles a value inside a message, so the linker can compose detail
// strings without knowing how the caller renders them.
Val(s string) string
}
Reporter renders a link's progress. The CLI backs it with its feedback layer; a daemon or an assistant backs it with plain lines or nothing at all.
type Result ¶
type Result struct {
Plan *Plan
// Site is the registration as written, which can differ from the plan's
// when a newly installed PHP version was accepted.
Site config.Site
// WroteIDEDataSource records that a JetBrains data source was written.
WroteIDEDataSource bool
}
Result is what a link did.
func Apply ¶
Apply carries out a plan: it registers the site, provisions the runtime that serves it, and performs the project writes the policy allows. A plan that skips registers nothing and returns without error.
func (*Result) Registered ¶
Registered reports whether a site was actually registered.
type TextReporter ¶
TextReporter writes plain lines, for the watcher and other log-only callers.
func (TextReporter) Line ¶
func (t TextReporter) Line(msg string)
func (TextReporter) Step ¶
func (t TextReporter) Step(label string) Step
func (TextReporter) Val ¶
func (TextReporter) Val(s string) string
func (TextReporter) Warn ¶
func (t TextReporter) Warn(format string, a ...any)