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
14 changes: 12 additions & 2 deletions bundle/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,21 @@ func Context(ctx context.Context, b *Bundle) context.Context {
return context.WithValue(ctx, &bundleKey, b)
}

// GetOrNil returns the bundle as configured on the context.
// It returns nil if it isn't configured.
func GetOrNil(ctx context.Context) *Bundle {
bundle, ok := ctx.Value(&bundleKey).(*Bundle)
if !ok {
return nil
}
return bundle
}

// Get returns the bundle as configured on the context.
// It panics if it isn't configured.
func Get(ctx context.Context) *Bundle {
bundle, ok := ctx.Value(&bundleKey).(*Bundle)
if !ok {
bundle := GetOrNil(ctx)
if bundle == nil {
panic("context not configured with bundle")
}
return bundle
Expand Down
80 changes: 80 additions & 0 deletions cmd/bundle/sync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package bundle

import (
"fmt"
"log"
"time"

"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/phases"
"github.com/databricks/bricks/cmd/root"
"github.com/databricks/bricks/libs/sync"
"github.com/spf13/cobra"
)

func syncOptionsFromBundle(cmd *cobra.Command, b *bundle.Bundle) (*sync.SyncOptions, error) {
cacheDir, err := b.CacheDir()
if err != nil {
return nil, fmt.Errorf("cannot get bundle cache directory: %w", err)
}

opts := sync.SyncOptions{
LocalPath: b.Config.Path,
RemotePath: b.Config.Workspace.FilePath.Workspace,
Full: full,
PollInterval: interval,

SnapshotBasePath: cacheDir,
WorkspaceClient: b.WorkspaceClient(),
}
return &opts, nil
}

var syncCmd = &cobra.Command{
Use: "sync [flags]",
Short: "Synchronize bundle tree to the workspace",
Args: cobra.NoArgs,

PreRunE: root.MustConfigureBundle,
RunE: func(cmd *cobra.Command, args []string) error {
b := bundle.Get(cmd.Context())

// Run initialize phase to make sure paths are set.
err := bundle.Apply(cmd.Context(), b, []bundle.Mutator{
phases.Initialize(),
})
if err != nil {
return err
}

opts, err := syncOptionsFromBundle(cmd, b)
if err != nil {
return err
}

ctx := cmd.Context()
s, err := sync.New(ctx, *opts)
if err != nil {
return err
}

log.Printf("[INFO] Remote file sync location: %v", opts.RemotePath)

if watch {
return s.RunContinuous(ctx)
}

return s.RunOnce(ctx)
},
}

var interval time.Duration
var full bool
var watch bool

func init() {
AddCommand(syncCmd)
syncCmd.Flags().DurationVar(&interval, "interval", 1*time.Second, "file system polling interval (for --watch)")
syncCmd.Flags().BoolVar(&full, "full", false, "perform full synchronization (default is incremental)")
syncCmd.Flags().BoolVar(&watch, "watch", false, "watch local file system for changes")
}
126 changes: 81 additions & 45 deletions cmd/sync/sync.go
Original file line number Diff line number Diff line change
@@ -1,63 +1,103 @@
package sync

import (
"flag"
"fmt"
"log"
"path/filepath"
"time"

"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/cmd/root"
"github.com/databricks/bricks/libs/git"
"github.com/databricks/bricks/libs/sync"
"github.com/databricks/bricks/project"
"github.com/databricks/databricks-sdk-go"
"github.com/spf13/cobra"
)

// syncCmd represents the sync command
func syncOptionsFromBundle(cmd *cobra.Command, args []string, b *bundle.Bundle) (*sync.SyncOptions, error) {
if len(args) > 0 {
return nil, fmt.Errorf("SRC and DST are not configurable in the context of a bundle")
}

cacheDir, err := b.CacheDir()
if err != nil {
return nil, fmt.Errorf("cannot get bundle cache directory: %w", err)
}

opts := sync.SyncOptions{
LocalPath: b.Config.Path,
RemotePath: b.Config.Workspace.FilePath.Workspace,
Full: full,
PollInterval: interval,

SnapshotBasePath: cacheDir,
WorkspaceClient: b.WorkspaceClient(),
}
return &opts, nil
}

func syncOptionsFromArgs(cmd *cobra.Command, args []string) (*sync.SyncOptions, error) {
if len(args) != 2 {
return nil, flag.ErrHelp
}

opts := sync.SyncOptions{
LocalPath: args[0],
RemotePath: args[1],
Full: full,
PollInterval: interval,

// We keep existing behavior for VS Code extension where if there is
// no bundle defined, we store the snapshots in `.databricks`.
// The sync code will automatically create this directory if it doesn't
// exist and add it to the `.gitignore` file in the root.
SnapshotBasePath: filepath.Join(args[0], ".databricks"),
WorkspaceClient: databricks.Must(databricks.NewWorkspaceClient()),
}
return &opts, nil
}

var syncCmd = &cobra.Command{
Use: "sync",
Use: "sync [flags] SRC DST",
Short: "Synchronize a local directory to a workspace directory",
Args: cobra.MaximumNArgs(2),

PreRunE: project.Configure,
// PreRunE: root.TryConfigureBundle,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
prj := project.Get(ctx)
wsc := prj.WorkspacesClient()

me, err := prj.Me()
var opts *sync.SyncOptions
var err error

//
// To be uncommented and used once our VS Code extension is bundle aware.
// Until then, this could interfere with extension usage where a `bundle.yml` file is present.
// See https://github.com/databricks/bricks/pull/207.
//
// b := bundle.GetOrNil(cmd.Context())
// if b != nil {
// // Run initialize phase to make sure paths are set.
// err = bundle.Apply(cmd.Context(), b, []bundle.Mutator{
// phases.Initialize(),
// })
// if err != nil {
// return err
// }
// opts, err = syncOptionsFromBundle(cmd, args, b)
// } else {
opts, err = syncOptionsFromArgs(cmd, args)
// }
if err != nil {
return err
}

if *remotePath == "" {
repositoryName, err := git.RepositoryName()
if err != nil {
return err
}
*remotePath = fmt.Sprintf("/Repos/%s/%s", me.UserName, repositoryName)
}

log.Printf("[INFO] Remote file sync location: %v", *remotePath)

cacheDir, err := prj.CacheDir()
ctx := cmd.Context()
s, err := sync.New(ctx, *opts)
if err != nil {
return err
}

opts := sync.SyncOptions{
LocalPath: prj.Root(),
RemotePath: *remotePath,
Full: *full,
SnapshotBasePath: cacheDir,
PollInterval: *interval,
WorkspaceClient: wsc,
}

s, err := sync.New(ctx, opts)
if err != nil {
return err
}
log.Printf("[INFO] Remote file sync location: %v", opts.RemotePath)
Comment thread
pietern marked this conversation as resolved.

if *watch {
if watch {
return s.RunContinuous(ctx)
}

Expand All @@ -66,17 +106,13 @@ var syncCmd = &cobra.Command{
}

// project files polling interval
var interval *time.Duration

var remotePath *string

var full *bool
var watch *bool
var interval time.Duration
var full bool
var watch bool

func init() {
root.RootCmd.AddCommand(syncCmd)
interval = syncCmd.Flags().Duration("interval", 1*time.Second, "file system polling interval (for --watch)")
remotePath = syncCmd.Flags().String("remote-path", "", "remote path to store repo in. eg: /Repos/me@example.com/test-repo")
full = syncCmd.Flags().Bool("full", false, "perform full synchronization (default is incremental)")
watch = syncCmd.Flags().Bool("watch", false, "watch local file system for changes")
syncCmd.Flags().DurationVar(&interval, "interval", 1*time.Second, "file system polling interval (for --watch)")
syncCmd.Flags().BoolVar(&full, "full", false, "perform full synchronization (default is incremental)")
syncCmd.Flags().BoolVar(&watch, "watch", false, "watch local file system for changes")
}
55 changes: 55 additions & 0 deletions cmd/sync/sync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package sync

import (
"flag"
"path/filepath"
"testing"

"github.com/databricks/bricks/bundle"
"github.com/databricks/bricks/bundle/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestSyncOptionsFromBundle(t *testing.T) {
tempDir := t.TempDir()
b := &bundle.Bundle{
Config: config.Root{
Path: tempDir,

Bundle: config.Bundle{
Environment: "default",
},

Workspace: config.Workspace{
FilePath: config.PathLike{
Workspace: "/Users/jane@doe.com/path",
},
},
},
}

opts, err := syncOptionsFromBundle(syncCmd, []string{}, b)
require.NoError(t, err)
assert.Equal(t, tempDir, opts.LocalPath)
assert.Equal(t, "/Users/jane@doe.com/path", opts.RemotePath)
assert.Equal(t, filepath.Join(tempDir, ".databricks", "bundle", "default"), opts.SnapshotBasePath)
assert.NotNil(t, opts.WorkspaceClient)
}

func TestSyncOptionsFromArgsRequiredTwoArgs(t *testing.T) {
var err error
_, err = syncOptionsFromArgs(syncCmd, []string{})
require.ErrorIs(t, err, flag.ErrHelp)
_, err = syncOptionsFromArgs(syncCmd, []string{"foo"})
require.ErrorIs(t, err, flag.ErrHelp)
_, err = syncOptionsFromArgs(syncCmd, []string{"foo", "bar", "qux"})
require.ErrorIs(t, err, flag.ErrHelp)
}

func TestSyncOptionsFromArgs(t *testing.T) {
opts, err := syncOptionsFromArgs(syncCmd, []string{"/local", "/remote"})
require.NoError(t, err)
assert.Equal(t, "/local", opts.LocalPath)
assert.Equal(t, "/remote", opts.RemotePath)
}
Loading