github.com/kngu9/glide@v0.0.0-20160505135211-e73500c73591/glide.go (about) 1 // Glide is a command line utility that manages Go project dependencies and 2 // your GOPATH. 3 // 4 // Dependencies are managed via a glide.yaml in the root of a project. The yaml 5 // 6 // Params: 7 // - filename (string): The name of the glide YAML file. Default is glide.yaml. 8 // - project (string): The name of the project. Default is 'main'. 9 // file lets you specify projects, versions (tags, branches, or references), 10 // and even alias one location in as other one. Aliasing is useful when supporting 11 // forks without needing to rewrite the imports in a codebase. 12 // 13 // A glide.yaml file looks like: 14 // 15 // package: github.com/Masterminds/glide 16 // imports: 17 // - package: github.com/Masterminds/cookoo 18 // vcs: git 19 // ref: 1.1.0 20 // subpackages: ** 21 // - package: github.com/kylelemons/go-gypsy 22 // subpackages: yaml 23 // 24 // Glide puts dependencies in a vendor directory. Go utilities require this to 25 // be in your GOPATH. Glide makes this easy. Use the `glide in` command to enter 26 // a shell (your default) with the GOPATH set to the projects vendor directory. 27 // To leave this shell simply exit it. 28 // 29 // If your .bashrc, .zshrc, or other startup shell sets your GOPATH you many need 30 // to optionally set it using something like: 31 // 32 // if [ "" = "${GOPATH}" ]; then 33 // export GOPATH="/some/dir" 34 // fi 35 // 36 // For more information use the `glide help` command or see https://github.com/Masterminds/glide 37 package main 38 39 import ( 40 "path/filepath" 41 42 "github.com/Masterminds/glide/action" 43 "github.com/Masterminds/glide/msg" 44 gpath "github.com/Masterminds/glide/path" 45 "github.com/Masterminds/glide/repo" 46 "github.com/Masterminds/glide/util" 47 48 "github.com/codegangsta/cli" 49 50 "fmt" 51 "os" 52 "os/user" 53 ) 54 55 var version = "0.11.0-dev" 56 57 const usage = `The lightweight vendor package manager for your Go projects. 58 59 Each project should have a 'glide.yaml' file in the project directory. Files 60 look something like this: 61 62 package: github.com/Masterminds/glide 63 imports: 64 - package: github.com/Masterminds/cookoo 65 vcs: git 66 ref: 1.1.0 67 subpackages: ** 68 - package: github.com/kylelemons/go-gypsy 69 subpackages: yaml 70 flatten: true 71 72 NOTE: As of Glide 0.5, the commands 'into', 'gopath', 'status', and 'env' 73 no longer exist. 74 ` 75 76 // VendorDir default vendor directory name 77 var VendorDir = "vendor" 78 79 func main() { 80 app := cli.NewApp() 81 app.Name = "glide" 82 app.Usage = usage 83 app.Version = version 84 app.Flags = []cli.Flag{ 85 cli.StringFlag{ 86 Name: "yaml, y", 87 Value: "glide.yaml", 88 Usage: "Set a YAML configuration file.", 89 }, 90 cli.BoolFlag{ 91 Name: "quiet, q", 92 Usage: "Quiet (no info or debug messages)", 93 }, 94 cli.BoolFlag{ 95 Name: "debug", 96 Usage: "Print Debug messages (verbose)", 97 }, 98 cli.StringFlag{ 99 Name: "home", 100 Value: defaultGlideDir(), 101 Usage: "The location of Glide files", 102 EnvVar: "GLIDE_HOME", 103 }, 104 cli.BoolFlag{ 105 Name: "no-color", 106 Usage: "Turn off colored output for log messages", 107 }, 108 } 109 app.CommandNotFound = func(c *cli.Context, command string) { 110 // TODO: Set some useful env vars. 111 action.Plugin(command, os.Args) 112 } 113 app.Before = startup 114 app.Commands = commands() 115 116 // Detect errors from the Before and After calls and exit on them. 117 if err := app.Run(os.Args); err != nil { 118 msg.Err(err.Error()) 119 os.Exit(1) 120 } 121 122 // If there was a Error message exit non-zero. 123 if msg.HasErrored() { 124 m := msg.Color(msg.Red, "An Error has occurred") 125 msg.Msg(m) 126 os.Exit(2) 127 } 128 } 129 130 func commands() []cli.Command { 131 return []cli.Command{ 132 { 133 Name: "create", 134 ShortName: "init", 135 Usage: "Initialize a new project, creating a glide.yaml file", 136 Description: `This command starts from a project without Glide and 137 sets it up. It generates a glide.yaml file, parsing your codebase to guess 138 the dependencies to include. Once this step is done you may edit the 139 glide.yaml file to update imported dependency properties such as the version 140 or version range to include. 141 142 To fetch the dependencies you may run 'glide install'.`, 143 Flags: []cli.Flag{ 144 cli.BoolFlag{ 145 Name: "skip-import", 146 Usage: "When initializing skip importing from other package managers.", 147 }, 148 }, 149 Action: func(c *cli.Context) { 150 action.Create(".", c.Bool("skip-import")) 151 }, 152 }, 153 { 154 Name: "get", 155 Usage: "Install one or more packages into `vendor/` and add dependency to glide.yaml.", 156 Description: `Gets one or more package (like 'go get') and then adds that file 157 to the glide.yaml file. Multiple package names can be specified on one line. 158 159 $ glide get github.com/Masterminds/cookoo/web 160 161 The above will install the project github.com/Masterminds/cookoo and add 162 the subpackage 'web'. 163 164 If a fetched dependency has a glide.yaml file, configuration from Godep, 165 GPM, or GB Glide that configuration will be used to find the dependencies 166 and versions to fetch. If those are not available the dependent packages will 167 be fetched as either a version specified elsewhere or the latest version. 168 169 When adding a new dependency Glide will perform an update to work out the 170 the versions to use from the dependency tree. This will generate an updated 171 glide.lock file with specific locked versions to use. 172 173 If you are storing the outside dependencies in your version control system 174 (VCS), also known as vendoring, there are a few flags that may be useful. 175 The '--update-vendored' flag will cause Glide to update packages when VCS 176 information is unavailable. This can be used with the '--strip-vcs' flag which 177 will strip VCS data found in the vendor directory. This is useful for 178 removing VCS data from transitive dependencies and initial setups. The 179 '--strip-vendor' flag will remove any nested 'vendor' folders and 180 'Godeps/_workspace' folders after an update (along with undoing any Godep 181 import rewriting). Note, The Godeps specific functionality is deprecated and 182 will be removed when most Godeps users have migrated to using the vendor 183 folder.`, 184 Flags: []cli.Flag{ 185 cli.BoolFlag{ 186 Name: "insecure", 187 Usage: "Use http:// rather than https:// to retrieve pacakges.", 188 }, 189 cli.BoolFlag{ 190 Name: "no-recursive, quick", 191 Usage: "Disable updating dependencies' dependencies.", 192 }, 193 cli.BoolFlag{ 194 Name: "force", 195 Usage: "If there was a change in the repo or VCS switch to new one. Warning, changes will be lost.", 196 }, 197 cli.BoolFlag{ 198 Name: "all-dependencies", 199 Usage: "This will resolve all dependencies for all packages, not just those directly used.", 200 }, 201 cli.BoolFlag{ 202 Name: "update-vendored, u", 203 Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.", 204 }, 205 cli.BoolFlag{ 206 Name: "cache", 207 Usage: "When downloading dependencies attempt to cache them.", 208 }, 209 cli.BoolFlag{ 210 Name: "cache-gopath", 211 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 212 }, 213 cli.BoolFlag{ 214 Name: "use-gopath", 215 Usage: "Copy dependencies from the GOPATH if they exist there.", 216 }, 217 cli.BoolFlag{ 218 Name: "resolve-current", 219 Usage: "Resolve dependencies for only the current system rather than all build modes.", 220 }, 221 cli.BoolFlag{ 222 Name: "strip-vcs, s", 223 Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.", 224 }, 225 cli.BoolFlag{ 226 Name: "strip-vendor, v", 227 Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.", 228 }, 229 }, 230 Action: func(c *cli.Context) { 231 if c.Bool("strip-vendor") && !c.Bool("strip-vcs") { 232 msg.Die("--strip-vendor cannot be used without --strip-vcs") 233 } 234 235 if len(c.Args()) < 1 { 236 fmt.Println("Oops! Package name is required.") 237 os.Exit(1) 238 } 239 240 if c.Bool("resolve-current") { 241 util.ResolveCurrent = true 242 msg.Warn("Only resolving dependencies for the current OS/Arch") 243 } 244 245 inst := repo.NewInstaller() 246 inst.Force = c.Bool("force") 247 inst.UseCache = c.Bool("cache") 248 inst.UseGopath = c.Bool("use-gopath") 249 inst.UseCacheGopath = c.Bool("cache-gopath") 250 inst.UpdateVendored = c.Bool("update-vendored") 251 inst.ResolveAllFiles = c.Bool("all-dependencies") 252 packages := []string(c.Args()) 253 insecure := c.Bool("insecure") 254 action.Get(packages, inst, insecure, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor")) 255 }, 256 }, 257 { 258 Name: "remove", 259 ShortName: "rm", 260 Usage: "Remove a package from the glide.yaml file, and regenerate the lock file.", 261 Description: `This takes one or more package names, and removes references from the glide.yaml file. 262 This will rebuild the glide lock file with the following constraints: 263 264 - Dependencies are re-negotiated. Any that are no longer used are left out of the lock. 265 - Minor version re-nogotiation is performed on remaining dependencies. 266 - No updates are peformed. You may want to run 'glide up' to accomplish that.`, 267 Flags: []cli.Flag{ 268 cli.BoolFlag{ 269 Name: "delete,d", 270 Usage: "Also delete from vendor/ any packages that are no longer used.", 271 }, 272 }, 273 Action: func(c *cli.Context) { 274 if len(c.Args()) < 1 { 275 fmt.Println("Oops! At least one package name is required.") 276 os.Exit(1) 277 } 278 279 if c.Bool("delete") { 280 // FIXME: Implement this in the installer. 281 fmt.Println("Delete is not currently implemented.") 282 } 283 inst := repo.NewInstaller() 284 inst.Force = c.Bool("force") 285 packages := []string(c.Args()) 286 action.Remove(packages, inst) 287 }, 288 }, 289 { 290 Name: "import", 291 Usage: "Import files from other dependency management systems.", 292 Subcommands: []cli.Command{ 293 { 294 Name: "godep", 295 Usage: "Import Godep's Godeps.json files and display the would-be yaml file", 296 Flags: []cli.Flag{ 297 cli.StringFlag{ 298 Name: "file, f", 299 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 300 }, 301 }, 302 Action: func(c *cli.Context) { 303 action.ImportGodep(c.String("file")) 304 }, 305 }, 306 { 307 Name: "gpm", 308 Usage: "Import GPM's Godeps and Godeps-Git files and display the would-be yaml file", 309 Flags: []cli.Flag{ 310 cli.StringFlag{ 311 Name: "file, f", 312 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 313 }, 314 }, 315 Action: func(c *cli.Context) { 316 action.ImportGPM(c.String("file")) 317 }, 318 }, 319 { 320 Name: "gb", 321 Usage: "Import gb's manifest file and display the would-be yaml file", 322 Flags: []cli.Flag{ 323 cli.StringFlag{ 324 Name: "file, f", 325 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 326 }, 327 }, 328 Action: func(c *cli.Context) { 329 action.ImportGB(c.String("file")) 330 }, 331 }, 332 { 333 Name: "gom", 334 Usage: "Import Gomfile and display the would-be yaml file", 335 Flags: []cli.Flag{ 336 cli.StringFlag{ 337 Name: "file, f", 338 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 339 }, 340 }, 341 Action: func(c *cli.Context) { 342 action.ImportGom(c.String("file")) 343 }, 344 }, 345 }, 346 }, 347 { 348 Name: "name", 349 Usage: "Print the name of this project.", 350 Description: `Read the glide.yaml file and print the name given on the 'package' line.`, 351 Action: func(c *cli.Context) { 352 action.Name() 353 }, 354 }, 355 { 356 Name: "novendor", 357 ShortName: "nv", 358 Usage: "List all non-vendor paths in a directory.", 359 Description: `Given a directory, list all the relevant Go paths that are not vendored. 360 361 Example: 362 $ go test $(glide novendor)`, 363 Flags: []cli.Flag{ 364 cli.StringFlag{ 365 Name: "dir,d", 366 Usage: "Specify a directory to run novendor against.", 367 Value: ".", 368 }, 369 cli.BoolFlag{ 370 Name: "no-subdir,x", 371 Usage: "Specify this to prevent nv from append '/...' to all directories.", 372 }, 373 }, 374 Action: func(c *cli.Context) { 375 action.NoVendor(c.String("dir"), true, !c.Bool("no-subdir")) 376 }, 377 }, 378 { 379 Name: "rebuild", 380 Usage: "Rebuild ('go build') the dependencies", 381 Description: `This rebuilds the packages' '.a' files. On some systems 382 this can improve performance on subsequent 'go run' and 'go build' calls.`, 383 Action: func(c *cli.Context) { 384 action.Rebuild() 385 }, 386 }, 387 { 388 Name: "install", 389 ShortName: "i", 390 Usage: "Install a project's dependencies", 391 Description: `This uses the native VCS of each packages to install 392 the appropriate version. There are two ways a projects dependencies can 393 be installed. When there is a glide.yaml file defining the dependencies but 394 no lock file (glide.lock) the dependencies are installed using the "update" 395 command and a glide.lock file is generated pinning all dependencies. If a 396 glide.lock file is already present the dependencies are installed or updated 397 from the lock file.`, 398 Flags: []cli.Flag{ 399 cli.BoolFlag{ 400 Name: "delete", 401 Usage: "Delete vendor packages not specified in config.", 402 }, 403 cli.BoolFlag{ 404 Name: "force", 405 Usage: "If there was a change in the repo or VCS switch to new one. Warning: changes will be lost.", 406 }, 407 cli.BoolFlag{ 408 Name: "update-vendored, u", 409 Usage: "Update vendored packages (without local VCS repo). Warning: this may destroy local modifications to vendor/.", 410 }, 411 cli.StringFlag{ 412 Name: "file, f", 413 Usage: "Save all of the discovered dependencies to a Glide YAML file. (DEPRECATED: This has no impact.)", 414 }, 415 cli.BoolFlag{ 416 Name: "cache", 417 Usage: "When downloading dependencies attempt to cache them.", 418 }, 419 cli.BoolFlag{ 420 Name: "cache-gopath", 421 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 422 }, 423 cli.BoolFlag{ 424 Name: "use-gopath", 425 Usage: "Copy dependencies from the GOPATH if they exist there.", 426 }, 427 cli.BoolFlag{ 428 Name: "strip-vcs, s", 429 Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.", 430 }, 431 cli.BoolFlag{ 432 Name: "strip-vendor, v", 433 Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.", 434 }, 435 }, 436 Action: func(c *cli.Context) { 437 if c.Bool("strip-vendor") && !c.Bool("strip-vcs") { 438 msg.Die("--strip-vendor cannot be used without --strip-vcs") 439 } 440 441 installer := repo.NewInstaller() 442 installer.Force = c.Bool("force") 443 installer.UseCache = c.Bool("cache") 444 installer.UseGopath = c.Bool("use-gopath") 445 installer.UseCacheGopath = c.Bool("cache-gopath") 446 installer.UpdateVendored = c.Bool("update-vendored") 447 installer.Home = gpath.Home() 448 installer.DeleteUnused = c.Bool("deleteOptIn") 449 450 action.Install(installer, c.Bool("strip-vcs"), c.Bool("strip-vendor")) 451 }, 452 }, 453 { 454 Name: "update", 455 ShortName: "up", 456 Usage: "Update a project's dependencies", 457 Description: `This uses the native VCS of each package to try to 458 pull the most applicable updates. Packages with fixed refs (Versions or 459 tags) will not be updated. Packages with no ref or with a branch ref will 460 be updated as expected. 461 462 If a dependency has a glide.yaml file, update will read that file and 463 update those dependencies accordingly. Those dependencies are maintained in 464 a the top level 'vendor/' directory. 'vendor/foo/bar' will have its 465 dependencies stored in 'vendor/'. This behavior can be disabled with 466 '--no-recursive'. When this behavior is skipped a glide.lock file is not 467 generated because the full dependency tree cannot be known. 468 469 Glide will also import Godep, GB, and GPM files as it finds them in dependencies. 470 It will create a glide.yaml file from the Godeps data, and then update. This 471 has no effect if '--no-recursive' is set. 472 473 If you are storing the outside dependencies in your version control system 474 (VCS), also known as vendoring, there are a few flags that may be useful. 475 The '--update-vendored' flag will cause Glide to update packages when VCS 476 information is unavailable. This can be used with the '--strip-vcs' flag which 477 will strip VCS data found in the vendor directory. This is useful for 478 removing VCS data from transitive dependencies and initial setups. The 479 '--strip-vendor' flag will remove any nested 'vendor' folders and 480 'Godeps/_workspace' folders after an update (along with undoing any Godep 481 import rewriting). Note, The Godeps specific functionality is deprecated and 482 will be removed when most Godeps users have migrated to using the vendor 483 folder. 484 485 Note, Glide detects vendored dependencies. With the '--update-vendored' flag 486 Glide will update vendored dependencies leaving them in a vendored state. 487 Tertiary dependencies will not be vendored automatically unless the 488 '--strip-vcs' flag is used along with it. 489 490 By default, packages that are discovered are considered transient, and are 491 not stored in the glide.yaml file. The --file=NAME.yaml flag allows you 492 to save the discovered dependencies to a YAML file.`, 493 Flags: []cli.Flag{ 494 cli.BoolFlag{ 495 Name: "delete", 496 Usage: "Delete vendor packages not specified in config.", 497 }, 498 cli.BoolFlag{ 499 Name: "no-recursive, quick", 500 Usage: "Disable updating dependencies' dependencies. Only update things in glide.yaml.", 501 }, 502 cli.BoolFlag{ 503 Name: "force", 504 Usage: "If there was a change in the repo or VCS switch to new one. Warning, changes will be lost.", 505 }, 506 cli.BoolFlag{ 507 Name: "all-dependencies", 508 Usage: "This will resolve all dependencies for all packages, not just those directly used.", 509 }, 510 cli.BoolFlag{ 511 Name: "update-vendored, u", 512 Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.", 513 }, 514 cli.StringFlag{ 515 Name: "file, f", 516 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 517 }, 518 cli.BoolFlag{ 519 Name: "cache", 520 Usage: "When downloading dependencies attempt to cache them.", 521 }, 522 cli.BoolFlag{ 523 Name: "cache-gopath", 524 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 525 }, 526 cli.BoolFlag{ 527 Name: "use-gopath", 528 Usage: "Copy dependencies from the GOPATH if they exist there.", 529 }, 530 cli.BoolFlag{ 531 Name: "resolve-current", 532 Usage: "Resolve dependencies for only the current system rather than all build modes.", 533 }, 534 cli.BoolFlag{ 535 Name: "strip-vcs, s", 536 Usage: "Removes version control metada (e.g, .git directory) from the vendor folder.", 537 }, 538 cli.BoolFlag{ 539 Name: "strip-vendor, v", 540 Usage: "Removes nested vendor and Godeps/_workspace directories. Requires --strip-vcs.", 541 }, 542 }, 543 Action: func(c *cli.Context) { 544 if c.Bool("strip-vendor") && !c.Bool("strip-vcs") { 545 msg.Die("--strip-vendor cannot be used without --strip-vcs") 546 } 547 548 if c.Bool("resolve-current") { 549 util.ResolveCurrent = true 550 msg.Warn("Only resolving dependencies for the current OS/Arch") 551 } 552 553 installer := repo.NewInstaller() 554 installer.Force = c.Bool("force") 555 installer.UseCache = c.Bool("cache") 556 installer.UseGopath = c.Bool("use-gopath") 557 installer.UseCacheGopath = c.Bool("cache-gopath") 558 installer.UpdateVendored = c.Bool("update-vendored") 559 installer.ResolveAllFiles = c.Bool("all-dependencies") 560 installer.Home = gpath.Home() 561 installer.DeleteUnused = c.Bool("deleteOptIn") 562 563 action.Update(installer, c.Bool("no-recursive"), c.Bool("strip-vcs"), c.Bool("strip-vendor")) 564 }, 565 }, 566 { 567 Name: "tree", 568 Usage: "Tree prints the dependencies of this project as a tree.", 569 Description: `This scans a project's source files and builds a tree 570 representation of the import graph. 571 572 It ignores testdata/ and directories that begin with . or _. Packages in 573 vendor/ are only included if they are referenced by the main project or 574 one of its dependencies.`, 575 Action: func(c *cli.Context) { 576 action.Tree(".", false) 577 }, 578 }, 579 { 580 Name: "list", 581 Usage: "List prints all dependencies that the present code references.", 582 Description: `List scans your code and lists all of the packages that are used. 583 584 It does not use the glide.yaml. Instead, it inspects the code to determine what packages are 585 imported. 586 587 Directories that begin with . or _ are ignored, as are testdata directories. Packages in 588 vendor are only included if they are used by the project.`, 589 Action: func(c *cli.Context) { 590 action.List(".", true, c.String("output")) 591 }, 592 Flags: []cli.Flag{ 593 cli.StringFlag{ 594 Name: "output, o", 595 Usage: "Output format. One of: json|json-pretty|text", 596 Value: "text", 597 }, 598 }, 599 }, 600 { 601 Name: "info", 602 Usage: "Info prints information about this project", 603 Flags: []cli.Flag{ 604 cli.StringFlag{ 605 Name: "format, f", 606 Usage: `Format of the information wanted (required).`, 607 }, 608 }, 609 Description: `A format containing the text with replacement variables 610 has to be passed in. Those variables are: 611 612 %n - name 613 %d - description 614 %h - homepage 615 %l - license 616 617 For example, given a project with the following glide.yaml: 618 619 package: foo 620 homepage: https://example.com 621 license: MIT 622 description: Some example description 623 624 Then running the following commands: 625 626 glide info -f %n 627 prints 'foo' 628 629 glide info -f "License: %l" 630 prints 'License: MIT' 631 632 glide info -f "%n - %d - %h - %l" 633 prints 'foo - Some example description - https://example.com - MIT'`, 634 Action: func(c *cli.Context) { 635 if c.IsSet("format") { 636 action.Info(c.String("format")) 637 } else { 638 cli.ShowCommandHelp(c, c.Command.Name) 639 } 640 }, 641 }, 642 { 643 Name: "about", 644 Usage: "Learn about Glide", 645 Action: func(c *cli.Context) { 646 action.About() 647 }, 648 }, 649 } 650 } 651 652 func defaultGlideDir() string { 653 c, err := user.Current() 654 if err != nil { 655 return "" 656 } 657 return filepath.Join(c.HomeDir, ".glide") 658 } 659 660 // startup sets up the base environment. 661 // 662 // It does not assume the presence of a Glide.yaml file or vendor/ directory, 663 // so it can be used by any Glide command. 664 func startup(c *cli.Context) error { 665 action.Debug(c.Bool("debug")) 666 action.NoColor(c.Bool("no-color")) 667 action.Quiet(c.Bool("quiet")) 668 action.Init(c.String("yaml"), c.String("home")) 669 action.EnsureGoVendor() 670 return nil 671 } 672 673 // Get the path to the glide.yaml file. 674 // 675 // This returns the name of the path, even if the file does not exist. The value 676 // may be set by the user, or it may be the default. 677 func glidefile(c *cli.Context) string { 678 path := c.String("file") 679 if path == "" { 680 // For now, we construct a basic assumption. In the future, we could 681 // traverse backward to see if a glide.yaml exists in a parent. 682 path = "./glide.yaml" 683 } 684 a, err := filepath.Abs(path) 685 if err != nil { 686 // Underlying fs didn't provide working dir. 687 return path 688 } 689 return a 690 }