diff --git a/cmd/dagger/cmd/common/common.go b/cmd/dagger/cmd/common/common.go index 295b20ae..05114deb 100644 --- a/cmd/dagger/cmd/common/common.go +++ b/cmd/dagger/cmd/common/common.go @@ -12,78 +12,8 @@ import ( "go.dagger.io/dagger/client" "go.dagger.io/dagger/compiler" "go.dagger.io/dagger/plancontext" - "go.dagger.io/dagger/state" ) -func CurrentProject(ctx context.Context) *state.Project { - lg := log.Ctx(ctx) - - if projectPath := viper.GetString("project"); projectPath != "" { - project, err := state.Open(ctx, projectPath) - if err != nil { - lg. - Fatal(). - Err(err). - Str("path", projectPath). - Msg("failed to open project") - } - return project - } - - project, err := state.Current(ctx) - if err != nil { - lg. - Fatal(). - Err(err). - Msg("failed to determine current project") - } - return project -} - -func CurrentEnvironmentState(ctx context.Context, project *state.Project) *state.State { - lg := log.Ctx(ctx) - - environmentName := viper.GetString("environment") - if environmentName != "" { - st, err := project.Get(ctx, environmentName) - if err != nil { - lg. - Fatal(). - Err(err). - Msg("failed to load environment") - } - return st - } - - environments, err := project.List(ctx) - if err != nil { - lg. - Fatal(). - Err(err). - Msg("failed to list environments") - } - - if len(environments) == 0 { - lg. - Fatal(). - Msg("no environments") - } - - if len(environments) > 1 { - envNames := []string{} - for _, e := range environments { - envNames = append(envNames, e.Name) - } - lg. - Fatal(). - Err(err). - Strs("environments", envNames). - Msg("multiple environments available in the project, select one with `--environment`") - } - - return environments[0] -} - // FormatValue returns the String representation of the cue value func FormatValue(val *compiler.Value) string { switch { diff --git a/cmd/dagger/cmd/common/track.go b/cmd/dagger/cmd/common/track.go index f9ab140f..67bfe77a 100644 --- a/cmd/dagger/cmd/common/track.go +++ b/cmd/dagger/cmd/common/track.go @@ -8,7 +8,6 @@ import ( "github.com/go-git/go-git/v5" "github.com/spf13/cobra" - "go.dagger.io/dagger/state" "go.dagger.io/dagger/telemetry" ) @@ -32,34 +31,6 @@ func commandName(cmd *cobra.Command) string { return strings.Join(parts, " ") } -// TrackProjectCommand is like TrackCommand but includes project and -// optionally environment metadata. -func TrackProjectCommand(ctx context.Context, cmd *cobra.Command, w *state.Project, env *state.State, props ...*telemetry.Property) chan struct{} { - props = append([]*telemetry.Property{ - { - // Hash the repository URL for privacy - Name: "git_repository_hash", - Value: hash(gitRepoURL(w.Path)), - }, - { - // The project path might contain the username (e.g. /home/user/project), so we hash it for privacy. - Name: "project_path_hash", - Value: hash(w.Path), - }, - }, props...) - - if env != nil { - props = append([]*telemetry.Property{ - { - Name: "environment_name", - Value: env.Name, - }, - }, props...) - } - - return TrackCommand(ctx, cmd, props...) -} - // hash returns the sha256 digest of the string func hash(s string) string { return fmt.Sprintf("%x", sha256.Sum256([]byte(s))) diff --git a/cmd/dagger/cmd/init.go b/cmd/dagger/cmd/init.go index 26f9eeda..aa575fb4 100644 --- a/cmd/dagger/cmd/init.go +++ b/cmd/dagger/cmd/init.go @@ -5,9 +5,8 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "go.dagger.io/dagger/cmd/dagger/cmd/common" "go.dagger.io/dagger/cmd/dagger/logger" - "go.dagger.io/dagger/state" + "go.dagger.io/dagger/pkg" ) var initCmd = &cobra.Command{ @@ -37,12 +36,13 @@ var initCmd = &cobra.Command{ dir = cwd } - project, err := state.Init(ctx, dir) + err := pkg.CueModInit(ctx, dir) if err != nil { lg.Fatal().Err(err).Msg("failed to initialize project") } - <-common.TrackProjectCommand(ctx, cmd, project, nil) + // TODO: Add telemtry for init + // <-common.TrackProjectCommand(ctx, cmd, project, nil) }, } diff --git a/cmd/dagger/cmd/mod/get.go b/cmd/dagger/cmd/mod/get.go index 60ee43c2..16b2d015 100644 --- a/cmd/dagger/cmd/mod/get.go +++ b/cmd/dagger/cmd/mod/get.go @@ -6,7 +6,6 @@ import ( "go.dagger.io/dagger/cmd/dagger/logger" "go.dagger.io/dagger/mod" "go.dagger.io/dagger/pkg" - "go.dagger.io/dagger/state" ) var getCmd = &cobra.Command{ @@ -28,10 +27,10 @@ var getCmd = &cobra.Command{ var err error cueModPath := pkg.GetCueModParent() - // err = pkg.CueModInit(ctx, cueModPath) - _, err = state.Init(ctx, cueModPath) - if err != nil && err != state.ErrAlreadyInit { + err = pkg.CueModInit(ctx, cueModPath) + if err != nil { lg.Fatal().Err(err).Msg("failed to initialize cue.mod") + panic(err) } var update = viper.GetBool("update") diff --git a/pkg/pkg.go b/pkg/pkg.go index 5c6b5f1c..cd11e84d 100644 --- a/pkg/pkg.go +++ b/pkg/pkg.go @@ -59,7 +59,7 @@ func Vendor(ctx context.Context, p string) error { }() // ensure cue module is initialized - if err := cueModInit(ctx, p); err != nil { + if err := CueModInit(ctx, p); err != nil { return err } @@ -167,7 +167,7 @@ func GetCueModParent() string { return parentDir } -func cueModInit(ctx context.Context, parentDir string) error { +func CueModInit(ctx context.Context, parentDir string) error { lg := log.Ctx(ctx) modDir := path.Join(parentDir, "cue.mod") diff --git a/state/input.go b/state/input.go deleted file mode 100644 index 726ec5b7..00000000 --- a/state/input.go +++ /dev/null @@ -1,300 +0,0 @@ -package state - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "strings" - - "cuelang.org/go/cue" - - "go.dagger.io/dagger/compiler" -) - -// An input is a value or artifact supplied by the user. -// -// - A value is any structured data which can be encoded as cue. -// -// - An artifact is a piece of data, like a source code checkout, -// binary bundle, docker image, database backup etc. -// -// Artifacts can be passed as inputs, generated dynamically from -// other inputs, and received as outputs. -// Under the hood, an artifact is encoded as a LLB pipeline, and -// attached to the cue configuration as a -// - -type Input struct { - Dir *dirInput `yaml:"dir,omitempty"` - Git *gitInput `yaml:"git,omitempty"` - Secret *secretInput `yaml:"secret,omitempty"` - Text *textInput `yaml:"text,omitempty"` - JSON *jsonInput `yaml:"json,omitempty"` - YAML *yamlInput `yaml:"yaml,omitempty"` - File *fileInput `yaml:"file,omitempty"` - Bool *boolInput `yaml:"bool,omitempty"` - Socket *socketInput `yaml:"socket,omitempty"` -} - -func (i Input) Compile(state *State) (*compiler.Value, error) { - switch { - case i.Dir != nil: - return i.Dir.Compile(state) - case i.Git != nil: - return i.Git.Compile(state) - case i.Text != nil: - return i.Text.Compile(state) - case i.Secret != nil: - return i.Secret.Compile(state) - case i.JSON != nil: - return i.JSON.Compile(state) - case i.YAML != nil: - return i.YAML.Compile(state) - case i.File != nil: - return i.File.Compile(state) - case i.Bool != nil: - return i.Bool.Compile(state) - case i.Socket != nil: - return i.Socket.Compile(state) - default: - return nil, fmt.Errorf("input has not been set") - } -} - -// An input artifact loaded from a local directory -func DirInput(path string, include []string, exclude []string) Input { - return Input{ - Dir: &dirInput{ - Path: path, - Include: include, - Exclude: exclude, - }, - } -} - -type dirInput struct { - Path string `yaml:"path,omitempty"` - Include []string `yaml:"include,omitempty"` - Exclude []string `yaml:"exclude,omitempty"` -} - -func (dir dirInput) Compile(state *State) (*compiler.Value, error) { - // FIXME: serialize an intermediate struct, instead of generating cue source - - // json.Marshal([]string{}) returns []byte("null"), which wreaks havoc - // in Cue because `null` is not a `[...string]` - includeLLB := []byte("[]") - if len(dir.Include) > 0 { - var err error - includeLLB, err = json.Marshal(dir.Include) - if err != nil { - return nil, err - } - } - excludeLLB := []byte("[]") - if len(dir.Exclude) > 0 { - var err error - excludeLLB, err = json.Marshal(dir.Exclude) - if err != nil { - return nil, err - } - } - - p := dir.Path - if !filepath.IsAbs(p) { - p = filepath.Clean(path.Join(state.Project, dir.Path)) - } - if !strings.HasPrefix(p, state.Project) { - return nil, fmt.Errorf("%q is outside the project", dir.Path) - } - // Check that directory exists - if _, err := os.Stat(p); os.IsNotExist(err) { - return nil, fmt.Errorf("%q dir doesn't exist", dir.Path) - } - - dirPath, err := json.Marshal(p) - if err != nil { - return nil, err - } - - state.Context.LocalDirs.Add(p) - - llb := fmt.Sprintf( - `#up: [{do: "local", dir: %s, include: %s, exclude: %s}]`, - dirPath, - includeLLB, - excludeLLB, - ) - return compiler.Compile("", llb) -} - -// An input artifact loaded from a git repository -type gitInput struct { - Remote string `yaml:"remote,omitempty"` - Ref string `yaml:"ref,omitempty"` - Dir string `yaml:"dir,omitempty"` -} - -func GitInput(remote, ref, dir string) Input { - return Input{ - Git: &gitInput{ - Remote: remote, - Ref: ref, - Dir: dir, - }, - } -} - -func (git gitInput) Compile(_ *State) (*compiler.Value, error) { - ref := "HEAD" - if git.Ref != "" { - ref = git.Ref - } - - dir := "" - if git.Dir != "" { - dir = fmt.Sprintf(`,{do:"subdir", dir:"%s"}`, git.Dir) - } - - return compiler.Compile("", fmt.Sprintf( - `#up: [{do:"fetch-git", remote:"%s", ref:"%s"}%s]`, - git.Remote, - ref, - dir, - )) -} - -// An input value encoded as text -func TextInput(data string) Input { - i := textInput(data) - return Input{ - Text: &i, - } -} - -type textInput string - -func (i textInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.Compile("", fmt.Sprintf("%q", i)) -} - -// A secret input value -func SecretInput(data string) Input { - i := secretInput(data) - return Input{ - Secret: &i, - } -} - -type secretInput string - -func (i secretInput) Compile(st *State) (*compiler.Value, error) { - secret := st.Context.Secrets.New(i.PlainText()) - return secret.MarshalCUE(), nil -} - -func (i secretInput) PlainText() string { - return string(i) -} - -// An input value encoded as Bool -func BoolInput(data string) Input { - i := boolInput(data) - return Input{ - Bool: &i, - } -} - -type boolInput string - -func (i boolInput) Compile(_ *State) (*compiler.Value, error) { - s := map[boolInput]struct{}{ - "true": {}, - "false": {}, - } - if _, ok := s[i]; ok { - return compiler.DecodeJSON("", []byte(i)) - } - return nil, fmt.Errorf("%q is not a valid boolean: ", i) -} - -// An input value encoded as JSON -func JSONInput(data string) Input { - i := jsonInput(data) - return Input{ - JSON: &i, - } -} - -type jsonInput string - -func (i jsonInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.DecodeJSON("", []byte(i)) -} - -// An input value encoded as YAML -func YAMLInput(data string) Input { - i := yamlInput(data) - return Input{ - YAML: &i, - } -} - -type yamlInput string - -func (i yamlInput) Compile(_ *State) (*compiler.Value, error) { - return compiler.DecodeYAML("", []byte(i)) -} - -func FileInput(data string) Input { - return Input{ - File: &fileInput{ - Path: data, - }, - } -} - -type fileInput struct { - Path string `yaml:"path,omitempty"` -} - -func (i fileInput) Compile(_ *State) (*compiler.Value, error) { - data, err := ioutil.ReadFile(i.Path) - if err != nil { - return nil, err - } - value := compiler.NewValue() - if err := value.FillPath(cue.MakePath(), data); err != nil { - return nil, err - } - return value, nil -} - -// A socket input value -func SocketInput(data, socketType string) Input { - i := socketInput{} - - switch socketType { - case "npipe": - i.Npipe = data - case "unix": - i.Unix = data - } - - return Input{ - Socket: &i, - } -} - -type socketInput struct { - Unix string `json:"unix,omitempty" yaml:"unix,omitempty"` - Npipe string `json:"npipe,omitempty" yaml:"npipe,omitempty"` -} - -func (i socketInput) Compile(st *State) (*compiler.Value, error) { - service := st.Context.Services.New(i.Unix, i.Npipe) - return service.MarshalCUE(), nil -} diff --git a/state/project.go b/state/project.go deleted file mode 100644 index bc530d9e..00000000 --- a/state/project.go +++ /dev/null @@ -1,354 +0,0 @@ -package state - -import ( - "bytes" - "context" - "errors" - "fmt" - "os" - "path" - "path/filepath" - "strings" - - "github.com/rs/zerolog/log" - "go.dagger.io/dagger/keychain" - "go.dagger.io/dagger/pkg" - "go.dagger.io/dagger/plancontext" - "gopkg.in/yaml.v3" -) - -var ( - ErrNotInit = errors.New("not initialized") - ErrAlreadyInit = errors.New("already initialized") - ErrNotExist = errors.New("environment doesn't exist") - ErrExist = errors.New("environment already exists") -) - -const ( - daggerDir = ".dagger" - envDir = "env" - stateDir = "state" - planDir = "plan" - manifestFile = "values.yaml" - computedFile = "computed.json" -) - -type Project struct { - Path string -} - -func Init(ctx context.Context, dir string) (*Project, error) { - root, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - daggerRoot := path.Join(root, daggerDir) - if err := os.Mkdir(daggerRoot, 0755); err != nil { - if errors.Is(err, os.ErrExist) { - return nil, ErrAlreadyInit - } - return nil, err - } - - if err := os.Mkdir(path.Join(daggerRoot, envDir), 0755); err != nil { - return nil, err - } - - if err := pkg.Vendor(ctx, root); err != nil { - return nil, err - } - - return &Project{ - Path: root, - }, nil -} - -func Open(ctx context.Context, dir string) (*Project, error) { - _, err := os.Stat(path.Join(dir, daggerDir)) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, ErrNotInit - } - return nil, err - } - - root, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - return &Project{ - Path: root, - }, nil -} - -func Current(ctx context.Context) (*Project, error) { - current, err := os.Getwd() - if err != nil { - return nil, err - } - - // Walk every parent directory to find .dagger - for { - _, err := os.Stat(path.Join(current, daggerDir, envDir)) - if err == nil { - return Open(ctx, current) - } - parent := filepath.Dir(current) - if parent == current { - break - } - current = parent - } - - return nil, ErrNotInit -} - -func (w *Project) envPath(name string) string { - return path.Join(w.Path, daggerDir, envDir, name) -} - -func (w *Project) List(ctx context.Context) ([]*State, error) { - var ( - environments = []*State{} - err error - ) - - files, err := os.ReadDir(path.Join(w.Path, daggerDir, envDir)) - if err != nil { - return nil, err - } - for _, f := range files { - if !f.IsDir() { - continue - } - st, err := w.Get(ctx, f.Name()) - if err != nil { - // If the environment doesn't exist (e.g. no values.yaml, skip silently) - if !errors.Is(err, ErrNotExist) { - log. - Ctx(ctx). - Err(err). - Str("name", f.Name()). - Msg("failed to load environment") - } - continue - } - environments = append(environments, st) - } - - return environments, nil -} - -func (w *Project) Get(ctx context.Context, name string) (*State, error) { - envPath, err := filepath.Abs(w.envPath(name)) - if err != nil { - return nil, err - } - if _, err := os.Stat(envPath); err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, ErrNotExist - } - return nil, err - } - - manifest, err := os.ReadFile(path.Join(envPath, manifestFile)) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, ErrNotExist - } - return nil, err - } - manifest, err = keychain.Decrypt(ctx, manifest) - if err != nil { - return nil, fmt.Errorf("unable to decrypt state: %w", err) - } - - var st State - if err := yaml.Unmarshal(manifest, &st); err != nil { - return nil, err - } - st.Context = plancontext.New() - if platform := st.Platform; platform != "" { - if err := st.Context.Platform.Set(platform); err != nil { - return nil, err - } - } - st.Path = envPath - // 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 { - planRelPath, err := filepath.Rel(w.Path, planPath) - if err != nil { - return nil, err - } - st.Plan.Module = planRelPath - } - } - st.Project = w.Path - - computed, err := os.ReadFile(path.Join(envPath, stateDir, computedFile)) - if err == nil { - st.Computed = string(computed) - } - - return &st, nil -} - -func (w *Project) Save(ctx context.Context, st *State) error { - data, err := yaml.Marshal(st) - if err != nil { - return err - } - - manifestPath := path.Join(st.Path, manifestFile) - - currentEncrypted, err := os.ReadFile(manifestPath) - if err != nil { - return err - } - currentPlain, err := keychain.Decrypt(ctx, currentEncrypted) - if err != nil { - return fmt.Errorf("unable to decrypt state: %w", err) - } - - // Only update the encrypted file if there were changes - if !bytes.Equal(data, currentPlain) { - encrypted, err := keychain.Reencrypt(ctx, manifestPath, data) - if err != nil { - return err - } - if err := os.WriteFile(manifestPath, encrypted, 0600); err != nil { - return err - } - } - - if st.Computed != "" { - state := path.Join(st.Path, stateDir) - if err := os.MkdirAll(state, 0755); err != nil { - return err - } - err := os.WriteFile( - path.Join(state, "computed.json"), - []byte(st.Computed), - 0600) - if err != nil { - return err - } - } - - return nil -} - -func (w *Project) Create(ctx context.Context, name string, plan Plan, platform string) (*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 - } - - // Environment directory - if err := os.MkdirAll(envPath, 0755); err != nil { - return nil, err - } - - manifestPath := path.Join(envPath, manifestFile) - - st := &State{ - Context: plancontext.New(), - - Path: envPath, - Project: w.Path, - Plan: Plan{ - Package: pkg, - }, - Name: name, - Platform: platform, - } - - data, err := yaml.Marshal(st) - if err != nil { - return nil, err - } - key, err := keychain.Default(ctx) - if err != nil { - return nil, err - } - encrypted, err := keychain.Encrypt(ctx, manifestPath, data, key) - if err != nil { - return nil, err - } - if err := os.WriteFile(manifestPath, encrypted, 0600); err != nil { - return nil, err - } - - err = os.WriteFile( - path.Join(envPath, ".gitignore"), - []byte("# dagger state\nstate/**\n"), - 0600, - ) - if err != nil { - return nil, err - } - - return st, nil -} - -func (w *Project) 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 project") - 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/state/project_test.go b/state/project_test.go deleted file mode 100644 index 9d3d3223..00000000 --- a/state/project_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package state - -import ( - "context" - "os" - "path" - "strings" - "testing" - - "github.com/stretchr/testify/require" - "go.dagger.io/dagger/keychain" - "gopkg.in/yaml.v3" -) - -func TestProject(t *testing.T) { - ctx := context.TODO() - - keychain.EnsureDefaultKey(ctx) - - root, err := os.MkdirTemp(os.TempDir(), "dagger-*") - require.NoError(t, err) - - // Open should fail since the directory is not initialized - _, err = Open(ctx, root) - require.ErrorIs(t, ErrNotInit, err) - - // Init - project, err := Init(ctx, root) - require.NoError(t, err) - require.Equal(t, root, project.Path) - - // Create - st, err := project.Create(ctx, "test", Plan{ - Module: ".", - }, "linux/amd64") - require.NoError(t, err) - require.Equal(t, "test", st.Name) - require.Equal(t, "linux/amd64", st.Platform) - - // Open - project, err = Open(ctx, root) - require.NoError(t, err) - require.Equal(t, root, project.Path) - - // List - envs, err := project.List(ctx) - require.NoError(t, err) - require.Len(t, envs, 1) - require.Equal(t, "test", envs[0].Name) - - // Get - env, err := project.Get(ctx, "test") - require.NoError(t, err) - require.Equal(t, "test", env.Name) - require.Equal(t, "linux/amd64", env.Platform) - - // Save - require.NoError(t, env.SetInput("foo", TextInput("bar"))) - require.NoError(t, project.Save(ctx, env)) - project, err = Open(ctx, root) - require.NoError(t, err) - env, err = project.Get(ctx, "test") - require.NoError(t, err) - require.Contains(t, env.Inputs, "foo") -} - -func TestEncryption(t *testing.T) { - ctx := context.TODO() - - keychain.EnsureDefaultKey(ctx) - - readManifest := func(st *State) *State { - data, err := os.ReadFile(path.Join(st.Path, manifestFile)) - require.NoError(t, err) - m := State{} - require.NoError(t, yaml.Unmarshal(data, &m)) - return &m - } - - root, err := os.MkdirTemp(os.TempDir(), "dagger-*") - require.NoError(t, err) - project, err := Init(ctx, root) - require.NoError(t, err) - - _, err = project.Create(ctx, "test", Plan{ - Module: ".", - }, "linux/amd64") - require.NoError(t, err) - - // Set a plaintext input, make sure it is not encrypted - st, err := project.Get(ctx, "test") - require.NoError(t, err) - require.NoError(t, st.SetInput("plain", TextInput("plain"))) - require.NoError(t, project.Save(ctx, st)) - o := readManifest(st) - require.Contains(t, o.Inputs, "plain") - require.Equal(t, "plain", string(*o.Inputs["plain"].Text)) - - // Set a secret input, make sure it's encrypted - st, err = project.Get(ctx, "test") - require.NoError(t, err) - require.NoError(t, st.SetInput("secret", SecretInput("secret"))) - require.NoError(t, project.Save(ctx, st)) - o = readManifest(st) - require.Contains(t, o.Inputs, "secret") - secretValue := string(*o.Inputs["secret"].Secret) - require.NotEqual(t, "secret", secretValue) - require.True(t, strings.HasPrefix(secretValue, "ENC[")) - - // Change another input, make sure our secret didn't change - st, err = project.Get(ctx, "test") - require.NoError(t, err) - require.NoError(t, st.SetInput("plain", TextInput("different"))) - require.NoError(t, project.Save(ctx, st)) - o = readManifest(st) - require.Contains(t, o.Inputs, "plain") - require.Equal(t, "different", string(*o.Inputs["plain"].Text)) - require.Contains(t, o.Inputs, "secret") - require.Equal(t, secretValue, string(*o.Inputs["secret"].Secret)) -} diff --git a/state/state.go b/state/state.go deleted file mode 100644 index df68d2bd..00000000 --- a/state/state.go +++ /dev/null @@ -1,112 +0,0 @@ -package state - -import ( - "context" - "path" - - "cuelang.org/go/cue" - "go.dagger.io/dagger/compiler" - "go.dagger.io/dagger/pkg" - "go.dagger.io/dagger/plancontext" -) - -// Contents of an environment serialized to a file -type State struct { - // Plan Context. - // FIXME: this is used as a bridge and is temporary. - Context *plancontext.Context `yaml:"-"` - - // State path - Path string `yaml:"-"` - - // Project path - Project string `yaml:"-"` - - // Plan - Plan Plan `yaml:"plan,omitempty"` - - // Human-friendly environment name. - // A environment may have more than one name. - // FIXME: store multiple names? - Name string `yaml:"name,omitempty"` - - // Platform execution - Platform string `yaml:"platform,omitempty"` - - // User Inputs - Inputs map[string]Input `yaml:"inputs,omitempty"` - - // Computed values - Computed string `yaml:"-"` -} - -// Cue module containing the environment plan -func (s *State) CompilePlan(ctx context.Context) (*compiler.Value, error) { - w := s.Project - // FIXME: backward compatibility - if planModule := s.Plan.Module; planModule != "" { - w = path.Join(w, planModule) - } - - // FIXME: universe vendoring - // This is already done on `dagger init` and shouldn't be done here too. - // However: - // 1) As of right now, there's no way to update universe through the - // CLI, so we are lazily updating on `dagger up` using the embedded `universe` - // 2) For backward compatibility: if the project was `dagger - // init`-ed before we added support for vendoring universe, it might not - // contain a `cue.mod`. - if err := pkg.Vendor(ctx, w); err != nil { - return nil, err - } - - var args []string - if pkg := s.Plan.Package; pkg != "" { - args = append(args, pkg) - } - - return compiler.Build(w, nil, args...) -} - -func (s *State) CompileInputs() (*compiler.Value, error) { - v := compiler.NewValue() - - // Prepare inputs - for key, input := range s.Inputs { - i, err := input.Compile(s) - if err != nil { - return nil, err - } - if key == "" { - err = v.FillPath(cue.MakePath(), i) - } else { - err = v.FillPath(cue.ParsePath(key), i) - } - if err != nil { - return nil, err - } - } - - return v, nil -} - -type Plan struct { - Module string `yaml:"module,omitempty"` - Package string `yaml:"package,omitempty"` -} - -func (s *State) SetInput(key string, value Input) error { - if s.Inputs == nil { - s.Inputs = make(map[string]Input) - } - s.Inputs[key] = value - return nil -} - -// Remove all inputs at the given key, including sub-keys. -// For example RemoveInputs("foo.bar") will remove all inputs -// at foo.bar, foo.bar.baz, etc. -func (s *State) RemoveInputs(key string) error { - delete(s.Inputs, key) - return nil -} diff --git a/state/state_test.go b/state/state_test.go deleted file mode 100644 index 7bf2df5b..00000000 --- a/state/state_test.go +++ /dev/null @@ -1 +0,0 @@ -package state