diff --git a/cmd/dagger/cmd/init.go b/cmd/dagger/cmd/init.go index ca6a4f84..f16975ba 100644 --- a/cmd/dagger/cmd/init.go +++ b/cmd/dagger/cmd/init.go @@ -36,12 +36,10 @@ var initCmd = &cobra.Command{ dir = cwd } - ws, err := state.Init(ctx, dir) + _, err := state.Init(ctx, dir) if err != nil { lg.Fatal().Err(err).Msg("failed to initialize workspace") } - - lg.Info().Str("path", ws.DaggerDir()).Msg("initialized new empty workspace") }, } diff --git a/cmd/dagger/cmd/new.go b/cmd/dagger/cmd/new.go index 470de68c..70ece746 100644 --- a/cmd/dagger/cmd/new.go +++ b/cmd/dagger/cmd/new.go @@ -1,10 +1,6 @@ package cmd import ( - "fmt" - "path/filepath" - "strings" - "github.com/spf13/cobra" "github.com/spf13/viper" "go.dagger.io/dagger/cmd/dagger/cmd/common" @@ -36,42 +32,16 @@ var newCmd = &cobra.Command{ } name := args[0] - module := viper.GetString("module") - if module == "" { - lg.Fatal().Msg("missing --module") - } - p, err := filepath.Abs(module) - if err != nil { - lg.Fatal().Err(err).Str("path", module).Msg("unable to resolve path") - } - - if !strings.HasPrefix(p, workspace.Path) { - lg.Fatal().Err(err).Str("path", module).Msg("module is outside the workspace") - } - p, err = filepath.Rel(workspace.Path, p) - if err != nil { - lg.Fatal().Err(err).Str("path", module).Msg("unable to resolve path") - } - if !strings.HasPrefix(p, ".") { - p = "./" + p - } - module = p - - ws, err := workspace.Create(ctx, name, state.Plan{ - Module: module, + _, err := workspace.Create(ctx, name, state.Plan{ Package: viper.GetString("package"), }) if err != nil { lg.Fatal().Err(err).Msg("failed to create environment") } - - lg.Info().Str("name", name).Msg("created new empty environment") - lg.Info().Str("name", name).Msg(fmt.Sprintf("to add code to the plan, copy or create cue files under: %s", ws.Plan.Module)) }, } func init() { - newCmd.Flags().StringP("module", "m", ".", "references the local path of the cue module to use as a plan, relative to the workspace root") newCmd.Flags().StringP("package", "p", "", "references the name of the Cue package within the module to use as a plan. Default: defer to cue loader") if err := viper.BindPFlags(newCmd.Flags()); err != nil { panic(err) diff --git a/environment/environment.go b/environment/environment.go index 59dc671c..34ef1fc2 100644 --- a/environment/environment.go +++ b/environment/environment.go @@ -82,7 +82,7 @@ func (e *Environment) LoadPlan(ctx context.Context, s solver.Solver) error { span, ctx := opentracing.StartSpanFromContext(ctx, "environment.LoadPlan") defer span.Finish() - planSource, err := e.state.Plan.Source().Compile("", e.state) + planSource, err := e.state.Source().Compile("", e.state) if err != nil { return err } @@ -157,7 +157,7 @@ func (e *Environment) LocalDirs() map[string]string { } // 2. Scan the plan - plan, err := e.state.Plan.Source().Compile("", e.state) + plan, err := e.state.Source().Compile("", e.state) if err != nil { panic(err) } diff --git a/state/state.go b/state/state.go index 67637322..b8c7a87a 100644 --- a/state/state.go +++ b/state/state.go @@ -9,7 +9,7 @@ type State struct { Workspace string `yaml:"-"` // Plan - Plan Plan `yaml:"plan"` + Plan Plan `yaml:"plan,omitempty"` // Human-friendly environment name. // A environment may have more than one name. @@ -23,17 +23,21 @@ type State struct { Computed string `yaml:"-"` } +// Cue module containing the environment plan +func (s *State) Source() Input { + w := s.Workspace + // FIXME: backward compatibility + if mod := s.Plan.Module; mod != "" { + w = mod + } + return DirInput(w, []string{}, []string{}) +} + type Plan struct { Module string `yaml:"module,omitempty"` Package string `yaml:"package,omitempty"` } -// Cue module containing the environment plan -// The input's top-level artifact is used as a module directory. -func (p *Plan) Source() Input { - return DirInput(p.Module, []string{}, []string{}) -} - func (s *State) SetInput(key string, value Input) error { if s.Inputs == nil { s.Inputs = make(map[string]Input) diff --git a/state/workspace.go b/state/workspace.go index d0c7befd..dc7c4993 100644 --- a/state/workspace.go +++ b/state/workspace.go @@ -8,6 +8,7 @@ import ( "os" "path" "path/filepath" + "strings" "github.com/rs/zerolog/log" "go.dagger.io/dagger/keychain" @@ -161,18 +162,16 @@ func (w *Workspace) Get(ctx context.Context, name string) (*State, error) { return nil, err } st.Path = envPath - // Backward compat: if no plan module has been provided, - // use `.dagger/env//plan` + // FIXME: Backward compat: Support for old-style `.dagger/env//plan` if st.Plan.Module == "" { planPath := path.Join(envPath, planDir) - if _, err := os.Stat(planPath); err != nil { - return nil, fmt.Errorf("missing plan information for %q", name) + if _, err := os.Stat(planPath); err == nil { + planRelPath, err := filepath.Rel(w.Path, planPath) + if err != nil { + return nil, err + } + st.Plan.Module = planRelPath } - planRelPath, err := filepath.Rel(w.Path, planPath) - if err != nil { - return nil, err - } - st.Plan.Module = planRelPath } st.Workspace = w.Path @@ -230,20 +229,22 @@ func (w *Workspace) Save(ctx context.Context, st *State) error { } func (w *Workspace) Create(ctx context.Context, name string, plan Plan) (*State, error) { + if _, err := w.Get(ctx, name); err == nil { + return nil, ErrExist + } + + pkg, err := w.cleanPackageName(ctx, plan.Package) + if err != nil { + return nil, err + } + envPath, err := filepath.Abs(w.envPath(name)) if err != nil { return nil, err } - if _, err := os.Stat(plan.Module); err != nil { - return nil, err - } - // Environment directory if err := os.MkdirAll(envPath, 0755); err != nil { - if errors.Is(err, os.ErrExist) { - return nil, ErrExist - } return nil, err } @@ -252,8 +253,10 @@ func (w *Workspace) Create(ctx context.Context, name string, plan Plan) (*State, st := &State{ Path: envPath, Workspace: w.Path, - Plan: plan, - Name: name, + Plan: Plan{ + Package: pkg, + }, + Name: name, } data, err := yaml.Marshal(st) @@ -284,6 +287,51 @@ func (w *Workspace) Create(ctx context.Context, name string, plan Plan) (*State, return st, nil } -func (w *Workspace) DaggerDir() string { - return path.Join(w.Path, daggerDir) +func (w *Workspace) cleanPackageName(ctx context.Context, pkg string) (string, error) { + lg := log. + Ctx(ctx). + With(). + Str("package", pkg). + Logger() + + if pkg == "" { + return pkg, nil + } + + // If the package is not a path, then it must be a domain (e.g. foo.bar/mypackage) + if _, err := os.Stat(pkg); err != nil { + if !errors.Is(err, os.ErrNotExist) { + return "", err + } + + // Make sure the domain is in the correct form + if !strings.Contains(pkg, ".") || !strings.Contains(pkg, "/") { + return "", fmt.Errorf("invalid package %q", pkg) + } + + return pkg, nil + } + + p, err := filepath.Abs(pkg) + if err != nil { + lg.Error().Err(err).Msg("unable to resolve path") + return "", err + } + + if !strings.HasPrefix(p, w.Path) { + lg.Fatal().Err(err).Msg("package is outside the workspace") + return "", err + } + + p, err = filepath.Rel(w.Path, p) + if err != nil { + lg.Fatal().Err(err).Msg("unable to resolve path") + return "", err + } + + if !strings.HasPrefix(p, ".") { + p = "./" + p + } + + return p, nil } diff --git a/tests/cli.bats b/tests/cli.bats index b2db1e56..b696c8ac 100644 --- a/tests/cli.bats +++ b/tests/cli.bats @@ -27,7 +27,7 @@ setup() { assert_success refute_output - run "$DAGGER" new "test" --module "$DAGGER_WORKSPACE" + run "$DAGGER" new "test" assert_success run "$DAGGER" list @@ -42,10 +42,10 @@ setup() { @test "dagger new: modules" { "$DAGGER" init - ln -s "$TESTDIR"/cli/input/simple "$DAGGER_WORKSPACE"/plan + cp -a "$TESTDIR"/cli/input/simple/* "$DAGGER_WORKSPACE" - "$DAGGER" new "a" --module "$DAGGER_WORKSPACE"/plan - "$DAGGER" new "b" --module "$DAGGER_WORKSPACE"/plan + "$DAGGER" new "a" + "$DAGGER" new "b" "$DAGGER" input -e "a" text "input" "a" "$DAGGER" input -e "b" text "input" "b" @@ -60,6 +60,9 @@ setup() { run "$DAGGER" query -l error -e "b" input -f text assert_success assert_output "b" + + # run ls -la "$DAGGER_WORKSPACE" + # assert_failure } # create different environments from the same module, @@ -67,10 +70,10 @@ setup() { @test "dagger new: packages" { "$DAGGER" init - ln -s "$TESTDIR"/cli/packages "$DAGGER_WORKSPACE"/plan + cp -a "$TESTDIR"/cli/packages/* "$DAGGER_WORKSPACE" - "$DAGGER" new "a" --module "$DAGGER_WORKSPACE"/plan --package alpha.dagger.io/test/a - "$DAGGER" new "b" --module "$DAGGER_WORKSPACE"/plan --package alpha.dagger.io/test/b + "$DAGGER" new "a" --package alpha.dagger.io/test/a + "$DAGGER" new "b" --package alpha.dagger.io/test/b "$DAGGER" up -e "a" "$DAGGER" up -e "b" diff --git a/tests/helpers.bash b/tests/helpers.bash index e4f231c9..9bbd96e0 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash @@ -20,11 +20,10 @@ common_setup() { dagger_new_with_plan() { local name="$1" local sourcePlan="$2" - local targetPlan="$DAGGER_WORKSPACE"/"$name" - ln -s "$sourcePlan" "$targetPlan" - "$DAGGER" new "$name" --module "$targetPlan" + cp -a "$sourcePlan"/* "$DAGGER_WORKSPACE" + "$DAGGER" new "$name" } # dagger helper to execute the right binary