Skip to content

Commit ad38f11

Browse files
committed
fix(wfctl): accept trailing plugin install flags
1 parent 0339c2d commit ad38f11

2 files changed

Lines changed: 63 additions & 7 deletions

File tree

cmd/wfctl/plugin_install.go

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func runPluginInstall(args []string) error {
8787
fmt.Fprintf(fs.Output(), "Usage: wfctl plugin install [options] [<name>[@<version>]]\n\nInstall a plugin from the registry, a URL, a local directory, or from the lockfile.\n\n wfctl plugin install <name> Install latest from registry\n wfctl plugin install <name>@v1.0.0 Install specific version\n wfctl plugin install --url <url> Install from a direct download URL\n wfctl plugin install --local <dir> Install from a local build directory\n wfctl plugin install --from-config <f> Install all requires.plugins[] from workflow config\n wfctl plugin install Install all plugins from .wfctl-lock.yaml\n\nOptions:\n")
8888
fs.PrintDefaults()
8989
}
90-
if err := fs.Parse(args); err != nil {
90+
if err := fs.Parse(interspersedPluginInstallArgs(fs, args)); err != nil {
9191
return err
9292
}
9393
// Validate flag combinations before doing anything else.
@@ -251,6 +251,46 @@ func runPluginInstall(args []string) error {
251251
return nil
252252
}
253253

254+
type boolFlag interface {
255+
IsBoolFlag() bool
256+
}
257+
258+
func interspersedPluginInstallArgs(fs *flag.FlagSet, args []string) []string {
259+
if len(args) == 0 {
260+
return args
261+
}
262+
flags := make([]string, 0, len(args))
263+
positionals := make([]string, 0, len(args))
264+
for i := 0; i < len(args); i++ {
265+
arg := args[i]
266+
if arg == "--" {
267+
positionals = append(positionals, args[i:]...)
268+
break
269+
}
270+
if !strings.HasPrefix(arg, "-") || arg == "-" {
271+
positionals = append(positionals, arg)
272+
continue
273+
}
274+
flags = append(flags, arg)
275+
name := strings.TrimLeft(arg, "-")
276+
if idx := strings.IndexByte(name, '='); idx >= 0 {
277+
name = name[:idx]
278+
}
279+
f := fs.Lookup(name)
280+
if f == nil || strings.Contains(arg, "=") {
281+
continue
282+
}
283+
if bf, ok := f.Value.(boolFlag); ok && bf.IsBoolFlag() {
284+
continue
285+
}
286+
if i+1 < len(args) {
287+
i++
288+
flags = append(flags, args[i])
289+
}
290+
}
291+
return append(flags, positionals...)
292+
}
293+
254294
// installPluginFromManifest downloads, extracts, and installs a plugin using the
255295
// provided registry manifest. It is shared by runPluginInstall and runPluginUpdate.
256296
// The plugin.json is always written/updated from the manifest to keep version tracking correct.
@@ -1431,10 +1471,10 @@ func preparePluginStagingDir(destDir string) (stagingDir string, cleanup func(),
14311471
// renamed to a trash location on the same filesystem. Only after the new
14321472
// directory is successfully renamed into place is the trash removed.
14331473
//
1434-
// 1. Rename destDir → destDir+".uninstalling" (no-op if destDir absent)
1435-
// 2. Rename stagingDir → destDir
1436-
// 3. On step-2 failure: restore destDir+".uninstalling" → destDir
1437-
// 4. On step-2 success: remove destDir+".uninstalling"
1474+
// 1. Rename destDir → destDir+".uninstalling" (no-op if destDir absent)
1475+
// 2. Rename stagingDir → destDir
1476+
// 3. On step-2 failure: restore destDir+".uninstalling" → destDir
1477+
// 4. On step-2 success: remove destDir+".uninstalling"
14381478
//
14391479
// On success stagingDir no longer exists on disk; the deferred cleanup
14401480
// returned by preparePluginStagingDir becomes a harmless no-op.

cmd/wfctl/plugin_install_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,6 @@ func sha256sum(data []byte) string {
749749
return hex.EncodeToString(h[:])
750750
}
751751

752-
753-
754752
func TestVerifyChecksum_MismatchFormat(t *testing.T) {
755753
err := verifyChecksum([]byte("data"), strings.Repeat("0", 64))
756754
if err == nil {
@@ -976,6 +974,24 @@ func TestRunPluginInstallCompatSkipsNewerKnownFail(t *testing.T) {
976974
}
977975
}
978976

977+
func TestRunPluginInstallHonorsTrailingPluginDirFlag(t *testing.T) {
978+
reg := newCompatInstallRegistry(t, "test", "v0.2.0", []compatInstallVersion{
979+
{Version: "v0.2.0", Status: PluginCompatibilityStatusPass},
980+
})
981+
pluginDir := t.TempDir()
982+
if err := runPluginInstall([]string{
983+
"test",
984+
"--config", reg.ConfigPath,
985+
"--plugin-dir", pluginDir,
986+
"--engine-version", "v0.51.2",
987+
}); err != nil {
988+
t.Fatalf("runPluginInstall: %v", err)
989+
}
990+
if got := readInstalledVersion(filepath.Join(pluginDir, "test")); got != "v0.2.0" {
991+
t.Fatalf("installed version in trailing --plugin-dir = %q, want v0.2.0", got)
992+
}
993+
}
994+
979995
func TestRunPluginInstallCompatRequestedFailErrorsAndWarnPermits(t *testing.T) {
980996
reg := newCompatInstallRegistry(t, "test", "v0.2.0", []compatInstallVersion{
981997
{Version: "v0.2.0", Status: PluginCompatibilityStatusFail},

0 commit comments

Comments
 (0)