Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/sailpoint-oss/sailpoint-cli/cmd/set"
"github.com/sailpoint-oss/sailpoint-cli/cmd/spconfig"
"github.com/sailpoint-oss/sailpoint-cli/cmd/transform"
"github.com/sailpoint-oss/sailpoint-cli/cmd/ui_plugins"
"github.com/sailpoint-oss/sailpoint-cli/cmd/va"
"github.com/sailpoint-oss/sailpoint-cli/cmd/workflow"
"github.com/sailpoint-oss/sailpoint-cli/internal/terminal"
Expand Down Expand Up @@ -69,6 +70,7 @@ func NewRootCommand() *cobra.Command {
workflow.NewWorkflowCommand(),
sanitize.NewSanitizeCommand(),
reassign.NewReassignCommand(),
ui_plugins.NewUIPluginsCommand(),
)

root.PersistentFlags().StringVarP(&env, "env", "", "", "Environment to use for SailPoint CLI commands")
Expand Down
2 changes: 1 addition & 1 deletion cmd/root/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// Expected number of subcommands to `sail` root command
const (
numRootSubcommands = 16
numRootSubcommands = 17
)

func TestNewRootCmd_noArgs(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions cmd/ui_plugins/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newCreateCommand() *cobra.Command {
return &cobra.Command{
Use: "create",
Short: "Create a UI plugin instance in the current tenant",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins create` is not implemented yet")
},
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newDeleteCommand() *cobra.Command {
return &cobra.Command{
Use: "delete",
Short: "Delete a UI plugin instance",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins delete` is not implemented yet")
},
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newInitCommand() *cobra.Command {
return &cobra.Command{
Use: "init",
Short: "Initialize a UI plugin workspace",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins init` is not implemented yet")
},
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/link.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newLinkCommand() *cobra.Command {
return &cobra.Command{
Use: "link",
Short: "Link local development URL for a UI plugin",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins link` is not implemented yet")
},
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newListCommand() *cobra.Command {
return &cobra.Command{
Use: "list",
Short: "List UI plugin instances in the current tenant",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins list` is not implemented yet")
},
}
}
59 changes: 59 additions & 0 deletions cmd/ui_plugins/ui_plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
_ "embed"
"fmt"
"os"

"github.com/sailpoint-oss/sailpoint-cli/internal/util"
"github.com/spf13/cobra"
)

const experimentalUIPluginsEnvVar = "SAIL_EXPERIMENTAL_UI_PLUGINS"

//go:embed ui_plugins.md
var uiPluginsHelp string

func NewUIPluginsCommand() *cobra.Command {
help := util.ParseHelp(uiPluginsHelp)
cmd := &cobra.Command{
Use: "ui-plugins",
Short: "Manage UI plugin workflows in Identity Security Cloud",
Long: help.Long,
Example: help.Example,
Hidden: true,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if !isUIPluginsEnabled() {
return experimentalDisabledError()
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

cmd.AddCommand(
newInitCommand(),
newCreateCommand(),
newLinkCommand(),
newUpdateCommand(),
newUploadCommand(),
newListCommand(),
newDeleteCommand(),
)

return cmd
}

func isUIPluginsEnabled() bool {
return os.Getenv(experimentalUIPluginsEnvVar) == "1"
}

func experimentalDisabledError() error {
return fmt.Errorf(
"the `sail ui-plugins` command group is experimental and currently disabled. Enable it with `%s=1`",
experimentalUIPluginsEnvVar,
)
}
27 changes: 27 additions & 0 deletions cmd/ui_plugins/ui_plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
==Long==
# UI Plugins
Manage UI Plugin workflows in Identity Security Cloud.

This command group is currently experimental and hidden from default command discovery.
Enable it for development with:

```bash
export SAIL_EXPERIMENTAL_UI_PLUGINS=1
```

Planned workflow commands:
- init
- create
- link
- update
- upload
- list
- delete
====

==Example==
```bash
sail ui-plugins
SAIL_EXPERIMENTAL_UI_PLUGINS=1 sail ui-plugins init
```
====
64 changes: 64 additions & 0 deletions cmd/ui_plugins/ui_plugins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"strings"
"testing"
)

func TestNewUIPluginsCommandStructure(t *testing.T) {
cmd := NewUIPluginsCommand()

if cmd.Use != "ui-plugins" {
t.Fatalf("expected use to be ui-plugins, got %s", cmd.Use)
}

if !cmd.Hidden {
t.Fatal("expected ui-plugins command to be hidden")
}

if len(cmd.Commands()) != 7 {
t.Fatalf("expected 7 subcommands, got %d", len(cmd.Commands()))
}
}

func TestUIPluginsGateDisabled(t *testing.T) {
t.Setenv(experimentalUIPluginsEnvVar, "")
cmd := NewUIPluginsCommand()
cmd.SetArgs([]string{})

err := cmd.Execute()
if err == nil {
t.Fatal("expected command to fail when experimental gate is disabled")
}

errText := err.Error()
if !strings.Contains(errText, "experimental") || !strings.Contains(errText, experimentalUIPluginsEnvVar) {
t.Fatalf("expected error to mention experimental gate and env var, got: %s", errText)
}
}

func TestUIPluginsGateEnabled(t *testing.T) {
t.Setenv(experimentalUIPluginsEnvVar, "1")
cmd := NewUIPluginsCommand()
cmd.SetArgs([]string{})

if err := cmd.Execute(); err != nil {
t.Fatalf("expected command to run when gate is enabled, got: %v", err)
}
}

func TestUIPluginsStubReachableWhenEnabled(t *testing.T) {
t.Setenv(experimentalUIPluginsEnvVar, "1")
cmd := NewUIPluginsCommand()
cmd.SetArgs([]string{"init"})

err := cmd.Execute()
if err == nil {
t.Fatal("expected init stub command to return not implemented error")
}

if !strings.Contains(err.Error(), "not implemented yet") {
t.Fatalf("expected not implemented error, got: %s", err.Error())
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newUpdateCommand() *cobra.Command {
return &cobra.Command{
Use: "update",
Short: "Update a UI plugin manifest configuration",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins update` is not implemented yet")
},
}
}
18 changes: 18 additions & 0 deletions cmd/ui_plugins/upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2026, SailPoint Technologies, Inc. All rights reserved.
package ui_plugins

import (
"fmt"

"github.com/spf13/cobra"
)

func newUploadCommand() *cobra.Command {
return &cobra.Command{
Use: "upload",
Short: "Upload compiled UI plugin assets",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("`sail ui-plugins upload` is not implemented yet")
},
}
}
Loading