1*105f6285SAndroid Build Coastguard Worker// Copyright 2020 Google LLC 2*105f6285SAndroid Build Coastguard Worker// 3*105f6285SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*105f6285SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*105f6285SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*105f6285SAndroid Build Coastguard Worker// 7*105f6285SAndroid Build Coastguard Worker// https://www.apache.org/licenses/LICENSE-2.0 8*105f6285SAndroid Build Coastguard Worker// 9*105f6285SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*105f6285SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*105f6285SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*105f6285SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*105f6285SAndroid Build Coastguard Worker// limitations under the License. 14*105f6285SAndroid Build Coastguard Worker 15*105f6285SAndroid Build Coastguard Worker// Package workspace let's you manage workspaces 16*105f6285SAndroid Build Coastguard Workerpackage workspace 17*105f6285SAndroid Build Coastguard Worker 18*105f6285SAndroid Build Coastguard Workerimport ( 19*105f6285SAndroid Build Coastguard Worker "fmt" 20*105f6285SAndroid Build Coastguard Worker "io/ioutil" 21*105f6285SAndroid Build Coastguard Worker "os" 22*105f6285SAndroid Build Coastguard Worker "os/exec" 23*105f6285SAndroid Build Coastguard Worker "path/filepath" 24*105f6285SAndroid Build Coastguard Worker "strings" 25*105f6285SAndroid Build Coastguard Worker 26*105f6285SAndroid Build Coastguard Worker "android.googlesource.com/platform/tools/treble.git/hacksaw/bind" 27*105f6285SAndroid Build Coastguard Worker "android.googlesource.com/platform/tools/treble.git/hacksaw/codebase" 28*105f6285SAndroid Build Coastguard Worker "android.googlesource.com/platform/tools/treble.git/hacksaw/config" 29*105f6285SAndroid Build Coastguard Worker "android.googlesource.com/platform/tools/treble.git/hacksaw/git" 30*105f6285SAndroid Build Coastguard Worker) 31*105f6285SAndroid Build Coastguard Worker 32*105f6285SAndroid Build Coastguard Workertype Workspace struct { 33*105f6285SAndroid Build Coastguard Worker composer Composer 34*105f6285SAndroid Build Coastguard Worker topDir string 35*105f6285SAndroid Build Coastguard Worker} 36*105f6285SAndroid Build Coastguard Worker 37*105f6285SAndroid Build Coastguard Workerfunc New(bm bind.PathBinder, topDir string) Workspace { 38*105f6285SAndroid Build Coastguard Worker return Workspace{NewComposer(bm), topDir} 39*105f6285SAndroid Build Coastguard Worker} 40*105f6285SAndroid Build Coastguard Worker 41*105f6285SAndroid Build Coastguard Worker// Create workspace 42*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) Create(workspaceName string, codebaseName string) (string, error) { 43*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 44*105f6285SAndroid Build Coastguard Worker _, ok := cfg.Codebases[codebaseName] 45*105f6285SAndroid Build Coastguard Worker if !ok { 46*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Codebase %s does not exist", codebaseName) 47*105f6285SAndroid Build Coastguard Worker } 48*105f6285SAndroid Build Coastguard Worker if _, ok := cfg.Workspaces[workspaceName]; ok { 49*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Workspace %s already exists", workspaceName) 50*105f6285SAndroid Build Coastguard Worker } 51*105f6285SAndroid Build Coastguard Worker cfg.Workspaces[workspaceName] = codebaseName 52*105f6285SAndroid Build Coastguard Worker workspaceDir, err := w.GetDir(workspaceName) 53*105f6285SAndroid Build Coastguard Worker if err != nil { 54*105f6285SAndroid Build Coastguard Worker return "", err 55*105f6285SAndroid Build Coastguard Worker } 56*105f6285SAndroid Build Coastguard Worker if err = os.MkdirAll(workspaceDir, os.ModePerm); err != nil { 57*105f6285SAndroid Build Coastguard Worker return "", err 58*105f6285SAndroid Build Coastguard Worker } 59*105f6285SAndroid Build Coastguard Worker codebaseDir, err := codebase.GetDir(codebaseName) 60*105f6285SAndroid Build Coastguard Worker if err != nil { 61*105f6285SAndroid Build Coastguard Worker return "", err 62*105f6285SAndroid Build Coastguard Worker } 63*105f6285SAndroid Build Coastguard Worker //TODO: match the order of parameters with Create 64*105f6285SAndroid Build Coastguard Worker if _, err = w.composer.Compose(codebaseDir, workspaceDir); err != nil { 65*105f6285SAndroid Build Coastguard Worker return "", err 66*105f6285SAndroid Build Coastguard Worker } 67*105f6285SAndroid Build Coastguard Worker return workspaceDir, nil 68*105f6285SAndroid Build Coastguard Worker} 69*105f6285SAndroid Build Coastguard Worker 70*105f6285SAndroid Build Coastguard Worker// Recreate workspace 71*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) Recreate(workspaceName string) (string, error) { 72*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 73*105f6285SAndroid Build Coastguard Worker codebaseName, ok := cfg.Workspaces[workspaceName] 74*105f6285SAndroid Build Coastguard Worker if !ok { 75*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Workspace %s does not exist", workspaceName) 76*105f6285SAndroid Build Coastguard Worker } 77*105f6285SAndroid Build Coastguard Worker workspaceDir, err := w.GetDir(workspaceName) 78*105f6285SAndroid Build Coastguard Worker if err != nil { 79*105f6285SAndroid Build Coastguard Worker return "", err 80*105f6285SAndroid Build Coastguard Worker } 81*105f6285SAndroid Build Coastguard Worker codebaseDir, err := codebase.GetDir(codebaseName) 82*105f6285SAndroid Build Coastguard Worker if err != nil { 83*105f6285SAndroid Build Coastguard Worker return "", err 84*105f6285SAndroid Build Coastguard Worker } 85*105f6285SAndroid Build Coastguard Worker if _, err = w.composer.Compose(codebaseDir, workspaceDir); err != nil { 86*105f6285SAndroid Build Coastguard Worker return "", err 87*105f6285SAndroid Build Coastguard Worker } 88*105f6285SAndroid Build Coastguard Worker return workspaceDir, nil 89*105f6285SAndroid Build Coastguard Worker} 90*105f6285SAndroid Build Coastguard Worker 91*105f6285SAndroid Build Coastguard Worker// GetDir retrieves the directory of a specific workspace 92*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) GetDir(workspaceName string) (string, error) { 93*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 94*105f6285SAndroid Build Coastguard Worker _, ok := cfg.Workspaces[workspaceName] 95*105f6285SAndroid Build Coastguard Worker if !ok { 96*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Workspace %s not found", workspaceName) 97*105f6285SAndroid Build Coastguard Worker } 98*105f6285SAndroid Build Coastguard Worker dir := filepath.Join(w.topDir, workspaceName) 99*105f6285SAndroid Build Coastguard Worker return dir, nil 100*105f6285SAndroid Build Coastguard Worker} 101*105f6285SAndroid Build Coastguard Worker 102*105f6285SAndroid Build Coastguard Worker// GetCodebase retrieves the codebase that a workspace belongs to 103*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) GetCodebase(workspaceName string) (string, error) { 104*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 105*105f6285SAndroid Build Coastguard Worker codebase, ok := cfg.Workspaces[workspaceName] 106*105f6285SAndroid Build Coastguard Worker if !ok { 107*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Workspace %s not found", workspaceName) 108*105f6285SAndroid Build Coastguard Worker } 109*105f6285SAndroid Build Coastguard Worker return codebase, nil 110*105f6285SAndroid Build Coastguard Worker} 111*105f6285SAndroid Build Coastguard Worker 112*105f6285SAndroid Build Coastguard Worker//SetTopDir sets the directory that contains all workspaces 113*105f6285SAndroid Build Coastguard Workerfunc (w *Workspace) SetTopDir(dir string) { 114*105f6285SAndroid Build Coastguard Worker w.topDir = dir 115*105f6285SAndroid Build Coastguard Worker} 116*105f6285SAndroid Build Coastguard Worker 117*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) List() map[string]string { 118*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 119*105f6285SAndroid Build Coastguard Worker list := make(map[string]string) 120*105f6285SAndroid Build Coastguard Worker for name, codebaseName := range cfg.Workspaces { 121*105f6285SAndroid Build Coastguard Worker list[name] = codebaseName 122*105f6285SAndroid Build Coastguard Worker } 123*105f6285SAndroid Build Coastguard Worker return list 124*105f6285SAndroid Build Coastguard Worker} 125*105f6285SAndroid Build Coastguard Worker 126*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) DetachGitWorktrees(workspaceName string, unbindList []string) error { 127*105f6285SAndroid Build Coastguard Worker workspaceDir, err := w.GetDir(workspaceName) 128*105f6285SAndroid Build Coastguard Worker if err != nil { 129*105f6285SAndroid Build Coastguard Worker return err 130*105f6285SAndroid Build Coastguard Worker } 131*105f6285SAndroid Build Coastguard Worker workspaceDir, err = filepath.Abs(workspaceDir) 132*105f6285SAndroid Build Coastguard Worker if err != nil { 133*105f6285SAndroid Build Coastguard Worker return err 134*105f6285SAndroid Build Coastguard Worker } 135*105f6285SAndroid Build Coastguard Worker //resolve all symlinks so it can be 136*105f6285SAndroid Build Coastguard Worker //matched to mount paths 137*105f6285SAndroid Build Coastguard Worker workspaceDir, err = filepath.EvalSymlinks(workspaceDir) 138*105f6285SAndroid Build Coastguard Worker if err != nil { 139*105f6285SAndroid Build Coastguard Worker return err 140*105f6285SAndroid Build Coastguard Worker } 141*105f6285SAndroid Build Coastguard Worker codebaseName, err := w.GetCodebase(workspaceName) 142*105f6285SAndroid Build Coastguard Worker if err != nil { 143*105f6285SAndroid Build Coastguard Worker return err 144*105f6285SAndroid Build Coastguard Worker } 145*105f6285SAndroid Build Coastguard Worker codebaseDir, err := codebase.GetDir(codebaseName) 146*105f6285SAndroid Build Coastguard Worker if err != nil { 147*105f6285SAndroid Build Coastguard Worker return err 148*105f6285SAndroid Build Coastguard Worker } 149*105f6285SAndroid Build Coastguard Worker lister := git.NewRepoLister() 150*105f6285SAndroid Build Coastguard Worker gitProjects, err := lister.List(codebaseDir) 151*105f6285SAndroid Build Coastguard Worker if err != nil { 152*105f6285SAndroid Build Coastguard Worker return err 153*105f6285SAndroid Build Coastguard Worker } 154*105f6285SAndroid Build Coastguard Worker gitWorktrees := make(map[string]bool) 155*105f6285SAndroid Build Coastguard Worker for _, project := range gitProjects { 156*105f6285SAndroid Build Coastguard Worker gitWorktrees[project] = true 157*105f6285SAndroid Build Coastguard Worker } 158*105f6285SAndroid Build Coastguard Worker //projects that were unbound were definitely 159*105f6285SAndroid Build Coastguard Worker //never git worktrees 160*105f6285SAndroid Build Coastguard Worker for _, unbindPath := range unbindList { 161*105f6285SAndroid Build Coastguard Worker project, err := filepath.Rel(workspaceDir, unbindPath) 162*105f6285SAndroid Build Coastguard Worker if err != nil { 163*105f6285SAndroid Build Coastguard Worker return err 164*105f6285SAndroid Build Coastguard Worker } 165*105f6285SAndroid Build Coastguard Worker if _, ok := gitWorktrees[project]; ok { 166*105f6285SAndroid Build Coastguard Worker gitWorktrees[project] = false 167*105f6285SAndroid Build Coastguard Worker } 168*105f6285SAndroid Build Coastguard Worker } 169*105f6285SAndroid Build Coastguard Worker for project, isWorktree := range gitWorktrees { 170*105f6285SAndroid Build Coastguard Worker if !isWorktree { 171*105f6285SAndroid Build Coastguard Worker continue 172*105f6285SAndroid Build Coastguard Worker } 173*105f6285SAndroid Build Coastguard Worker codebaseProject := filepath.Join(codebaseDir, project) 174*105f6285SAndroid Build Coastguard Worker workspaceProject := filepath.Join(workspaceDir, project) 175*105f6285SAndroid Build Coastguard Worker _, err = os.Stat(workspaceProject) 176*105f6285SAndroid Build Coastguard Worker if err == nil { 177*105f6285SAndroid Build Coastguard Worker //proceed to detach 178*105f6285SAndroid Build Coastguard Worker } else if os.IsNotExist(err) { 179*105f6285SAndroid Build Coastguard Worker //just skip if it doesn't exist 180*105f6285SAndroid Build Coastguard Worker continue 181*105f6285SAndroid Build Coastguard Worker } else { 182*105f6285SAndroid Build Coastguard Worker return err 183*105f6285SAndroid Build Coastguard Worker } 184*105f6285SAndroid Build Coastguard Worker contents, err := ioutil.ReadDir(workspaceProject) 185*105f6285SAndroid Build Coastguard Worker if err != nil { 186*105f6285SAndroid Build Coastguard Worker return err 187*105f6285SAndroid Build Coastguard Worker } 188*105f6285SAndroid Build Coastguard Worker if len(contents) == 0 { 189*105f6285SAndroid Build Coastguard Worker //empty directory, not even a .git 190*105f6285SAndroid Build Coastguard Worker //not a wortree 191*105f6285SAndroid Build Coastguard Worker continue 192*105f6285SAndroid Build Coastguard Worker } 193*105f6285SAndroid Build Coastguard Worker fmt.Print(".") 194*105f6285SAndroid Build Coastguard Worker cmd := exec.Command("git", 195*105f6285SAndroid Build Coastguard Worker "-C", codebaseProject, 196*105f6285SAndroid Build Coastguard Worker "worktree", "remove", "--force", workspaceProject) 197*105f6285SAndroid Build Coastguard Worker output, err := cmd.CombinedOutput() 198*105f6285SAndroid Build Coastguard Worker if err != nil { 199*105f6285SAndroid Build Coastguard Worker return fmt.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 200*105f6285SAndroid Build Coastguard Worker cmd.String(), err.Error(), output) 201*105f6285SAndroid Build Coastguard Worker } 202*105f6285SAndroid Build Coastguard Worker cmd = exec.Command("git", 203*105f6285SAndroid Build Coastguard Worker "-C", codebaseProject, 204*105f6285SAndroid Build Coastguard Worker "branch", "--delete", "--force", workspaceName) 205*105f6285SAndroid Build Coastguard Worker output, err = cmd.CombinedOutput() 206*105f6285SAndroid Build Coastguard Worker if err != nil { 207*105f6285SAndroid Build Coastguard Worker return fmt.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 208*105f6285SAndroid Build Coastguard Worker cmd.String(), err.Error(), output) 209*105f6285SAndroid Build Coastguard Worker } 210*105f6285SAndroid Build Coastguard Worker } 211*105f6285SAndroid Build Coastguard Worker return nil 212*105f6285SAndroid Build Coastguard Worker} 213*105f6285SAndroid Build Coastguard Worker 214*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) Remove(remove string) (*config.Config, error) { 215*105f6285SAndroid Build Coastguard Worker cfg := config.GetConfig() 216*105f6285SAndroid Build Coastguard Worker _, ok := cfg.Workspaces[remove] 217*105f6285SAndroid Build Coastguard Worker if !ok { 218*105f6285SAndroid Build Coastguard Worker return cfg, fmt.Errorf("Workspace %s not found", remove) 219*105f6285SAndroid Build Coastguard Worker } 220*105f6285SAndroid Build Coastguard Worker workspaceDir, err := w.GetDir(remove) 221*105f6285SAndroid Build Coastguard Worker if err != nil { 222*105f6285SAndroid Build Coastguard Worker return cfg, err 223*105f6285SAndroid Build Coastguard Worker } 224*105f6285SAndroid Build Coastguard Worker unbindList, err := w.composer.Dismantle(workspaceDir) 225*105f6285SAndroid Build Coastguard Worker if err != nil { 226*105f6285SAndroid Build Coastguard Worker return cfg, err 227*105f6285SAndroid Build Coastguard Worker } 228*105f6285SAndroid Build Coastguard Worker fmt.Print("Detaching worktrees") 229*105f6285SAndroid Build Coastguard Worker if err = w.DetachGitWorktrees(remove, unbindList); err != nil { 230*105f6285SAndroid Build Coastguard Worker return cfg, err 231*105f6285SAndroid Build Coastguard Worker } 232*105f6285SAndroid Build Coastguard Worker fmt.Print("\n") 233*105f6285SAndroid Build Coastguard Worker fmt.Println("Removing files") 234*105f6285SAndroid Build Coastguard Worker if err = os.RemoveAll(workspaceDir); err != nil { 235*105f6285SAndroid Build Coastguard Worker return cfg, err 236*105f6285SAndroid Build Coastguard Worker } 237*105f6285SAndroid Build Coastguard Worker delete(cfg.Workspaces, remove) 238*105f6285SAndroid Build Coastguard Worker return cfg, err 239*105f6285SAndroid Build Coastguard Worker} 240*105f6285SAndroid Build Coastguard Worker 241*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) Edit(editPath string) (string, string, error) { 242*105f6285SAndroid Build Coastguard Worker editPath, err := filepath.Abs(editPath) 243*105f6285SAndroid Build Coastguard Worker if err != nil { 244*105f6285SAndroid Build Coastguard Worker return "", "", err 245*105f6285SAndroid Build Coastguard Worker } 246*105f6285SAndroid Build Coastguard Worker editPath, err = filepath.EvalSymlinks(editPath) 247*105f6285SAndroid Build Coastguard Worker if err != nil { 248*105f6285SAndroid Build Coastguard Worker return "", "", err 249*105f6285SAndroid Build Coastguard Worker } 250*105f6285SAndroid Build Coastguard Worker relProjectPath, err := w.getReadOnlyProjectFromPath(editPath) 251*105f6285SAndroid Build Coastguard Worker if err != nil { 252*105f6285SAndroid Build Coastguard Worker return "", "", err 253*105f6285SAndroid Build Coastguard Worker } 254*105f6285SAndroid Build Coastguard Worker workspaceName, err := w.getWorkspaceFromPath(editPath) 255*105f6285SAndroid Build Coastguard Worker if err != nil { 256*105f6285SAndroid Build Coastguard Worker return "", "", err 257*105f6285SAndroid Build Coastguard Worker } 258*105f6285SAndroid Build Coastguard Worker workspaceDir, err := w.GetDir(workspaceName) 259*105f6285SAndroid Build Coastguard Worker if err != nil { 260*105f6285SAndroid Build Coastguard Worker return "", "", err 261*105f6285SAndroid Build Coastguard Worker } 262*105f6285SAndroid Build Coastguard Worker codebaseName, err := w.GetCodebase(workspaceName) 263*105f6285SAndroid Build Coastguard Worker if err != nil { 264*105f6285SAndroid Build Coastguard Worker return "", "", err 265*105f6285SAndroid Build Coastguard Worker } 266*105f6285SAndroid Build Coastguard Worker codebaseDir, err := codebase.GetDir(codebaseName) 267*105f6285SAndroid Build Coastguard Worker if err != nil { 268*105f6285SAndroid Build Coastguard Worker return "", "", err 269*105f6285SAndroid Build Coastguard Worker } 270*105f6285SAndroid Build Coastguard Worker wsProjectPath := filepath.Join(workspaceDir, relProjectPath) 271*105f6285SAndroid Build Coastguard Worker if err = w.composer.Unbind(wsProjectPath); err != nil { 272*105f6285SAndroid Build Coastguard Worker return "", "", err 273*105f6285SAndroid Build Coastguard Worker } 274*105f6285SAndroid Build Coastguard Worker //TODO: support editing nested projects 275*105f6285SAndroid Build Coastguard Worker //the command above unbinds nested child projects but 276*105f6285SAndroid Build Coastguard Worker //we don't rebind them after checking out an editable project branch 277*105f6285SAndroid Build Coastguard Worker cbProjectPath := filepath.Join(codebaseDir, relProjectPath) 278*105f6285SAndroid Build Coastguard Worker branchName := workspaceName 279*105f6285SAndroid Build Coastguard Worker cmd := exec.Command("git", 280*105f6285SAndroid Build Coastguard Worker "-C", cbProjectPath, 281*105f6285SAndroid Build Coastguard Worker "worktree", "add", 282*105f6285SAndroid Build Coastguard Worker "-b", branchName, 283*105f6285SAndroid Build Coastguard Worker wsProjectPath) 284*105f6285SAndroid Build Coastguard Worker output, err := cmd.CombinedOutput() 285*105f6285SAndroid Build Coastguard Worker if err != nil { 286*105f6285SAndroid Build Coastguard Worker return "", "", fmt.Errorf("Command\n%s\nfailed with the following:\n%s\n%s", 287*105f6285SAndroid Build Coastguard Worker cmd.String(), err.Error(), output) 288*105f6285SAndroid Build Coastguard Worker } 289*105f6285SAndroid Build Coastguard Worker return branchName, wsProjectPath, err 290*105f6285SAndroid Build Coastguard Worker} 291*105f6285SAndroid Build Coastguard Worker 292*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) getReadOnlyProjectFromPath(inPath string) (string, error) { 293*105f6285SAndroid Build Coastguard Worker worspaceName, err := w.getWorkspaceFromPath(inPath) 294*105f6285SAndroid Build Coastguard Worker if err != nil { 295*105f6285SAndroid Build Coastguard Worker return "", err 296*105f6285SAndroid Build Coastguard Worker } 297*105f6285SAndroid Build Coastguard Worker workspacePath, err := w.GetDir(worspaceName) 298*105f6285SAndroid Build Coastguard Worker if err != nil { 299*105f6285SAndroid Build Coastguard Worker return "", err 300*105f6285SAndroid Build Coastguard Worker } 301*105f6285SAndroid Build Coastguard Worker bindList, err := w.composer.List(workspacePath) 302*105f6285SAndroid Build Coastguard Worker if err != nil { 303*105f6285SAndroid Build Coastguard Worker return "", err 304*105f6285SAndroid Build Coastguard Worker } 305*105f6285SAndroid Build Coastguard Worker for _, bindPath := range bindList { 306*105f6285SAndroid Build Coastguard Worker if !strings.HasPrefix(inPath+"/", bindPath+"/") { 307*105f6285SAndroid Build Coastguard Worker continue 308*105f6285SAndroid Build Coastguard Worker } 309*105f6285SAndroid Build Coastguard Worker relProjectPath, err := filepath.Rel(workspacePath, bindPath) 310*105f6285SAndroid Build Coastguard Worker if err != nil { 311*105f6285SAndroid Build Coastguard Worker return "", err 312*105f6285SAndroid Build Coastguard Worker } 313*105f6285SAndroid Build Coastguard Worker return relProjectPath, nil 314*105f6285SAndroid Build Coastguard Worker } 315*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Path %s is already editable", inPath) 316*105f6285SAndroid Build Coastguard Worker} 317*105f6285SAndroid Build Coastguard Worker 318*105f6285SAndroid Build Coastguard Workerfunc (w Workspace) getWorkspaceFromPath(inPath string) (string, error) { 319*105f6285SAndroid Build Coastguard Worker for workspaceName, _ := range w.List() { 320*105f6285SAndroid Build Coastguard Worker dir, err := w.GetDir(workspaceName) 321*105f6285SAndroid Build Coastguard Worker if err != nil { 322*105f6285SAndroid Build Coastguard Worker return "", err 323*105f6285SAndroid Build Coastguard Worker } 324*105f6285SAndroid Build Coastguard Worker if strings.HasPrefix(inPath+"/", dir+"/") { 325*105f6285SAndroid Build Coastguard Worker return workspaceName, nil 326*105f6285SAndroid Build Coastguard Worker } 327*105f6285SAndroid Build Coastguard Worker } 328*105f6285SAndroid Build Coastguard Worker return "", fmt.Errorf("Path %s is not contained in a workspace", inPath) 329*105f6285SAndroid Build Coastguard Worker} 330