Skip to content

Commit d5108bf

Browse files
authored
New hidden install-pipelines-cli command that creates pipelines symlink to databricks (#3009)
## Changes - **New file:** `pipelines.go` (package `pipelines`) - **New file:** `install_pipelines_cli.go` (package `pipelines`) - To `databricks`, implements a hidden `install-pipelines-cli` command that creates a symlink named `pipelines` pointing to the real `databricks` binary. - Handles symlink creation, permission checks, and error handling for existing files/symlinks. - **New folder:** `pipelines/install-pipelines-cli` acceptance test - `main.go` returns a root depending on if the binary is invoked as `pipelines`/`pipelines.exe` or `databricks` ## Why - **Symlink installation:** This enables users to invoke `pipelines` as a standalone CLI, which internally points to the main `databricks` binary. This is a step towards a dedicated Pipelines CLI experience, while minimizing code duplication. - **Stub CLI:** Lays groundwork for future pipelines-specific commands and workflows - **Testing:** Acceptance test. ## Tests - Added acceptance tests for `install-pipelines-cli` in pipelines to check if success message `pipelines successfully installed` is printed - **Test descriptions:** 1. **Fresh installation:** Creates temporary directory and verifies successful symlink creation. Validates CLI output and stub command functionality. 2. **Already installed scenario:** Confirms graceful handling when symlink already exists. 3. **File collision test:** Ensures installation fails when regular file exists at target path. 4. **Aliased binary execution:** Verifies installation works when databricks binary is renamed/called via alias.
1 parent d177292 commit d5108bf

7 files changed

Lines changed: 172 additions & 1 deletion

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
=== install pipelines cli
3+
>>> errcode [CLI] install-pipelines-cli -d ./subdir
4+
pipelines successfully installed in directory "./subdir"
5+
6+
>>> errcode ./subdir/pipelines
7+
Pipelines CLI (stub, to be filled in)
8+
9+
Usage:
10+
pipelines [flags]
11+
12+
Flags:
13+
-h, --help help for pipelines
14+
15+
=== pipelines already installed
16+
>>> errcode [CLI] install-pipelines-cli -d ./subdir
17+
pipelines already installed in directory "./subdir"
18+
19+
=== pipelines file exists, should not overwrite
20+
>>> errcode [CLI] install-pipelines-cli -d ./subdir
21+
Error: cannot install pipelines CLI: "subdir/pipelines" already exists
22+
23+
Exit code: 1
24+
25+
=== databricks executable called with alias
26+
>>> errcode ./subdir/notdatabricks install-pipelines-cli -d ./subdir
27+
pipelines successfully installed in directory "./subdir"
28+
29+
>>> errcode ./subdir/pipelines
30+
Pipelines CLI (stub, to be filled in)
31+
32+
Usage:
33+
pipelines [flags]
34+
35+
Flags:
36+
-h, --help help for pipelines
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tmpdir="./subdir"
2+
pipelines="$tmpdir/pipelines"
3+
mkdir -p $tmpdir
4+
5+
title "install pipelines cli"
6+
trace errcode $CLI install-pipelines-cli -d $tmpdir
7+
trace errcode $pipelines
8+
9+
title "pipelines already installed"
10+
trace errcode $CLI install-pipelines-cli -d $tmpdir
11+
rm -f $pipelines
12+
13+
title "pipelines file exists, should not overwrite"
14+
touch $pipelines
15+
trace errcode $CLI install-pipelines-cli -d $tmpdir
16+
rm -f $pipelines
17+
18+
title "databricks executable called with alias"
19+
cp $CLI $tmpdir/notdatabricks
20+
trace errcode $tmpdir/notdatabricks install-pipelines-cli -d $tmpdir
21+
trace errcode $pipelines
22+
23+
# Cleanup
24+
rm -f $tmpdir/notdatabricks $pipelines
25+
rm -rf $tmpdir
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Fix path with reverse slashes in the output for Windows.
2+
[[Repls]]
3+
Old = '\\\\'
4+
New = '/'
5+
6+
[[Repls]]
7+
Old = '\\'
8+
New = '/'

cmd/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/databricks/cli/cmd/configure"
1212
"github.com/databricks/cli/cmd/fs"
1313
"github.com/databricks/cli/cmd/labs"
14+
"github.com/databricks/cli/cmd/pipelines"
1415
"github.com/databricks/cli/cmd/root"
1516
"github.com/databricks/cli/cmd/selftest"
1617
"github.com/databricks/cli/cmd/sync"
@@ -106,6 +107,7 @@ func New(ctx context.Context) *cobra.Command {
106107
cli.AddCommand(sync.New())
107108
cli.AddCommand(version.New())
108109
cli.AddCommand(selftest.New())
110+
cli.AddCommand(pipelines.InstallPipelinesCLI())
109111

110112
// Add workspace command groups, filtering out empty groups or groups with only hidden commands.
111113
allGroups := workspace.Groups()
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package pipelines
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/databricks/cli/libs/cmdio"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
func installPipelinesSymlink(ctx context.Context, directory string) error {
15+
path, err := os.Executable()
16+
if err != nil {
17+
return err
18+
}
19+
realPath, err := filepath.EvalSymlinks(path)
20+
if err != nil {
21+
return err
22+
}
23+
24+
dir := directory
25+
if dir == "" {
26+
dir = filepath.Dir(path)
27+
}
28+
pipelinesPath := filepath.Join(dir, "pipelines")
29+
30+
_, err = os.Lstat(pipelinesPath)
31+
if err != nil {
32+
if !errors.Is(err, os.ErrNotExist) {
33+
return err
34+
}
35+
err = os.Symlink(realPath, pipelinesPath)
36+
if err != nil {
37+
return err
38+
}
39+
cmdio.LogString(ctx, fmt.Sprintf("pipelines successfully installed in directory %q", dir))
40+
return nil
41+
}
42+
43+
target, err := filepath.EvalSymlinks(pipelinesPath)
44+
if err != nil {
45+
return err
46+
}
47+
if realPath == target {
48+
cmdio.LogString(ctx, fmt.Sprintf("pipelines already installed in directory %q", dir))
49+
return nil
50+
}
51+
return fmt.Errorf("cannot install pipelines CLI: %q already exists", pipelinesPath)
52+
}
53+
54+
func InstallPipelinesCLI() *cobra.Command {
55+
var directory string
56+
cmd := &cobra.Command{
57+
Use: "install-pipelines-cli",
58+
Short: "Install Pipelines CLI",
59+
Hidden: true,
60+
RunE: func(cmd *cobra.Command, args []string) error {
61+
return installPipelinesSymlink(cmd.Context(), directory)
62+
},
63+
}
64+
cmd.Flags().StringVarP(&directory, "directory", "d", "", "Directory in which to install pipelines CLI (defaults to databricks CLI's directory)")
65+
return cmd
66+
}

cmd/pipelines/pipelines.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package pipelines
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
func New() *cobra.Command {
8+
cmd := &cobra.Command{
9+
Use: "pipelines",
10+
Short: "Pipelines CLI",
11+
Long: "Pipelines CLI (stub, to be filled in)",
12+
Run: func(cmd *cobra.Command, args []string) {
13+
_ = cmd.Help()
14+
},
15+
}
16+
17+
return cmd
18+
}

main.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@ package main
33
import (
44
"context"
55
"os"
6+
"path/filepath"
7+
"strings"
68

79
"github.com/databricks/cli/cmd"
10+
"github.com/databricks/cli/cmd/pipelines"
811
"github.com/databricks/cli/cmd/root"
12+
"github.com/spf13/cobra"
913
)
1014

15+
// If invoked as 'pipelines' (or 'pipelines.exe' on Windows), returns pipelines-specific commands,
16+
// otherwise returns the databricks CLI commands. This is used to allow the same
17+
// binary to be used for both pipelines and databricks CLI commands.
18+
func getCommand(ctx context.Context) *cobra.Command {
19+
invokedAs := filepath.Base(os.Args[0])
20+
if strings.HasPrefix(invokedAs, "pipelines") {
21+
return pipelines.New()
22+
}
23+
return cmd.New(ctx)
24+
}
25+
1126
func main() {
1227
ctx := context.Background()
13-
err := root.Execute(ctx, cmd.New(ctx))
28+
command := getCommand(ctx)
29+
err := root.Execute(ctx, command)
1430
if err != nil {
1531
os.Exit(1)
1632
}

0 commit comments

Comments
 (0)