gitflow: multi env support
Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
f0156f449f
commit
90abadf0de
@ -9,13 +9,37 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func GetCurrentEnvironmentState(ctx context.Context) *state.State {
|
||||
func CurrentWorkspace(ctx context.Context) *state.Workspace {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
// If no environment name has been given, look for the current environment
|
||||
environment := viper.GetString("environment")
|
||||
if environment == "" {
|
||||
st, err := state.Current(ctx)
|
||||
if workspacePath := viper.GetString("workspace"); workspacePath != "" {
|
||||
workspace, err := state.Open(ctx, workspacePath)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("path", workspacePath).
|
||||
Msg("failed to open workspace")
|
||||
}
|
||||
return workspace
|
||||
}
|
||||
|
||||
workspace, err := state.Current(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to determine current workspace")
|
||||
}
|
||||
return workspace
|
||||
}
|
||||
|
||||
func CurrentEnvironmentState(ctx context.Context, workspace *state.Workspace) *state.State {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
environmentName := viper.GetString("environment")
|
||||
if environmentName != "" {
|
||||
st, err := workspace.Get(ctx, environmentName)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -25,38 +49,33 @@ func GetCurrentEnvironmentState(ctx context.Context) *state.State {
|
||||
return st
|
||||
}
|
||||
|
||||
// At this point, it must be an environment name
|
||||
workspace := viper.GetString("workspace")
|
||||
var err error
|
||||
if workspace == "" {
|
||||
workspace, err = state.CurrentWorkspace(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to determine current workspace")
|
||||
}
|
||||
}
|
||||
|
||||
environments, err := state.List(ctx, workspace)
|
||||
environments, err := workspace.List(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to list environments")
|
||||
}
|
||||
for _, e := range environments {
|
||||
if e.Name == environment {
|
||||
return e
|
||||
}
|
||||
|
||||
if len(environments) == 0 {
|
||||
lg.
|
||||
Fatal().
|
||||
Msg("no environments")
|
||||
}
|
||||
|
||||
lg.
|
||||
Fatal().
|
||||
Str("environment", environment).
|
||||
Msg("environment not found")
|
||||
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 workspace, select one with `--environment`")
|
||||
}
|
||||
|
||||
return nil
|
||||
return environments[0]
|
||||
}
|
||||
|
||||
// Re-compute an environment (equivalent to `dagger up`).
|
||||
|
@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
@ -24,7 +23,7 @@ var initCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
dir := viper.GetString("environment")
|
||||
dir := viper.GetString("workspace")
|
||||
if dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
@ -36,29 +35,13 @@ var initCmd = &cobra.Command{
|
||||
dir = cwd
|
||||
}
|
||||
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
} else {
|
||||
name = getNewEnvironmentName(dir)
|
||||
}
|
||||
|
||||
_, err := state.Init(ctx, dir, name)
|
||||
_, err := state.Init(ctx, dir)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to initialize")
|
||||
lg.Fatal().Err(err).Msg("failed to initialize workspace")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getNewEnvironmentName(dir string) string {
|
||||
dirName := filepath.Base(dir)
|
||||
if dirName == "/" {
|
||||
return "root"
|
||||
}
|
||||
|
||||
return dirName
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(initCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
|
@ -1,6 +1,10 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
@ -22,7 +26,24 @@ var dirCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateEnvironmentInput(ctx, args[0], state.DirInput(args[1], []string{}))
|
||||
p, err := filepath.Abs(args[1])
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Str("path", args[1]).Msg("unable to resolve path")
|
||||
}
|
||||
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
if !strings.HasPrefix(p, workspace.Path) {
|
||||
lg.Fatal().Err(err).Str("path", args[1]).Msg("dir is outside the workspace")
|
||||
}
|
||||
p, err = filepath.Rel(workspace.Path, p)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Str("path", args[1]).Msg("unable to resolve path")
|
||||
}
|
||||
if !strings.HasPrefix(p, ".") {
|
||||
p = "./" + p
|
||||
}
|
||||
|
||||
updateEnvironmentInput(ctx, args[0], state.DirInput(p, []string{}))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,8 @@ var listCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
environment := common.GetCurrentEnvironmentState(ctx)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
environment := common.CurrentEnvironmentState(ctx, workspace)
|
||||
|
||||
lg = lg.With().
|
||||
Str("environment", environment.Name).
|
||||
|
@ -35,10 +35,11 @@ func init() {
|
||||
func updateEnvironmentInput(ctx context.Context, target string, input state.Input) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
st := common.CurrentEnvironmentState(ctx, workspace)
|
||||
st.SetInput(target, input)
|
||||
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
if err := workspace.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Str("environment", st.Name).Msg("cannot update environment")
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package input
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -23,10 +22,11 @@ var unsetCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
st := common.CurrentEnvironmentState(ctx, workspace)
|
||||
st.RemoveInputs(args[0])
|
||||
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
if err := workspace.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Str("environment", st.Name).Msg("cannot update environment")
|
||||
}
|
||||
lg.Info().Str("environment", st.Name).Msg("updated environment")
|
||||
|
@ -1,8 +1,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
@ -10,9 +8,8 @@ import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -32,21 +29,8 @@ var listCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
var (
|
||||
workspace = viper.GetString("workspace")
|
||||
err error
|
||||
)
|
||||
if workspace == "" {
|
||||
workspace, err = state.CurrentWorkspace(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to determine current workspace")
|
||||
}
|
||||
}
|
||||
|
||||
environments, err := state.List(ctx, workspace)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
environments, err := workspace.List(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -54,34 +38,15 @@ var listCmd = &cobra.Command{
|
||||
Msg("cannot list environments")
|
||||
}
|
||||
|
||||
environmentPath := getCurrentEnvironmentPath(ctx)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
|
||||
defer w.Flush()
|
||||
for _, e := range environments {
|
||||
line := fmt.Sprintf("%s\t%s\t", e.Name, formatPath(e.Path))
|
||||
if e.Path == environmentPath {
|
||||
line = fmt.Sprintf("%s- active environment", line)
|
||||
}
|
||||
fmt.Fprintln(w, line)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getCurrentEnvironmentPath(ctx context.Context) string {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
st, err := state.Current(ctx)
|
||||
if err != nil {
|
||||
// Ignore error if not initialized
|
||||
if errors.Is(err, state.ErrNotInit) {
|
||||
return ""
|
||||
}
|
||||
lg.Fatal().Err(err).Msg("failed to load current environment")
|
||||
}
|
||||
|
||||
return st.Path
|
||||
}
|
||||
|
||||
func formatPath(p string) string {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
|
42
cmd/dagger/cmd/new.go
Normal file
42
cmd/dagger/cmd/new.go
Normal file
@ -0,0 +1,42 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var newCmd = &cobra.Command{
|
||||
Use: "new",
|
||||
Args: cobra.ExactArgs(1),
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
// Fix Viper bug for duplicate flags:
|
||||
// https://github.com/spf13/viper/issues/233
|
||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
|
||||
if viper.GetString("environment") != "" {
|
||||
lg.
|
||||
Fatal().
|
||||
Msg("cannot use option -e,--environment for this command")
|
||||
}
|
||||
name := args[0]
|
||||
if _, err := workspace.Create(ctx, name); err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to create environment")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(newCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -30,7 +30,8 @@ var queryCmd = &cobra.Command{
|
||||
|
||||
cueOpts := parseQueryFlags()
|
||||
|
||||
state := common.GetCurrentEnvironmentState(ctx)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
state := common.CurrentEnvironmentState(ctx, workspace)
|
||||
|
||||
lg = lg.With().
|
||||
Str("environment", state.Name).
|
||||
|
@ -34,6 +34,7 @@ func init() {
|
||||
|
||||
rootCmd.AddCommand(
|
||||
initCmd,
|
||||
newCmd,
|
||||
computeCmd,
|
||||
listCmd,
|
||||
queryCmd,
|
||||
|
@ -3,7 +3,6 @@ package cmd
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -24,11 +23,12 @@ var upCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
workspace := common.CurrentWorkspace(ctx)
|
||||
st := common.CurrentEnvironmentState(ctx, workspace)
|
||||
result := common.EnvironmentUp(ctx, st, viper.GetBool("no-cache"))
|
||||
|
||||
st.Computed = result.Computed().JSON().PrettyString()
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
if err := workspace.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to update environment")
|
||||
}
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
|
||||
@ -90,7 +91,10 @@ func (dir dirInput) Compile(state *State) (*compiler.Value, error) {
|
||||
|
||||
p := dir.Path
|
||||
if !filepath.IsAbs(p) {
|
||||
p = filepath.Clean(path.Join(state.Path, p))
|
||||
p = filepath.Clean(path.Join(state.Workspace, dir.Path))
|
||||
}
|
||||
if !strings.HasPrefix(p, state.Workspace) {
|
||||
return nil, fmt.Errorf("%q is outside the workspace", dir.Path)
|
||||
}
|
||||
|
||||
llb := fmt.Sprintf(
|
||||
|
@ -1,10 +1,15 @@
|
||||
package state
|
||||
|
||||
import "path"
|
||||
|
||||
// Contents of an environment serialized to a file
|
||||
type State struct {
|
||||
// State path
|
||||
Path string `yaml:"-"`
|
||||
|
||||
// Workspace path
|
||||
Workspace string `yaml:"-"`
|
||||
|
||||
// Human-friendly environment name.
|
||||
// A environment may have more than one name.
|
||||
// FIXME: store multiple names?
|
||||
@ -20,7 +25,7 @@ type State struct {
|
||||
// Cue module containing the environment plan
|
||||
// The input's top-level artifact is used as a module directory.
|
||||
func (s *State) PlanSource() Input {
|
||||
return DirInput(s.Path, []string{"*.cue", "cue.mod"})
|
||||
return DirInput(path.Join(s.Path, planDir), []string{"*.cue", "cue.mod"})
|
||||
}
|
||||
|
||||
func (s *State) SetInput(key string, value Input) error {
|
||||
|
@ -7,26 +7,32 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"dagger.io/go/dagger/keychain"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotInit = errors.New("not initialized")
|
||||
ErrAlreadyInit = errors.New("already initialized")
|
||||
ErrNoCurrentWorkspace = errors.New("not in a git directory")
|
||||
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"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context, dir, name string) (*State, error) {
|
||||
type Workspace struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func Init(ctx context.Context, dir string) (*Workspace, error) {
|
||||
root := path.Join(dir, daggerDir)
|
||||
if err := os.Mkdir(root, 0755); err != nil {
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
@ -34,41 +40,31 @@ func Init(ctx context.Context, dir, name string) (*State, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
manifestPath := path.Join(dir, daggerDir, manifestFile)
|
||||
|
||||
st := &State{
|
||||
Path: dir,
|
||||
Name: name,
|
||||
}
|
||||
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(root, ".gitignore"),
|
||||
[]byte("# dagger state\nstate/**\n"),
|
||||
0600,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return st, nil
|
||||
return &Workspace{
|
||||
Path: root,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Current(ctx context.Context) (*State, error) {
|
||||
func Open(ctx context.Context, dir string) (*Workspace, 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 &Workspace{
|
||||
Path: root,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func Current(ctx context.Context) (*Workspace, error) {
|
||||
current, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -90,36 +86,63 @@ func Current(ctx context.Context) (*State, error) {
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
|
||||
func Open(ctx context.Context, dir string) (*State, error) {
|
||||
_, err := os.Stat(path.Join(dir, daggerDir))
|
||||
func (w *Workspace) envPath(name string) string {
|
||||
return path.Join(w.Path, daggerDir, envDir, name)
|
||||
}
|
||||
|
||||
func (w *Workspace) 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 {
|
||||
return nil, err
|
||||
}
|
||||
environments = append(environments, st)
|
||||
}
|
||||
|
||||
return environments, nil
|
||||
}
|
||||
|
||||
func (w *Workspace) 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, ErrNotInit
|
||||
return nil, ErrNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root, err := filepath.Abs(dir)
|
||||
manifest, err := os.ReadFile(path.Join(envPath, manifestFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path.Join(root, daggerDir, manifestFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err = keychain.Decrypt(ctx, data)
|
||||
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(data, &st); err != nil {
|
||||
if err := yaml.Unmarshal(manifest, &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st.Path = root
|
||||
st.Path = envPath
|
||||
st.Workspace = w.Path
|
||||
|
||||
computed, err := os.ReadFile(path.Join(root, daggerDir, stateDir, computedFile))
|
||||
computed, err := os.ReadFile(path.Join(envPath, stateDir, computedFile))
|
||||
if err == nil {
|
||||
st.Computed = string(computed)
|
||||
}
|
||||
@ -127,13 +150,13 @@ func Open(ctx context.Context, dir string) (*State, error) {
|
||||
return &st, nil
|
||||
}
|
||||
|
||||
func Save(ctx context.Context, st *State) error {
|
||||
func (w *Workspace) Save(ctx context.Context, st *State) error {
|
||||
data, err := yaml.Marshal(st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifestPath := path.Join(st.Path, daggerDir, manifestFile)
|
||||
manifestPath := path.Join(st.Path, manifestFile)
|
||||
|
||||
encrypted, err := keychain.Reencrypt(ctx, manifestPath, data)
|
||||
if err != nil {
|
||||
@ -144,7 +167,7 @@ func Save(ctx context.Context, st *State) error {
|
||||
}
|
||||
|
||||
if st.Computed != "" {
|
||||
state := path.Join(st.Path, daggerDir, stateDir)
|
||||
state := path.Join(st.Path, stateDir)
|
||||
if err := os.MkdirAll(state, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -160,74 +183,59 @@ func Save(ctx context.Context, st *State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CurrentWorkspace(ctx context.Context) (string, error) {
|
||||
current, err := os.Getwd()
|
||||
func (w *Workspace) Create(ctx context.Context, name string) (*State, error) {
|
||||
envPath, err := filepath.Abs(w.envPath(name))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Walk every parent directory to find .dagger
|
||||
for {
|
||||
_, err := os.Stat(path.Join(current, ".git"))
|
||||
if err == nil {
|
||||
return current, nil
|
||||
// Environment directory
|
||||
if err := os.MkdirAll(envPath, 0755); err != nil {
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil, ErrExist
|
||||
}
|
||||
parent := filepath.Dir(current)
|
||||
if parent == current {
|
||||
break
|
||||
}
|
||||
current = parent
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return "", ErrNoCurrentWorkspace
|
||||
}
|
||||
// Plan directory
|
||||
if err := os.Mkdir(path.Join(envPath, planDir), 0755); err != nil {
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil, ErrExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func List(ctx context.Context, workspace string) ([]*State, error) {
|
||||
var (
|
||||
environments = []*State{}
|
||||
err error
|
||||
manifestPath := path.Join(envPath, manifestFile)
|
||||
|
||||
st := &State{
|
||||
Path: envPath,
|
||||
Workspace: w.Path,
|
||||
Name: name,
|
||||
}
|
||||
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,
|
||||
)
|
||||
|
||||
workspace, err = filepath.Abs(workspace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = filepath.WalkDir(workspace, func(p string, info os.DirEntry, err error) error {
|
||||
// Ignore errors while we walk
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip regular files
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip non-dagger directories
|
||||
if info.Name() != daggerDir {
|
||||
// Caveat: limit traversal to a depth of 10 (arbitrary)
|
||||
relPath := strings.TrimPrefix(p, workspace)
|
||||
if strings.Count(relPath, string(os.PathSeparator)) > 10 {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
// Otherwise, continue traversing
|
||||
return nil
|
||||
}
|
||||
|
||||
st, err := Open(ctx, filepath.Dir(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
environments = append(environments, st)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return environments, nil
|
||||
return st, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user