diff --git a/changelog/pending/20221013--cli-package--get-schema-handle-dirs.yaml b/changelog/pending/20221013--cli-package--get-schema-handle-dirs.yaml new file mode 100644 index 000000000000..4604ec890224 --- /dev/null +++ b/changelog/pending/20221013--cli-package--get-schema-handle-dirs.yaml @@ -0,0 +1,5 @@ +changes: + - type: fix + scope: cli/package + description: > + Require a path separator for path based binaries. This allows us to distinguish between ./myProvider (execute the binary at path) and myProvider (execute the installed plugin). diff --git a/pkg/cmd/pulumi/package.go b/pkg/cmd/pulumi/package.go index 354641bb432f..e229d3f5a72b 100644 --- a/pkg/cmd/pulumi/package.go +++ b/pkg/cmd/pulumi/package.go @@ -17,6 +17,7 @@ package main import ( "encoding/json" "fmt" + "io/fs" "os" "path/filepath" "strings" @@ -106,24 +107,41 @@ func schemaFromSchemaSource(packageSource string) (*schema.Package, error) { } version = &v } - info, err := os.Stat(pkg) - if os.IsNotExist(err) { - if strings.ContainsRune(pkg, filepath.Separator) { - // We infer that we were given a file path, so we assume that this is a file. - // The file was not found, so we exit. - return nil, err - } + + isExecutable := func(info fs.FileInfo) bool { + return info.Mode()&0111 != 0 && !info.IsDir() + } + + // No file separators, so we try to look up the schema + // On unix, these checks are identical. On windows, filepath.Separator is '\\' + if !strings.ContainsRune(pkg, filepath.Separator) && !strings.ContainsRune(pkg, '/') { host, err := plugin.NewDefaultHost(pCtx, nil, false, nil) if err != nil { return nil, err } // We assume this was a plugin and not a path, so load the plugin. - return schema.NewPluginLoader(host).LoadPackage(pkg, version) - } else if err != nil { - return nil, err + schema, err := schema.NewPluginLoader(host).LoadPackage(pkg, version) + if err != nil { + // There is an executable with the same name, so suggest that + if info, statErr := os.Stat(pkg); statErr == nil && isExecutable(info) { + return nil, fmt.Errorf("could not find installed plugin %s, did you mean ./%[1]s: %w", pkg, err) + } + } + return schema, err + } + // We were given a path to a binary, so invoke that. - if info.Mode()&0111 == 0 { + + info, err := os.Stat(pkg) + if os.IsNotExist(err) { + return nil, fmt.Errorf("could not find file %s", pkg) + } else if err != nil { + return nil, err + } else if !isExecutable(info) { + if p, err := filepath.Abs(pkg); err == nil { + pkg = p + } return nil, fmt.Errorf("plugin at path %q not executable", pkg) } diff --git a/pkg/cmd/pulumi/stack.go b/pkg/cmd/pulumi/stack.go index dcbd52992c91..f0cf21e8dd50 100644 --- a/pkg/cmd/pulumi/stack.go +++ b/pkg/cmd/pulumi/stack.go @@ -90,38 +90,38 @@ func newStackCmd() *cobra.Command { } if snap != nil { - if t := snap.Manifest.Time; t.IsZero() && startTime == "" { - fmt.Printf(" Last update time unknown\n") - } else if startTime == "" { - fmt.Printf(" Last updated: %s (%v)\n", humanize.Time(t), t) + t := snap.Manifest.Time.Local() + if startTime == "" { + // If a stack update is not in progress + if !t.IsZero() && t.Before(time.Now()) { + // If the update time is in the future, best to not display something incorrect based on + // inaccurate clocks. + fmt.Printf(" Last updated: %s (%v)\n", humanize.Time(t), t) + } } - var cliver string - if snap.Manifest.Version == "" { - cliver = "?" - } else { - cliver = snap.Manifest.Version + if snap.Manifest.Version != "" { + fmt.Printf(" Pulumi version used: %s\n", snap.Manifest.Version) } - fmt.Printf(" Pulumi version: %s\n", cliver) - for _, plugin := range snap.Manifest.Plugins { - var plugver string - if plugin.Version == nil { - plugver = "?" + for _, p := range snap.Manifest.Plugins { + var pluginVersion string + if p.Version == nil { + pluginVersion = "?" } else { - plugver = plugin.Version.String() + pluginVersion = p.Version.String() } - fmt.Printf(" Plugin %s [%s] version: %s\n", plugin.Name, plugin.Kind, plugver) + fmt.Printf(" Plugin %s [%s] version: %s\n", p.Name, p.Kind, pluginVersion) } } else { fmt.Printf(" No updates yet; run `pulumi up`\n") } // Now show the resources. - var rescnt int + var resourceCount int if snap != nil { - rescnt = len(snap.Resources) + resourceCount = len(snap.Resources) } - fmt.Printf("Current stack resources (%d):\n", rescnt) - if rescnt == 0 { + fmt.Printf("Current stack resources (%d):\n", resourceCount) + if resourceCount == 0 { fmt.Printf(" No resources currently in this stack\n") } else { rows, ok := renderTree(snap, showURNs, showIDs) @@ -197,15 +197,15 @@ func printStackOutputs(outputs map[string]interface{}) { if len(outputs) == 0 { fmt.Printf(" No output values currently in this stack\n") } else { - var outkeys []string - for outkey := range outputs { - outkeys = append(outkeys, outkey) + var outKeys []string + for v := range outputs { + outKeys = append(outKeys, v) } - sort.Strings(outkeys) + sort.Strings(outKeys) rows := []cmdutil.TableRow{} - for _, key := range outkeys { + for _, key := range outKeys { rows = append(rows, cmdutil.TableRow{Columns: []string{key, stringifyOutput(outputs[key])}}) }