package projectsync import ( "context" "errors" "fmt" "os" "path/filepath" ) // ErrWorkspaceProvisionNotReady is returned when a workspace is not prepared. var ErrWorkspaceProvisionNotReady = errors.New("workspace provision is not ready") // WorkspaceProvisioner defines the interface for ensuring that the required // workspace paths and Git metadata are prepared. type WorkspaceProvisioner interface { EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error } // FilesystemWorkspaceProvisioner checks the local filesystem to verify // that the workspace topology matches the plan. type FilesystemWorkspaceProvisioner struct{} // EnsureProvisioned checks that the workspace root is a directory, // and that main branch, develop branch, and default slot paths are directories // containing a .git file or directory. func (FilesystemWorkspaceProvisioner) EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error { if err := ctx.Err(); err != nil { return err } if err := ensureDirectory(plan.ProjectWorkspaceRoot); err != nil { return err } for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath, plan.DefaultSlotPath} { if err := ctx.Err(); err != nil { return err } if err := ensureGitCheckout(path); err != nil { return err } } return nil } func ensureDirectory(path string) error { info, err := os.Stat(path) if err != nil { return fmt.Errorf("%w: path %q is not accessible: %v", ErrWorkspaceProvisionNotReady, path, err) } if !info.IsDir() { return fmt.Errorf("%w: path %q is not a directory", ErrWorkspaceProvisionNotReady, path) } return nil } func ensureGitCheckout(path string) error { if err := ensureDirectory(path); err != nil { return err } gitPath := filepath.Join(path, ".git") _, err := os.Stat(gitPath) if err != nil { return fmt.Errorf("%w: git metadata is missing at %q: %v", ErrWorkspaceProvisionNotReady, gitPath, err) } return nil }