diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3979801675d..40eee4603e3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,11 +9,6 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 -- package-ecosystem: gomod - directory: "eng/_core" - schedule: - interval: daily - open-pull-requests-limit: 10 - package-ecosystem: gomod directory: "eng/_util" schedule: diff --git a/eng/_core/README.md b/eng/_core/README.md deleted file mode 100644 index 00e5a681990..00000000000 --- a/eng/_core/README.md +++ /dev/null @@ -1,30 +0,0 @@ -## `github.com/microsoft/go/_core` - -This module is a set of utilities Microsoft uses to build Go in Azure DevOps and -maintain this repository. Run `eng/run.ps1 build -h` to list available build -options, or `eng/run.ps1` to list all commands in this module. - -Unlike `_util`, the `_core` module should have zero external dependencies and -only requires a stage 0 Go toolset to build. The commands in this module are -used to produce the signed Microsoft binaries. - -### Support for gotestsum wrapping -The `_util` module implements a gotestsum wrapper around `_core`'s `build` -command. This requires some features in `_core` that accommodate gotestsum but -don't make sense as standalone features a dev would use. For example, JSON test -output and stderr redirection to stdout. - -The high-level execution flow looks roughly like this when running in CI: - -* `eng/pipeline/jobs/run-stage.yml` - runs: -* `eng/run.ps1 run-builder -test -builder linux-amd64-test -junitfile [...]` - which runs the Go function: -* `gotestsum.Run(... eng/run.ps1 build -test -json ...)` - which runs and captures the output of: -* `eng/run.ps1 build -test -json` - which runs [`cmd/build/build.go`](cmd/build/build.go) in this module. - -This is not currently used in our CI because this process seems to cut off -some test output: -[microsoft/go#1114](https://github.com/microsoft/go/issues/1114). diff --git a/eng/_core/go.mod b/eng/_core/go.mod deleted file mode 100644 index d1de3bc0886..00000000000 --- a/eng/_core/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -module github.com/microsoft/go/_core - -go 1.21 diff --git a/eng/_core/patch/patch.go b/eng/_core/patch/patch.go deleted file mode 100644 index 88e25d8e4db..00000000000 --- a/eng/_core/patch/patch.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package patch - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" -) - -type ApplyMode int - -const ( - // ApplyModeCommits applies patches as commits. This is useful for developing changes to the - // patches, because the commits can be automatically extracted back into patch files. - ApplyModeCommits ApplyMode = iota - // ApplyModeIndex applies patches as changes to the Git index and working tree. This means - // further changes to the Go source code will show up as unstaged changes, so if any intentional - // changes are performed in this state, they can be differentiated from the patch changes. - ApplyModeIndex -) - -// Apply runs a Git command to apply the patches in the repository onto the submodule. The exact Git -// command used ("am" or "apply") depends on the patch mode. -func Apply(rootDir string, mode ApplyMode) error { - goDir := filepath.Join(rootDir, "go") - patchDir := filepath.Join(rootDir, "patches") - - cmd := exec.Command("git") - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Dir = goDir - - switch mode { - case ApplyModeCommits: - cmd.Args = append(cmd.Args, "am") - case ApplyModeIndex: - cmd.Args = append(cmd.Args, "apply", "--index") - default: - return fmt.Errorf("invalid patch mode '%v'", mode) - } - - // Trailing whitespace may be present in the patch files. Don't emit warnings for it here. These - // warnings should be avoided when authoring each patch file. If we made it to this point, it's - // too late to cause noisy warnings because of them. - cmd.Args = append(cmd.Args, "--whitespace=nowarn") - - // ReadDir returns alphabetical order for patches: we depend on it for the patch apply order. - entries, err := os.ReadDir(patchDir) - if err != nil { - return err - } - - for _, entry := range entries { - if entry.IsDir() { - continue - } - if filepath.Ext(entry.Name()) != ".patch" { - continue - } - cmd.Args = append(cmd.Args, filepath.Join(patchDir, entry.Name())) - } - - if err := runCmd(cmd); err != nil { - return err - } - return nil -} - -func runCmd(cmd *exec.Cmd) error { - fmt.Printf("---- Running command: %v\n", cmd.Args) - return cmd.Run() -} diff --git a/eng/_core/submodule/submodule.go b/eng/_core/submodule/submodule.go deleted file mode 100644 index 6fb4d54bb03..00000000000 --- a/eng/_core/submodule/submodule.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package submodule - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" -) - -// Init initializes and updates the submodule, but does not clean it. This func offers more options -// for initialization than Reset. If origin is defined, fetch the submodule from there instead of -// the default defined in '.gitmodules'. If fetchBearerToken is nonempty, use it as a bearer token -// during the fetch. If shallow is true, clone the submodule with depth 1. -func Init(rootDir, origin, fetchBearerToken string, shallow bool) error { - // Update the submodule commit, and initialize if it hasn't been done already. - command := []string{"git"} - if origin != "" { - command = append(command, "-c", "submodule.go.url="+origin) - } - if fetchBearerToken != "" { - command = append(command, "-c", "http.extraheader=AUTHORIZATION: bearer "+fetchBearerToken) - } - command = append(command, "submodule", "update", "--init") - if shallow { - command = append(command, "--depth", "1") - } - - if err := run(rootDir, command...); err != nil { - return err - } - return nil -} - -// Reset updates the submodule (with '--init'), resets all changes, and cleans all untracked files. -func Reset(rootDir string) error { - goDir := filepath.Join(rootDir, "go") - - // Update the submodule commit, and initialize if it hasn't been done already. - if err := run(rootDir, "git", "submodule", "update", "--init"); err != nil { - return err - } - - // Find toplevel directories (Git working tree roots) for the outer repo and what we expect to - // be the Go submodule. If the toplevel directory is the same for both, make sure not to clean! - // The submodule likely wasn't set up properly, and cleaning could result in unexpectedly losing - // work in the outer repo when the command spills over. - rootToplevel, err := getToplevel(rootDir) - if err != nil { - return err - } - goToplevel, err := getToplevel(goDir) - if err != nil { - return err - } - - if rootToplevel == goToplevel { - return fmt.Errorf("go submodule (%v) toplevel is the same as root (%v) toplevel: %v", goDir, rootDir, goToplevel) - } - - // Reset the index and working directory. This doesn't clean up new untracked files. - if err := run(goDir, "git", "reset", "--hard"); err != nil { - return err - } - // Delete untracked files detected by Git. Deliberately leave files that are ignored in - // '.gitignore': these files shouldn't interfere with the build process and could be used for - // incremental builds. - if err := run(goDir, "git", "clean", "-df"); err != nil { - return err - } - return nil -} - -func getToplevel(dir string) (string, error) { - c := exec.Command("git", "rev-parse", "--show-toplevel") - c.Dir = dir - out, err := c.CombinedOutput() - if err != nil { - return "", err - } - return string(out), nil -} - -func run(dir string, args ...string) error { - c := exec.Command(args[0], args[1:]...) - c.Stdout = os.Stdout - c.Stderr = os.Stderr - c.Dir = dir - return runCmd(c) -} - -func runCmd(cmd *exec.Cmd) error { - fmt.Printf("---- Running command: %v\n", cmd.Args) - return cmd.Run() -} diff --git a/eng/_util/README.md b/eng/_util/README.md index 870e9b96e02..6bd3f0c301e 100644 --- a/eng/_util/README.md +++ b/eng/_util/README.md @@ -1,9 +1,40 @@ ## `github.com/microsoft/go/_util` This module is a set of utilities Microsoft uses to build Go in Azure DevOps and -maintain this repository. Run `eng/run.ps1` to list the available commands and -see instructions on how to use them. +maintain this repository. Run `eng/run.ps1 build -h` to list available build +options, or `eng/run.ps1` to list all commands in this module. -The `_util` module requires the `gotestsum` library and doesn't vendor it. -`_util` is not strictly necessary to build Go, so it's ok if its dependencies -are downloaded when needed. CI avoids uses the `_util` module when possible. +### Minimal dependencies +Some commands in this module use minimal external dependencies. This reduces the +dependencies used to produce the signed Microsoft binaries. + +Commands that use more than the minimal external dependencies will panic upon +init if `MS_GO_UTIL_ALLOW_ONLY_MINIMAL_DEPS` is set to `1`. This makes it +possible to test our pipelines to make sure they only use the expected commands. + +The minimal dependencies are themselves tested by +`TestMinimalCommandDependencies` in `testutil`. It uses `go list` to ensure that +all commands that use more than the minimal set of dependencies include the +conditional panic upon init. + +### Support for gotestsum wrapping +The `run-builder` command implements a gotestsum wrapper around the `build` +command. This isn't implemented in `build` itself to keep dependencies for the +signed build low. There are some features in the build command that accommodate +gotestsum but don't make sense as standalone features a dev would use. For +example, JSON test output and stderr redirection to stdout. + +The high-level execution flow looks roughly like this when running in CI: + +* `eng/pipeline/jobs/run-stage.yml` + runs: +* `eng/run.ps1 run-builder -test -builder linux-amd64-test -junitfile [...]` + which runs the Go function: +* `gotestsum.Run(... eng/run.ps1 build -test -json ...)` + which runs and captures the output of: +* `eng/run.ps1 build -test -json` + which runs [`cmd/build/build.go`](cmd/build/build.go) in this module. + +> [!NOTE] +> This support is not currently used in our CI because this process seems to cut off some test output: +> [microsoft/go#1114](https://github.com/microsoft/go/issues/1114). diff --git a/eng/_core/buildutil/buildutil.go b/eng/_util/buildutil/buildutil.go similarity index 100% rename from eng/_core/buildutil/buildutil.go rename to eng/_util/buildutil/buildutil.go diff --git a/eng/_core/cmd/build/build.go b/eng/_util/cmd/build/build.go similarity index 97% rename from eng/_core/cmd/build/build.go rename to eng/_util/cmd/build/build.go index a316f6dd04d..7af05339872 100644 --- a/eng/_core/cmd/build/build.go +++ b/eng/_util/cmd/build/build.go @@ -15,9 +15,9 @@ import ( "runtime" "strings" - "github.com/microsoft/go/_core/buildutil" - "github.com/microsoft/go/_core/patch" - "github.com/microsoft/go/_core/submodule" + "github.com/microsoft/go-infra/patch" + "github.com/microsoft/go-infra/submodule" + "github.com/microsoft/go/_util/buildutil" ) const description = ` @@ -112,10 +112,14 @@ func build(o *options) error { } if o.Refresh { - if err := submodule.Reset(rootDir); err != nil { + config, err := patch.FindAncestorConfig(rootDir) + if err != nil { return err } - if err := patch.Apply(rootDir, patch.ApplyModeIndex); err != nil { + if err := submodule.Reset(rootDir, filepath.Join(config.RootDir, config.SubmoduleDir), true); err != nil { + return err + } + if err := patch.Apply(config, patch.ApplyModeIndex); err != nil { return err } } @@ -224,7 +228,7 @@ func build(o *options) error { // For example, if we're running in CI, gotestsum may be capturing our output to report in a // JUnit file. If gotestsum detects output in stderr, it prints it in an error message. This // error message stands out, and could mislead someone trying to diagnose a failed test run. - // Redirecting all stderr output avoids this scenario. (See /eng/_core/README.md for more + // Redirecting all stderr output avoids this scenario. (See /eng/_util/README.md for more // info on why we may be wrapped by gotestsum.) // // An example of benign stderr output is when the tests check for machine capabilities. A diff --git a/eng/_core/cmd/cmdscan/cmdscan.go b/eng/_util/cmd/cmdscan/cmdscan.go similarity index 100% rename from eng/_core/cmd/cmdscan/cmdscan.go rename to eng/_util/cmd/cmdscan/cmdscan.go diff --git a/eng/_util/cmd/createbuildassetjson/nonminimaldeps.go b/eng/_util/cmd/createbuildassetjson/nonminimaldeps.go new file mode 100644 index 00000000000..dd9d230013f --- /dev/null +++ b/eng/_util/cmd/createbuildassetjson/nonminimaldeps.go @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This command uses non-minimal dependencies, so ensure it can't be used while in minimal mode. + +import _ "github.com/microsoft/go/_util/internal/depsinitpanic" diff --git a/eng/_util/cmd/run-builder/nonminimaldeps.go b/eng/_util/cmd/run-builder/nonminimaldeps.go new file mode 100644 index 00000000000..dd9d230013f --- /dev/null +++ b/eng/_util/cmd/run-builder/nonminimaldeps.go @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This command uses non-minimal dependencies, so ensure it can't be used while in minimal mode. + +import _ "github.com/microsoft/go/_util/internal/depsinitpanic" diff --git a/eng/_util/cmd/run-builder/run-builder.go b/eng/_util/cmd/run-builder/run-builder.go index b3187737242..a256693ae5d 100644 --- a/eng/_util/cmd/run-builder/run-builder.go +++ b/eng/_util/cmd/run-builder/run-builder.go @@ -13,7 +13,7 @@ import ( "strconv" "strings" - "github.com/microsoft/go/_core/buildutil" + "github.com/microsoft/go/_util/buildutil" gotestsumcmd "gotest.tools/gotestsum/cmd" ) diff --git a/eng/_util/cmd/selftest/selftest.go b/eng/_util/cmd/selftest/selftest.go new file mode 100644 index 00000000000..dad8a235c4b --- /dev/null +++ b/eng/_util/cmd/selftest/selftest.go @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/microsoft/go-infra/executil" +) + +const description = ` +This command runs the _util self-tests using the stage 0 Go toolchain. +` + +func main() { + var help = flag.Bool("h", false, "Print this help message.") + + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage of selftest:\n") + flag.PrintDefaults() + fmt.Fprintf(flag.CommandLine.Output(), "%s\n", description) + } + + flag.Parse() + if *help { + flag.Usage() + return + } + + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +} + +func run() error { + stage0Goroot := os.Getenv("STAGE_0_GOROOT") + if stage0Goroot == "" { + return fmt.Errorf("STAGE_0_GOROOT not set") + } + + return executil.Run(executil.Dir( + filepath.Join("eng", "_util"), + filepath.Join(stage0Goroot, "bin", "go"), + "test", "./...", + )) +} diff --git a/eng/_core/cmd/submodule-refresh/submodule-refresh.go b/eng/_util/cmd/submodule-refresh/submodule-refresh.go similarity index 82% rename from eng/_core/cmd/submodule-refresh/submodule-refresh.go rename to eng/_util/cmd/submodule-refresh/submodule-refresh.go index 9566fb11511..e7902442346 100644 --- a/eng/_core/cmd/submodule-refresh/submodule-refresh.go +++ b/eng/_util/cmd/submodule-refresh/submodule-refresh.go @@ -8,9 +8,10 @@ import ( "flag" "fmt" "os" + "path/filepath" - "github.com/microsoft/go/_core/patch" - "github.com/microsoft/go/_core/submodule" + "github.com/microsoft/go-infra/patch" + "github.com/microsoft/go-infra/submodule" ) const description = ` @@ -54,7 +55,12 @@ func refresh(rootDir string) error { return err } - if err := submodule.Reset(rootDir); err != nil { + config, err := patch.FindAncestorConfig(rootDir) + if err != nil { + return err + } + + if err := submodule.Reset(rootDir, filepath.Join(config.RootDir, config.SubmoduleDir), true); err != nil { return err } @@ -67,7 +73,7 @@ func refresh(rootDir string) error { mode = patch.ApplyModeCommits } - if err := patch.Apply(rootDir, mode); err != nil { + if err := patch.Apply(config, mode); err != nil { return err } return nil diff --git a/eng/_util/cmd/updatelinktable/updatelinktable.go b/eng/_util/cmd/updatelinktable/updatelinktable.go index d6182800cb2..3dd0820f029 100644 --- a/eng/_util/cmd/updatelinktable/updatelinktable.go +++ b/eng/_util/cmd/updatelinktable/updatelinktable.go @@ -14,7 +14,7 @@ import ( "sort" "strings" - "github.com/microsoft/go/_core/supportdata" + "github.com/microsoft/go/_util/supportdata" ) var description = ` diff --git a/eng/_core/cmd/write-checksum/write-checksum.go b/eng/_util/cmd/write-checksum/write-checksum.go similarity index 100% rename from eng/_core/cmd/write-checksum/write-checksum.go rename to eng/_util/cmd/write-checksum/write-checksum.go diff --git a/eng/_util/go.mod b/eng/_util/go.mod index 9fc9a55030f..a82c44f6020 100644 --- a/eng/_util/go.mod +++ b/eng/_util/go.mod @@ -8,7 +8,6 @@ go 1.21 require ( github.com/microsoft/go-infra v0.0.3 - github.com/microsoft/go/_core v0.0.0 golang.org/x/sys v0.22.0 gotest.tools/gotestsum v1.12.0 ) @@ -27,5 +26,3 @@ require ( golang.org/x/term v0.20.0 // indirect golang.org/x/text v0.16.0 // indirect ) - -replace github.com/microsoft/go/_core => ../_core diff --git a/eng/_util/internal/depsinitpanic/depsinitpanic.go b/eng/_util/internal/depsinitpanic/depsinitpanic.go new file mode 100644 index 00000000000..7414968015e --- /dev/null +++ b/eng/_util/internal/depsinitpanic/depsinitpanic.go @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package depsinitpanic + +import "os" + +func init() { + const v = "MS_GO_UTIL_ALLOW_ONLY_MINIMAL_DEPS" + if os.Getenv(v) == "1" { + panic("This command may use more than minimal deps and can't be used while " + v + " is 1") + } +} diff --git a/eng/_core/supportdata/supportdata.go b/eng/_util/supportdata/supportdata.go similarity index 100% rename from eng/_core/supportdata/supportdata.go rename to eng/_util/supportdata/supportdata.go diff --git a/eng/_util/testutil/dependency_test.go b/eng/_util/testutil/dependency_test.go new file mode 100644 index 00000000000..d989b1ffc4d --- /dev/null +++ b/eng/_util/testutil/dependency_test.go @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testutil + +import ( + "errors" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +const nonMinimalDepsFilename = "nonminimaldeps.go" + +func TestMinimalCommandDependencies(t *testing.T) { + cmdList := combinedOutput(t, exec.Command("go", "list", "../cmd/...")) + cmdPackages := strings.Fields(cmdList) + if len(cmdPackages) == 0 { + t.Fatalf("no commands found") + } + + stdPackages := combinedOutput(t, exec.Command("go", "list", "std")) + stdPackageMap := make(map[string]struct{}) + for _, stdPackage := range strings.Fields(stdPackages) { + stdPackageMap[stdPackage] = struct{}{} + } + + for _, cmdPackage := range cmdPackages { + cmdPackage := cmdPackage + localPackage := strings.TrimPrefix(cmdPackage, "github.com/microsoft/go/_util/") + + t.Run(localPackage, func(t *testing.T) { + t.Parallel() + + if _, err := os.Stat(filepath.Join("..", localPackage, nonMinimalDepsFilename)); err != nil { + if errors.Is(err, os.ErrNotExist) { + // This package should have minimal deps. We need to check. + } else { + t.Fatalf("Failed to check whether to expect minimal deps for %q: %v", cmdPackage, err) + } + } else { + t.Logf("Skipping scan of %q (known to have non-minimal deps)", cmdPackage) + return + } + + depsString := combinedOutput(t, exec.Command("go", "list", "-f", `{{ join .Deps " " }}`, cmdPackage)) + + for _, dep := range strings.Fields(depsString) { + // Anything in the standard library is ok. Note: this uses the running version of + // Go, so introducing super new dependencies might seem to succeed locally with a + // new Go but fail in CI. + if _, ok := stdPackageMap[dep]; ok { + continue + } + // Allow some packages even in minimal mode. + if strings.HasPrefix(dep, "github.com/microsoft/go/_util/") || + strings.HasPrefix(dep, "github.com/microsoft/go-infra/") || + strings.HasPrefix(dep, "golang.org/x/") { + + continue + } + t.Errorf("error: depends on %q", dep) + } + }) + } +} + +func combinedOutput(t *testing.T, c *exec.Cmd) string { + out, err := c.CombinedOutput() + if err != nil { + t.Fatalf("error running %v: %v, output:\n%s", c, err, out) + } + return string(out) +} diff --git a/eng/pipeline/stages/run-stage.yml b/eng/pipeline/stages/run-stage.yml index 7f54fcc2d33..d79dcbd6b52 100644 --- a/eng/pipeline/stages/run-stage.yml +++ b/eng/pipeline/stages/run-stage.yml @@ -79,6 +79,12 @@ stages: - name: createPDB value: ${{ and(eq(parameters.createSymbols, true), eq(parameters.builder.config, 'buildandpack'), eq(parameters.builder.os, 'windows')) }} # Only create PDBs on Windows + - ${{ if eq(parameters.builder.config, 'buildandpack') }}: + # Make sure a PR doesn't accidentally add a command that uses more than minimal + # dependencies to the buildandpack pipeline. + - name: MS_GO_UTIL_ALLOW_ONLY_MINIMAL_DEPS + value: '1' + - ${{ if eq(parameters.builder.config, 'codeql_inner') }}: # Manually specify the repository being scanned by this job because # CodeQL can't detect the inner repository (the submodule) @@ -140,6 +146,12 @@ stages: # Set Git authorship info for 'cmd/go TestScript/build_buildvcs_auto'. - template: ../steps/set-bot-git-author-task.yml + # Run the _util tests during the devscript configuration. + # It generally shouldn't matter which config these run in, we just need to pick something. + - ${{ if eq(parameters.builder.config, 'devscript') }}: + - pwsh: eng/run.ps1 selftest + displayName: Run _util self-tests + - pwsh: | # Apply the patches as staged changes, so the HEAD commit is the same as upstream. eng/run.ps1 submodule-refresh diff --git a/eng/run.ps1 b/eng/run.ps1 index a12878de720..07d3c9fa7dd 100644 --- a/eng/run.ps1 +++ b/eng/run.ps1 @@ -4,7 +4,7 @@ <# .DESCRIPTION -This script builds and runs a tool defined in a module in 'eng'. +This script builds and runs a tool defined in 'eng/_util'. To run a tool: run.ps1 [arguments...] @@ -15,9 +15,9 @@ For example, to build the repository: To list all possible tools: run.ps1 -Builds 'eng//cmd//.go' and runs it using the list of +Builds 'eng/_util/cmd//.go' and runs it using the list of arguments. If necessary, this command automatically installs Go and downloads -the dependencies of the module. +the dependencies of the tool. Every tool accepts a '-h' argument to show tool usage help. #> @@ -39,24 +39,23 @@ if ($LASTEXITCODE) { function Write-ToolList() { Write-Host "Possible tools:" - foreach ($module in Get-ChildItem (Join-Path $PSScriptRoot "_*")) { - Write-Host " Module $($module.Name):" - foreach ($tool in Get-ChildItem (Join-Path $module "cmd" "*")) { - Write-Host " $($tool.Name)" - } + foreach ($tool in Get-ChildItem (Join-Path $PSScriptRoot "_util" "cmd" "*")) { + Write-Host " $($tool.Name)" } Write-Host "" } if (-not $tool) { Write-Host "No tool specified. Showing help and listing available tools:" - (Get-Help $PSCommandPath).DESCRIPTION | Out-String | Write-Host + Write-Host "" + ((Get-Help $PSCommandPath).DESCRIPTION | Out-String).Trim() | Write-Host + Write-Host "" Write-ToolList exit 0 } # Find tool script file based on the name given. -$tool_search = Join-Path $PSScriptRoot "_*" "cmd" "$tool" "$tool.go" +$tool_search = Join-Path $PSScriptRoot "_util" "cmd" "$tool" "$tool.go" # Find matches, and force the result to be an array. $tool_matches = @(Get-Item $tool_search) @@ -111,7 +110,7 @@ try { } } - Write-Host "Building done." + Write-Host "Built '$tool'. Running from repo root..." } finally { Pop-Location }