package projectsync // ProvisionPlan describes the complete workspace topology that must be prepared // for a project before any slot checkout or sync operation can run. Building it // never executes a git process; it only resolves paths from a normalized Config. type ProvisionPlan struct { ProjectWorkspaceRoot string MainBranchPath string DevelopBranchPath string DefaultSlotPath string } // BuildProvisionPlan derives the workspace topology plan from a project sync // Config. It returns ErrInvalidConfig when the Config cannot be normalized. func BuildProvisionPlan(config Config) (ProvisionPlan, error) { normalized, err := config.Normalize() if err != nil { return ProvisionPlan{}, err } root, err := ProjectWorkspaceRoot(normalized.WorkspaceBasePath, normalized.RepoDirName) if err != nil { return ProvisionPlan{}, err } mainPath, err := BranchWorkspacePath(normalized.WorkspaceBasePath, normalized.RepoDirName, BranchNameMain) if err != nil { return ProvisionPlan{}, err } developPath, err := BranchWorkspacePath(normalized.WorkspaceBasePath, normalized.RepoDirName, BranchNameDevelop) if err != nil { return ProvisionPlan{}, err } defaultSlot, err := SlotWorkspacePath(normalized.WorkspaceBasePath, normalized.RepoDirName, DefaultSlotIndex) if err != nil { return ProvisionPlan{}, err } return ProvisionPlan{ ProjectWorkspaceRoot: root, MainBranchPath: mainPath, DevelopBranchPath: developPath, DefaultSlotPath: defaultSlot, }, nil } // CheckoutPlan is the policy object describing where a single workspace slot // should be checked out for a project sync target. It is derived purely from a // normalized Config and a reserved WorkspaceSlot; building it never runs a git // process. Actual clone/fetch/checkout execution is out of this package's scope. type CheckoutPlan struct { // RemoteURL and SourceBranch always come from the project sync Config, never // from request input, so every slot of the same project checks out the same // remote and source-of-truth branch. RemoteURL string SourceBranch string // ProjectWorkspaceRoot is the per-project workspace root and SlotPath is this // slot's independent checkout directory under that root. ProjectWorkspaceRoot string SlotPath string SlotIndex SlotIndex } // BuildCheckoutPlan derives a CheckoutPlan from a project sync Config and a // reserved slot. The git remote and source branch are read from the Config so // they can never be spoofed by request input, and each slot resolves to its own // zero-padded checkout path (000 for the default single-operation slot, 001/002 // for parallel slots). It returns ErrInvalidConfig when the Config or slot index // is invalid. func BuildCheckoutPlan(config Config, slot WorkspaceSlot) (CheckoutPlan, error) { normalized, err := config.Normalize() if err != nil { return CheckoutPlan{}, err } if !slot.Index.Valid() { return CheckoutPlan{}, ErrInvalidConfig } root, err := ProjectWorkspaceRoot(normalized.WorkspaceBasePath, normalized.RepoDirName) if err != nil { return CheckoutPlan{}, err } slotPath, err := SlotWorkspacePath(normalized.WorkspaceBasePath, normalized.RepoDirName, slot.Index) if err != nil { return CheckoutPlan{}, err } return CheckoutPlan{ RemoteURL: normalized.GitRemoteURL, SourceBranch: normalized.SourceBranch, ProjectWorkspaceRoot: root, SlotPath: slotPath, SlotIndex: slot.Index, }, nil }