gitflow ux
Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
parent
52a1a6bac0
commit
b77d1a1e3e
44
.dagger/env/dev-stdlib/adhoc.cue
vendored
44
.dagger/env/dev-stdlib/adhoc.cue
vendored
@ -1,44 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dagger.io/dagger/op"
|
||||
)
|
||||
|
||||
// Reproduce inline issue.
|
||||
// See https://github.com/dagger/dagger/issues/395
|
||||
test: adhoc: repro395: {
|
||||
good: {
|
||||
// This field is correctly computed because its intermediary pipeline is not inlined.
|
||||
hello: sayHello.message
|
||||
|
||||
// Intermediary pipeline cannot be inlined: it must be visible in a field
|
||||
sayHello: {
|
||||
message: {
|
||||
string
|
||||
#up: [
|
||||
op.#FetchContainer & { ref: "alpine" },
|
||||
op.#Exec & {
|
||||
args: ["sh", "-c", "echo hello > /message"]
|
||||
},
|
||||
op.#Export & { source: "/message", format: "string" },
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
bad: {
|
||||
// This field is NOT correctly computed because its intermediary pipeline is inlined.
|
||||
hello: {
|
||||
message: {
|
||||
string
|
||||
#up: [
|
||||
op.#FetchContainer & { ref: "alpine" },
|
||||
op.#Exec & {
|
||||
args: ["sh", "-c", "echo hello > /message"]
|
||||
},
|
||||
op.#Export & { source: "/message", format: "string" },
|
||||
]
|
||||
}
|
||||
}.message
|
||||
|
||||
}
|
||||
}
|
69
.dagger/env/dev/main.cue
vendored
69
.dagger/env/dev/main.cue
vendored
@ -1,69 +0,0 @@
|
||||
// A dagger workflow to develop dagger
|
||||
package main
|
||||
|
||||
import (
|
||||
"dagger.io/dagger"
|
||||
"dagger.io/os"
|
||||
"dagger.io/alpine"
|
||||
"dagger.io/docker"
|
||||
"dagger.io/go"
|
||||
)
|
||||
|
||||
// Dagger source code
|
||||
source: dagger.#Artifact
|
||||
|
||||
|
||||
test: {
|
||||
// Go unit tests
|
||||
unit: {
|
||||
logs: (os.#File & {
|
||||
from: build.ctr
|
||||
path: "/test.log"
|
||||
read: format: "string"
|
||||
}).read.data
|
||||
}
|
||||
|
||||
// Full suite of bats integration tests
|
||||
integration: {
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
|
||||
// Build the dagger binaries
|
||||
build: {
|
||||
ctr: go.#Container & {
|
||||
"source": source
|
||||
setup: [
|
||||
"apk add --no-cache file",
|
||||
]
|
||||
command: """
|
||||
go test -v ./... > /test.log
|
||||
go build -o /binaries/ ./cmd/... > /build.log
|
||||
"""
|
||||
}
|
||||
|
||||
binaries: docker.#Container & {
|
||||
image: ctr
|
||||
outputDir: "/binaries"
|
||||
}
|
||||
|
||||
logs: (os.#File & {
|
||||
from: ctr
|
||||
path: "/build.log"
|
||||
read: format: "string"
|
||||
}).read.data
|
||||
}
|
||||
|
||||
|
||||
// Execute `dagger help`
|
||||
usage: docker.#Container & {
|
||||
image: alpine.#Image
|
||||
|
||||
command: "dagger help"
|
||||
|
||||
volume: binaries: {
|
||||
from: build.binaries
|
||||
dest: "/usr/local/dagger/bin/"
|
||||
}
|
||||
shell: search: "/usr/local/dagger/bin": true
|
||||
}
|
1
.dagger/env/hello-world
vendored
1
.dagger/env/hello-world
vendored
@ -1 +0,0 @@
|
||||
../../examples/hello-world
|
21
.dagger/env/sandbox/sandbox.cue
vendored
21
.dagger/env/sandbox/sandbox.cue
vendored
@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dagger.io/docker"
|
||||
"dagger.io/os"
|
||||
)
|
||||
|
||||
let ctr = docker.#Container & {
|
||||
command: "echo 'hello world!' > /etc/motd"
|
||||
}
|
||||
|
||||
motd: (os.#File & {
|
||||
from: ctr
|
||||
path: "/etc/motd"
|
||||
read: format: "string"
|
||||
}).read.data
|
||||
|
||||
etc: (os.#Dir & {
|
||||
from: ctr
|
||||
path: "/etc"
|
||||
}).read.tree
|
@ -2,65 +2,79 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func GetCurrentEnvironmentState(ctx context.Context, store *dagger.Store) *dagger.EnvironmentState {
|
||||
func GetCurrentEnvironmentState(ctx context.Context) *state.State {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
environmentName := viper.GetString("environment")
|
||||
if environmentName != "" {
|
||||
st, err := store.LookupEnvironmentByName(ctx, environmentName)
|
||||
// 1) If no environment name has been given, look for the current environment
|
||||
environment := viper.GetString("environment")
|
||||
if environment == "" {
|
||||
st, err := state.Current(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("environmentName", environmentName).
|
||||
Msg("failed to lookup environment by name")
|
||||
Msg("failed to load environment")
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("cannot get current working directory")
|
||||
// 2) Check if it's an environment path (can be opened directly)
|
||||
st, err := state.Open(ctx, environment)
|
||||
if err == nil {
|
||||
return st
|
||||
}
|
||||
st, err := store.LookupEnvironmentByPath(ctx, wd)
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("environmentPath", environment).
|
||||
Msg("failed to load environment")
|
||||
}
|
||||
|
||||
// At this point, it must be an environment name
|
||||
workspace := viper.GetString("workspace")
|
||||
if workspace == "" {
|
||||
workspace, err = state.CurrentWorkspace(ctx)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("environmentPath", wd).
|
||||
Msg("failed to lookup environment by path")
|
||||
Msg("failed to determine current workspace")
|
||||
}
|
||||
if len(st) == 0 {
|
||||
}
|
||||
|
||||
environments, err := state.List(ctx, workspace)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("environmentPath", wd).
|
||||
Msg("no environments match the current directory")
|
||||
Msg("failed to list environments")
|
||||
}
|
||||
if len(st) > 1 {
|
||||
environments := []string{}
|
||||
for _, s := range st {
|
||||
environments = append(environments, s.Name)
|
||||
for _, e := range environments {
|
||||
if e.Name == environment {
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Str("environmentPath", wd).
|
||||
Strs("environments", environments).
|
||||
Msg("multiple environments match the current directory, select one with `--environment`")
|
||||
}
|
||||
return st[0]
|
||||
Str("environment", environment).
|
||||
Msg("environment not found")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Re-compute an environment (equivalent to `dagger up`).
|
||||
func EnvironmentUp(ctx context.Context, state *dagger.EnvironmentState, noCache bool) *dagger.Environment {
|
||||
func EnvironmentUp(ctx context.Context, state *state.State, noCache bool) *dagger.Environment {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
c, err := dagger.NewClient(ctx, "", noCache)
|
||||
|
@ -10,12 +10,11 @@ import (
|
||||
"cuelang.org/go/cue"
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/compiler"
|
||||
"dagger.io/go/dagger/state"
|
||||
"go.mozilla.org/sops/v3"
|
||||
"go.mozilla.org/sops/v3/decrypt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -36,16 +35,15 @@ var computeCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
st := &dagger.EnvironmentState{
|
||||
ID: uuid.New().String(),
|
||||
st := &state.State{
|
||||
Name: "FIXME",
|
||||
PlanSource: dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}),
|
||||
Path: args[0],
|
||||
}
|
||||
|
||||
for _, input := range viper.GetStringSlice("input-string") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.SetInput(k, dagger.TextInput(v))
|
||||
err := st.SetInput(k, state.TextInput(v))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -58,7 +56,7 @@ var computeCmd = &cobra.Command{
|
||||
for _, input := range viper.GetStringSlice("input-dir") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.SetInput(k, dagger.DirInput(v, []string{}))
|
||||
err := st.SetInput(k, state.DirInput(v, []string{}))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -71,7 +69,7 @@ var computeCmd = &cobra.Command{
|
||||
for _, input := range viper.GetStringSlice("input-git") {
|
||||
parts := strings.SplitN(input, "=", 2)
|
||||
k, v := parts[0], parts[1]
|
||||
err := st.SetInput(k, dagger.GitInput(v, "", ""))
|
||||
err := st.SetInput(k, state.GitInput(v, "", ""))
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -102,7 +100,7 @@ var computeCmd = &cobra.Command{
|
||||
lg.Fatal().Msg("invalid json")
|
||||
}
|
||||
|
||||
err = st.SetInput("", dagger.JSONInput(string(content)))
|
||||
err = st.SetInput("", state.JSONInput(string(content)))
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to add input")
|
||||
}
|
||||
@ -125,7 +123,7 @@ var computeCmd = &cobra.Command{
|
||||
content = plaintext
|
||||
}
|
||||
|
||||
err = st.SetInput("", dagger.YAMLInput(string(content)))
|
||||
err = st.SetInput("", state.YAMLInput(string(content)))
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to add input")
|
||||
}
|
||||
@ -143,7 +141,7 @@ var computeCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
err = st.SetInput(k, dagger.FileInput(v))
|
||||
err = st.SetInput(k, state.FileInput(v))
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to set input string")
|
||||
}
|
||||
|
66
cmd/dagger/cmd/init.go
Normal file
66
cmd/dagger/cmd/init.go
Normal file
@ -0,0 +1,66 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Args: cobra.MaximumNArgs(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())
|
||||
|
||||
dir := viper.GetString("environment")
|
||||
if dir == "" {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to get current working dir")
|
||||
}
|
||||
dir = cwd
|
||||
}
|
||||
|
||||
var name string
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
} else {
|
||||
name = getNewEnvironmentName(dir)
|
||||
}
|
||||
|
||||
_, err := state.Init(ctx, dir, name)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to initialize")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -22,7 +22,7 @@ var containerCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateEnvironmentInput(ctx, args[0], dagger.DockerInput(args[1]))
|
||||
updateEnvironmentInput(ctx, args[0], state.DockerInput(args[1]))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -22,7 +22,7 @@ var dirCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
updateEnvironmentInput(ctx, args[0], dagger.DirInput(args[1], []string{}))
|
||||
updateEnvironmentInput(ctx, args[0], state.DirInput(args[1], []string{}))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -32,7 +32,7 @@ var gitCmd = &cobra.Command{
|
||||
subDir = args[3]
|
||||
}
|
||||
|
||||
updateEnvironmentInput(ctx, args[0], dagger.GitInput(args[1], ref, subDir))
|
||||
updateEnvironmentInput(ctx, args[0], state.GitInput(args[1], ref, subDir))
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -25,7 +25,7 @@ var jsonCmd = &cobra.Command{
|
||||
updateEnvironmentInput(
|
||||
ctx,
|
||||
args[0],
|
||||
dagger.JSONInput(readInput(ctx, args[1])),
|
||||
state.JSONInput(readInput(ctx, args[1])),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -30,16 +30,10 @@ var listCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
environment := common.GetCurrentEnvironmentState(ctx, store)
|
||||
environment := common.GetCurrentEnvironmentState(ctx)
|
||||
|
||||
lg = lg.With().
|
||||
Str("environmentName", environment.Name).
|
||||
Str("environmentId", environment.ID).
|
||||
Str("environment", environment.Name).
|
||||
Logger()
|
||||
|
||||
c, err := dagger.NewClient(ctx, "", false)
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -32,21 +32,15 @@ func init() {
|
||||
)
|
||||
}
|
||||
|
||||
func updateEnvironmentInput(ctx context.Context, target string, input dagger.Input) {
|
||||
func updateEnvironmentInput(ctx context.Context, target string, input state.Input) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx, store)
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
st.SetInput(target, input)
|
||||
|
||||
if err := store.UpdateEnvironment(ctx, st, nil); err != nil {
|
||||
lg.Fatal().Err(err).Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("cannot update environment")
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Str("environment", st.Name).Msg("cannot update environment")
|
||||
}
|
||||
lg.Info().Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("updated environment")
|
||||
}
|
||||
|
||||
func readInput(ctx context.Context, source string) string {
|
||||
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -25,7 +25,7 @@ var textCmd = &cobra.Command{
|
||||
updateEnvironmentInput(
|
||||
ctx,
|
||||
args[0],
|
||||
dagger.TextInput(readInput(ctx, args[1])),
|
||||
state.TextInput(readInput(ctx, args[1])),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package input
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -23,17 +23,12 @@ var unsetCmd = &cobra.Command{
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx, store)
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
st.RemoveInputs(args[0])
|
||||
|
||||
if err := store.UpdateEnvironment(ctx, st, nil); err != nil {
|
||||
lg.Fatal().Err(err).Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("cannot update environment")
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Str("environment", st.Name).Msg("cannot update environment")
|
||||
}
|
||||
lg.Info().Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("updated environment")
|
||||
lg.Info().Str("environment", st.Name).Msg("updated environment")
|
||||
},
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package input
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -25,7 +25,7 @@ var yamlCmd = &cobra.Command{
|
||||
updateEnvironmentInput(
|
||||
ctx,
|
||||
args[0],
|
||||
dagger.YAMLInput(readInput(ctx, args[1])),
|
||||
state.YAMLInput(readInput(ctx, args[1])),
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -30,12 +31,22 @@ var listCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
store, err := dagger.DefaultStore()
|
||||
|
||||
var (
|
||||
workspace = viper.GetString("workspace")
|
||||
err error
|
||||
)
|
||||
if workspace == "" {
|
||||
workspace, err = state.CurrentWorkspace(ctx)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to determine current workspace")
|
||||
}
|
||||
}
|
||||
|
||||
environments, err := store.ListEnvironments(ctx)
|
||||
environments, err := state.List(ctx, workspace)
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
@ -43,45 +54,32 @@ var listCmd = &cobra.Command{
|
||||
Msg("cannot list environments")
|
||||
}
|
||||
|
||||
environmentID := getCurrentEnvironmentID(ctx, store)
|
||||
environmentPath := getCurrentEnvironmentPath(ctx)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
|
||||
for _, r := range environments {
|
||||
line := fmt.Sprintf("%s\t%s\t", r.Name, formatPlanSource(r.PlanSource))
|
||||
if r.ID == environmentID {
|
||||
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)
|
||||
}
|
||||
w.Flush()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(listCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getCurrentEnvironmentID(ctx context.Context, store *dagger.Store) string {
|
||||
func getCurrentEnvironmentPath(ctx context.Context) string {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
wd, err := os.Getwd()
|
||||
st, err := state.Current(ctx)
|
||||
if err != nil {
|
||||
lg.Warn().Err(err).Msg("cannot get current working directory")
|
||||
// Ignore error if not initialized
|
||||
if errors.Is(err, state.ErrNotInit) {
|
||||
return ""
|
||||
}
|
||||
|
||||
st, err := store.LookupEnvironmentByPath(ctx, wd)
|
||||
if err != nil {
|
||||
// Ignore error
|
||||
return ""
|
||||
lg.Fatal().Err(err).Msg("failed to load current environment")
|
||||
}
|
||||
|
||||
if len(st) == 1 {
|
||||
return st[0].ID
|
||||
}
|
||||
|
||||
return ""
|
||||
return st.Path
|
||||
}
|
||||
|
||||
func formatPath(p string) string {
|
||||
@ -99,15 +97,8 @@ func formatPath(p string) string {
|
||||
return p
|
||||
}
|
||||
|
||||
func formatPlanSource(i dagger.Input) string {
|
||||
switch i.Type {
|
||||
case dagger.InputTypeDir:
|
||||
return formatPath(i.Dir.Path)
|
||||
case dagger.InputTypeGit:
|
||||
return i.Git.Remote
|
||||
case dagger.InputTypeDocker:
|
||||
return i.Docker.Ref
|
||||
func init() {
|
||||
if err := viper.BindPFlags(listCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return "no plan"
|
||||
}
|
||||
|
@ -1,148 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var newCmd = &cobra.Command{
|
||||
Use: "new",
|
||||
Short: "Create a new environment",
|
||||
Args: cobra.MaximumNArgs(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())
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
if viper.GetString("environment") != "" {
|
||||
lg.
|
||||
Fatal().
|
||||
Msg("cannot use option -d,--environment for this command")
|
||||
}
|
||||
|
||||
name := ""
|
||||
if len(args) > 0 {
|
||||
name = args[0]
|
||||
} else {
|
||||
name = getNewEnvironmentName(ctx)
|
||||
}
|
||||
|
||||
st := &dagger.EnvironmentState{
|
||||
Name: name,
|
||||
PlanSource: getPlanSource(ctx),
|
||||
}
|
||||
|
||||
err = store.CreateEnvironment(ctx, st)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to create environment")
|
||||
}
|
||||
lg.
|
||||
Info().
|
||||
Str("environmentId", st.ID).
|
||||
Str("environmentName", st.Name).
|
||||
Msg("environment created")
|
||||
|
||||
if viper.GetBool("up") {
|
||||
common.EnvironmentUp(ctx, st, false)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getNewEnvironmentName(ctx context.Context) string {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
workDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.
|
||||
Fatal().
|
||||
Err(err).
|
||||
Msg("failed to get current working dir")
|
||||
}
|
||||
|
||||
currentDir := filepath.Base(workDir)
|
||||
if currentDir == "/" {
|
||||
return "root"
|
||||
}
|
||||
|
||||
return currentDir
|
||||
}
|
||||
|
||||
func getPlanSource(ctx context.Context) dagger.Input {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
src := dagger.Input{}
|
||||
checkFirstSet := func() {
|
||||
if src.Type != dagger.InputTypeEmpty {
|
||||
lg.Fatal().Msg("only one of those options can be set: --plan-dir, --plan-git, --plan-package, --plan-file")
|
||||
}
|
||||
}
|
||||
|
||||
planDir := viper.GetString("plan-dir")
|
||||
planGit := viper.GetString("plan-git")
|
||||
|
||||
if planDir != "" {
|
||||
checkFirstSet()
|
||||
|
||||
src = dagger.DirInput(planDir, []string{"*.cue", "cue.mod"})
|
||||
}
|
||||
|
||||
if planGit != "" {
|
||||
checkFirstSet()
|
||||
|
||||
u, err := url.Parse(planGit)
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Str("url", planGit).Msg("cannot get current working directory")
|
||||
}
|
||||
ref := u.Fragment // eg. #main
|
||||
u.Fragment = ""
|
||||
remote := u.String()
|
||||
|
||||
src = dagger.GitInput(remote, ref, "")
|
||||
}
|
||||
|
||||
if src.Type == dagger.InputTypeEmpty {
|
||||
var err error
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("cannot get current working directory")
|
||||
}
|
||||
return dagger.DirInput(wd, []string{"*.cue", "cue.mod"})
|
||||
}
|
||||
|
||||
return src
|
||||
}
|
||||
|
||||
func init() {
|
||||
newCmd.Flags().BoolP("up", "u", false, "Bring the environment online")
|
||||
|
||||
newCmd.Flags().String("plan-dir", "", "Load plan from a local directory")
|
||||
newCmd.Flags().String("plan-git", "", "Load plan from a git repository")
|
||||
newCmd.Flags().String("plan-package", "", "Load plan from a cue package")
|
||||
newCmd.Flags().String("plan-file", "", "Load plan from a cue or json file")
|
||||
|
||||
newCmd.Flags().String("setup", "auto", "Specify whether to prompt user for initial setup (no|yes|auto)")
|
||||
|
||||
if err := viper.BindPFlags(newCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
package plan
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var dirCmd = &cobra.Command{
|
||||
Use: "dir PATH",
|
||||
Short: "Load plan from a local directory",
|
||||
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())
|
||||
|
||||
updateEnvironmentPlan(ctx, dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(dirCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package plan
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var fileCmd = &cobra.Command{
|
||||
Use: "file PATH|-",
|
||||
Short: "Load plan from a cue file",
|
||||
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())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(fileCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package plan
|
||||
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var gitCmd = &cobra.Command{
|
||||
Use: "git REMOTE [REF] [SUBDIR]",
|
||||
Short: "Load plan from a git package",
|
||||
Args: cobra.RangeArgs(1, 3),
|
||||
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())
|
||||
|
||||
ref := "HEAD"
|
||||
if len(args) > 1 {
|
||||
ref = args[1]
|
||||
}
|
||||
|
||||
subDir := ""
|
||||
if len(args) > 2 {
|
||||
subDir = args[2]
|
||||
}
|
||||
|
||||
updateEnvironmentPlan(ctx, dagger.GitInput(args[0], ref, subDir))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(gitCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package plan
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var packageCmd = &cobra.Command{
|
||||
Use: "package PKG",
|
||||
Short: "Load plan from a cue package",
|
||||
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())
|
||||
|
||||
panic("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if err := viper.BindPFlags(packageCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package plan
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/dagger"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Cmd exposes the top-level command
|
||||
var Cmd = &cobra.Command{
|
||||
Use: "plan",
|
||||
Short: "Manage an environment's plan",
|
||||
}
|
||||
|
||||
func init() {
|
||||
Cmd.AddCommand(
|
||||
packageCmd,
|
||||
dirCmd,
|
||||
gitCmd,
|
||||
fileCmd,
|
||||
)
|
||||
}
|
||||
|
||||
func updateEnvironmentPlan(ctx context.Context, planSource dagger.Input) {
|
||||
lg := log.Ctx(ctx)
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
st := common.GetCurrentEnvironmentState(ctx, store)
|
||||
st.PlanSource = planSource
|
||||
|
||||
if err := store.UpdateEnvironment(ctx, st, nil); err != nil {
|
||||
lg.Fatal().Err(err).Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("cannot update environment")
|
||||
}
|
||||
lg.Info().Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("updated environment")
|
||||
}
|
@ -30,16 +30,10 @@ var queryCmd = &cobra.Command{
|
||||
|
||||
cueOpts := parseQueryFlags()
|
||||
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
state := common.GetCurrentEnvironmentState(ctx, store)
|
||||
state := common.GetCurrentEnvironmentState(ctx)
|
||||
|
||||
lg = lg.With().
|
||||
Str("environmentName", state.Name).
|
||||
Str("environmentId", state.ID).
|
||||
Str("environment", state.Name).
|
||||
Logger()
|
||||
|
||||
cuePath := cue.MakePath()
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
|
||||
"dagger.io/go/cmd/dagger/cmd/input"
|
||||
"dagger.io/go/cmd/dagger/cmd/output"
|
||||
"dagger.io/go/cmd/dagger/cmd/plan"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"github.com/moby/buildkit/util/appcontext"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@ -24,6 +23,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().String("log-format", "", "Log format (json, pretty). Defaults to json if the terminal is not a tty")
|
||||
rootCmd.PersistentFlags().StringP("log-level", "l", "info", "Log level")
|
||||
rootCmd.PersistentFlags().StringP("environment", "e", "", "Select an environment")
|
||||
rootCmd.PersistentFlags().StringP("workspace", "w", "", "Specify a workspace (defaults to current git repository)")
|
||||
|
||||
rootCmd.PersistentPreRun = func(*cobra.Command, []string) {
|
||||
go checkVersion()
|
||||
@ -33,8 +33,8 @@ func init() {
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(
|
||||
initCmd,
|
||||
computeCmd,
|
||||
newCmd,
|
||||
listCmd,
|
||||
queryCmd,
|
||||
upCmd,
|
||||
@ -43,7 +43,6 @@ func init() {
|
||||
historyCmd,
|
||||
loginCmd,
|
||||
logoutCmd,
|
||||
plan.Cmd,
|
||||
input.Cmd,
|
||||
output.Cmd,
|
||||
versionCmd,
|
||||
|
@ -3,7 +3,7 @@ package cmd
|
||||
import (
|
||||
"dagger.io/go/cmd/dagger/cmd/common"
|
||||
"dagger.io/go/cmd/dagger/logger"
|
||||
"dagger.io/go/dagger"
|
||||
"dagger.io/go/dagger/state"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@ -23,15 +23,12 @@ var upCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
lg := logger.New()
|
||||
ctx := lg.WithContext(cmd.Context())
|
||||
store, err := dagger.DefaultStore()
|
||||
if err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to load store")
|
||||
}
|
||||
|
||||
state := common.GetCurrentEnvironmentState(ctx, store)
|
||||
result := common.EnvironmentUp(ctx, state, viper.GetBool("no-cache"))
|
||||
state.Computed = result.Computed().JSON().String()
|
||||
if err := store.UpdateEnvironment(ctx, state, nil); err != nil {
|
||||
st := common.GetCurrentEnvironmentState(ctx)
|
||||
result := common.EnvironmentUp(ctx, st, viper.GetBool("no-cache"))
|
||||
|
||||
st.Computed = result.Computed().JSON().PrettyString()
|
||||
if err := state.Save(ctx, st); err != nil {
|
||||
lg.Fatal().Err(err).Msg("failed to update environment")
|
||||
}
|
||||
},
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"dagger.io/go/pkg/progressui"
|
||||
|
||||
"dagger.io/go/dagger/compiler"
|
||||
"dagger.io/go/dagger/state"
|
||||
)
|
||||
|
||||
// A dagger client
|
||||
@ -63,7 +64,7 @@ func NewClient(ctx context.Context, host string, noCache bool) (*Client, error)
|
||||
type ClientDoFunc func(context.Context, *Environment, Solver) error
|
||||
|
||||
// FIXME: return completed *Route, instead of *compiler.Value
|
||||
func (c *Client) Do(ctx context.Context, state *EnvironmentState, fn ClientDoFunc) (*Environment, error) {
|
||||
func (c *Client) Do(ctx context.Context, state *state.State, fn ClientDoFunc) (*Environment, error) {
|
||||
lg := log.Ctx(ctx)
|
||||
eg, gctx := errgroup.WithContext(ctx)
|
||||
|
||||
|
@ -112,5 +112,5 @@ func (s JSON) PrettyString() string {
|
||||
if err := json.Indent(b, []byte(raw), "", " "); err != nil {
|
||||
return raw
|
||||
}
|
||||
return b.String()
|
||||
return fmt.Sprintf("%s\n", b.String())
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"cuelang.org/go/cue"
|
||||
cueflow "cuelang.org/go/tools/flow"
|
||||
"dagger.io/go/dagger/compiler"
|
||||
"dagger.io/go/dagger/state"
|
||||
"dagger.io/go/pkg/cuetils"
|
||||
"dagger.io/go/stdlib"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
@ -19,7 +21,7 @@ import (
|
||||
)
|
||||
|
||||
type Environment struct {
|
||||
state *EnvironmentState
|
||||
state *state.State
|
||||
|
||||
// Layer 1: plan configuration
|
||||
plan *compiler.Value
|
||||
@ -31,7 +33,7 @@ type Environment struct {
|
||||
computed *compiler.Value
|
||||
}
|
||||
|
||||
func NewEnvironment(st *EnvironmentState) (*Environment, error) {
|
||||
func NewEnvironment(st *state.State) (*Environment, error) {
|
||||
e := &Environment{
|
||||
state: st,
|
||||
|
||||
@ -42,7 +44,7 @@ func NewEnvironment(st *EnvironmentState) (*Environment, error) {
|
||||
|
||||
// Prepare inputs
|
||||
for _, input := range st.Inputs {
|
||||
v, err := input.Value.Compile()
|
||||
v, err := input.Value.Compile(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -59,16 +61,12 @@ func NewEnvironment(st *EnvironmentState) (*Environment, error) {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (e *Environment) ID() string {
|
||||
return e.state.ID
|
||||
}
|
||||
|
||||
func (e *Environment) Name() string {
|
||||
return e.state.Name
|
||||
}
|
||||
|
||||
func (e *Environment) PlanSource() Input {
|
||||
return e.state.PlanSource
|
||||
func (e *Environment) PlanSource() state.Input {
|
||||
return e.state.PlanSource()
|
||||
}
|
||||
|
||||
func (e *Environment) Plan() *compiler.Value {
|
||||
@ -88,7 +86,7 @@ func (e *Environment) LoadPlan(ctx context.Context, s Solver) error {
|
||||
span, ctx := opentracing.StartSpanFromContext(ctx, "environment.LoadPlan")
|
||||
defer span.Finish()
|
||||
|
||||
planSource, err := e.state.PlanSource.Compile()
|
||||
planSource, err := e.state.PlanSource().Compile(e.state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -159,7 +157,7 @@ func (e *Environment) LocalDirs() map[string]string {
|
||||
}
|
||||
|
||||
// 2. Scan the plan
|
||||
plan, err := e.state.PlanSource.Compile()
|
||||
plan, err := e.state.PlanSource().Compile(e.state)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -3,14 +3,15 @@ package dagger
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dagger.io/go/dagger/state"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInputDir(t *testing.T) {
|
||||
st := &EnvironmentState{
|
||||
PlanSource: DirInput("/tmp/source", []string{}),
|
||||
func TestLocalDirs(t *testing.T) {
|
||||
st := &state.State{
|
||||
Path: "/tmp/source",
|
||||
}
|
||||
require.NoError(t, st.SetInput("www.source", DirInput("/", []string{})))
|
||||
require.NoError(t, st.SetInput("www.source", state.DirInput("/", []string{})))
|
||||
|
||||
environment, err := NewEnvironment(st)
|
||||
require.NoError(t, err)
|
@ -1,9 +1,10 @@
|
||||
package dagger
|
||||
package state
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
@ -37,33 +38,33 @@ const (
|
||||
)
|
||||
|
||||
type Input struct {
|
||||
Type InputType `json:"type,omitempty"`
|
||||
Type InputType `yaml:"type,omitempty"`
|
||||
|
||||
Dir *dirInput `json:"dir,omitempty"`
|
||||
Git *gitInput `json:"git,omitempty"`
|
||||
Docker *dockerInput `json:"docker,omitempty"`
|
||||
Text *textInput `json:"text,omitempty"`
|
||||
JSON *jsonInput `json:"json,omitempty"`
|
||||
YAML *yamlInput `json:"yaml,omitempty"`
|
||||
File *fileInput `json:"file,omitempty"`
|
||||
Dir *dirInput `yaml:"dir,omitempty"`
|
||||
Git *gitInput `yaml:"git,omitempty"`
|
||||
Docker *dockerInput `yaml:"docker,omitempty"`
|
||||
Text *textInput `yaml:"text,omitempty"`
|
||||
JSON *jsonInput `yaml:"json,omitempty"`
|
||||
YAML *yamlInput `yaml:"yaml,omitempty"`
|
||||
File *fileInput `yaml:"file,omitempty"`
|
||||
}
|
||||
|
||||
func (i Input) Compile() (*compiler.Value, error) {
|
||||
func (i Input) Compile(state *State) (*compiler.Value, error) {
|
||||
switch i.Type {
|
||||
case InputTypeDir:
|
||||
return i.Dir.Compile()
|
||||
return i.Dir.Compile(state)
|
||||
case InputTypeGit:
|
||||
return i.Git.Compile()
|
||||
return i.Git.Compile(state)
|
||||
case InputTypeDocker:
|
||||
return i.Docker.Compile()
|
||||
return i.Docker.Compile(state)
|
||||
case InputTypeText:
|
||||
return i.Text.Compile()
|
||||
return i.Text.Compile(state)
|
||||
case InputTypeJSON:
|
||||
return i.JSON.Compile()
|
||||
return i.JSON.Compile(state)
|
||||
case InputTypeYAML:
|
||||
return i.YAML.Compile()
|
||||
return i.YAML.Compile(state)
|
||||
case InputTypeFile:
|
||||
return i.File.Compile()
|
||||
return i.File.Compile(state)
|
||||
case "":
|
||||
return nil, fmt.Errorf("input has not been set")
|
||||
default:
|
||||
@ -73,12 +74,6 @@ func (i Input) Compile() (*compiler.Value, error) {
|
||||
|
||||
// An input artifact loaded from a local directory
|
||||
func DirInput(path string, include []string) Input {
|
||||
// resolve absolute path
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return Input{
|
||||
Type: InputTypeDir,
|
||||
Dir: &dirInput{
|
||||
@ -93,7 +88,7 @@ type dirInput struct {
|
||||
Include []string `json:"include,omitempty"`
|
||||
}
|
||||
|
||||
func (dir dirInput) Compile() (*compiler.Value, error) {
|
||||
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
|
||||
@ -106,9 +101,15 @@ func (dir dirInput) Compile() (*compiler.Value, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
p := dir.Path
|
||||
if !filepath.IsAbs(p) {
|
||||
p = filepath.Clean(path.Join(state.Path, p))
|
||||
}
|
||||
|
||||
llb := fmt.Sprintf(
|
||||
`#up: [{do:"local",dir:"%s", include:%s}]`,
|
||||
dir.Path,
|
||||
p,
|
||||
includeLLB,
|
||||
)
|
||||
return compiler.Compile("", llb)
|
||||
@ -132,7 +133,7 @@ func GitInput(remote, ref, dir string) Input {
|
||||
}
|
||||
}
|
||||
|
||||
func (git gitInput) Compile() (*compiler.Value, error) {
|
||||
func (git gitInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
ref := "HEAD"
|
||||
if git.Ref != "" {
|
||||
ref = git.Ref
|
||||
@ -159,7 +160,7 @@ type dockerInput struct {
|
||||
Ref string `json:"ref,omitempty"`
|
||||
}
|
||||
|
||||
func (i dockerInput) Compile() (*compiler.Value, error) {
|
||||
func (i dockerInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
panic("NOT IMPLEMENTED")
|
||||
}
|
||||
|
||||
@ -177,7 +178,7 @@ type textInput struct {
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (i textInput) Compile() (*compiler.Value, error) {
|
||||
func (i textInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
return compiler.Compile("", fmt.Sprintf("%q", i.Data))
|
||||
}
|
||||
|
||||
@ -196,7 +197,7 @@ type jsonInput struct {
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (i jsonInput) Compile() (*compiler.Value, error) {
|
||||
func (i jsonInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
return compiler.DecodeJSON("", []byte(i.Data))
|
||||
}
|
||||
|
||||
@ -215,7 +216,7 @@ type yamlInput struct {
|
||||
Data string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (i yamlInput) Compile() (*compiler.Value, error) {
|
||||
func (i yamlInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
return compiler.DecodeYAML("", []byte(i.Data))
|
||||
}
|
||||
|
||||
@ -232,7 +233,7 @@ type fileInput struct {
|
||||
Path string `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (i fileInput) Compile() (*compiler.Value, error) {
|
||||
func (i fileInput) Compile(_ *State) (*compiler.Value, error) {
|
||||
data, err := ioutil.ReadFile(i.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
@ -1,32 +1,34 @@
|
||||
package dagger
|
||||
package state
|
||||
|
||||
// Contents of an environment serialized to a file
|
||||
type EnvironmentState struct {
|
||||
// Globally unique environment ID
|
||||
ID string `json:"id,omitempty"`
|
||||
type State struct {
|
||||
// State path
|
||||
Path string `yaml:"-"`
|
||||
|
||||
// Human-friendly environment name.
|
||||
// A environment may have more than one name.
|
||||
// FIXME: store multiple names?
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Cue module containing the environment plan
|
||||
// The input's top-level artifact is used as a module directory.
|
||||
PlanSource Input `json:"plan,omitempty"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
|
||||
// User Inputs
|
||||
Inputs []inputKV `json:"inputs,omitempty"`
|
||||
Inputs []inputKV `yaml:"inputs,omitempty"`
|
||||
|
||||
// Computed values
|
||||
Computed string `json:"output,omitempty"`
|
||||
Computed string `yaml:"-"`
|
||||
}
|
||||
|
||||
type inputKV struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Value Input `json:"value,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
Value Input `yaml:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (s *EnvironmentState) SetInput(key string, value Input) error {
|
||||
// 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"})
|
||||
}
|
||||
|
||||
func (s *State) SetInput(key string, value Input) error {
|
||||
for i, inp := range s.Inputs {
|
||||
if inp.Key != key {
|
||||
continue
|
||||
@ -42,7 +44,7 @@ func (s *EnvironmentState) SetInput(key string, value Input) error {
|
||||
// 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 *EnvironmentState) RemoveInputs(key string) error {
|
||||
func (s *State) RemoveInputs(key string) error {
|
||||
newInputs := make([]inputKV, 0, len(s.Inputs))
|
||||
for _, i := range s.Inputs {
|
||||
if i.Key == key {
|
205
dagger/state/store.go
Normal file
205
dagger/state/store.go
Normal file
@ -0,0 +1,205 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotInit = errors.New("not initialized")
|
||||
ErrAlreadyInit = errors.New("already initialized")
|
||||
ErrNoCurrentWorkspace = errors.New("not in a git directory")
|
||||
)
|
||||
|
||||
const (
|
||||
daggerDir = ".dagger"
|
||||
stateDir = "state"
|
||||
manifestFile = "values.yaml"
|
||||
computedFile = "computed.json"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context, dir, name string) (*State, error) {
|
||||
root := path.Join(dir, daggerDir)
|
||||
err := os.Mkdir(root, 0755)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil, ErrAlreadyInit
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = os.WriteFile(
|
||||
path.Join(root, ".gitignore"),
|
||||
[]byte("# dagger state\nstate/**\n"),
|
||||
0600,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
st := &State{
|
||||
Path: dir,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
return st, Save(ctx, st)
|
||||
}
|
||||
|
||||
func Current(ctx context.Context) (*State, 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))
|
||||
if err == nil {
|
||||
return Open(ctx, current)
|
||||
}
|
||||
parent := filepath.Dir(current)
|
||||
if parent == current {
|
||||
break
|
||||
}
|
||||
current = parent
|
||||
}
|
||||
|
||||
return nil, ErrNotInit
|
||||
}
|
||||
|
||||
func Open(ctx context.Context, dir string) (*State, 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
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path.Join(root, daggerDir, manifestFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var st State
|
||||
if err := yaml.Unmarshal(data, &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st.Path = root
|
||||
|
||||
computed, err := os.ReadFile(path.Join(root, daggerDir, stateDir, computedFile))
|
||||
if err == nil {
|
||||
st.Computed = string(computed)
|
||||
}
|
||||
|
||||
return &st, nil
|
||||
}
|
||||
|
||||
func Save(ctx context.Context, st *State) error {
|
||||
data, err := yaml.Marshal(st)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path.Join(st.Path, daggerDir, manifestFile), data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if st.Computed != "" {
|
||||
state := path.Join(st.Path, daggerDir, 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 CurrentWorkspace(ctx context.Context) (string, error) {
|
||||
current, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Walk every parent directory to find .dagger
|
||||
for {
|
||||
_, err := os.Stat(path.Join(current, ".git"))
|
||||
if err == nil {
|
||||
return current, nil
|
||||
}
|
||||
parent := filepath.Dir(current)
|
||||
if parent == current {
|
||||
break
|
||||
}
|
||||
current = parent
|
||||
}
|
||||
|
||||
return "", ErrNoCurrentWorkspace
|
||||
}
|
||||
|
||||
func List(ctx context.Context, workspace string) ([]*State, error) {
|
||||
var (
|
||||
environments = []*State{}
|
||||
err error
|
||||
)
|
||||
|
||||
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
|
||||
}
|
39
dagger/state/store_test.go
Normal file
39
dagger/state/store_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStore(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
// Init
|
||||
root, err := os.MkdirTemp(os.TempDir(), "dagger-*")
|
||||
require.NoError(t, err)
|
||||
st, err := Init(ctx, root, "test")
|
||||
require.Equal(t, "test", st.Name)
|
||||
require.Equal(t, root, st.Path)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Open
|
||||
_, err = Open(ctx, "/tmp/not/exist")
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, ErrNotInit, err)
|
||||
|
||||
st, err = Open(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "test", st.Name)
|
||||
require.Equal(t, root, st.Path)
|
||||
|
||||
// Save
|
||||
computed := `{"hello": "world"}`
|
||||
st.Computed = computed
|
||||
require.NoError(t, Save(ctx, st))
|
||||
st, err = Open(ctx, root)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, computed, st.Computed)
|
||||
}
|
250
dagger/store.go
250
dagger/store.go
@ -1,250 +0,0 @@
|
||||
package dagger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrEnvironmentExist = errors.New("environment already exists")
|
||||
ErrEnvironmentNotExist = errors.New("environment doesn't exist")
|
||||
)
|
||||
|
||||
const (
|
||||
defaultStoreRoot = "$HOME/.dagger/store"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
root string
|
||||
|
||||
l sync.RWMutex
|
||||
|
||||
// ID -> Environment
|
||||
environments map[string]*EnvironmentState
|
||||
|
||||
// Name -> Environment
|
||||
environmentsByName map[string]*EnvironmentState
|
||||
|
||||
// Path -> (ID->Environment)
|
||||
environmentsByPath map[string]map[string]*EnvironmentState
|
||||
|
||||
// ID -> (Path->{})
|
||||
pathsByEnvironmentID map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
func NewStore(root string) (*Store, error) {
|
||||
store := &Store{
|
||||
root: root,
|
||||
environments: make(map[string]*EnvironmentState),
|
||||
environmentsByName: make(map[string]*EnvironmentState),
|
||||
environmentsByPath: make(map[string]map[string]*EnvironmentState),
|
||||
pathsByEnvironmentID: make(map[string]map[string]struct{}),
|
||||
}
|
||||
return store, store.loadAll()
|
||||
}
|
||||
|
||||
func DefaultStore() (*Store, error) {
|
||||
if root := os.Getenv("DAGGER_STORE"); root != "" {
|
||||
return NewStore(root)
|
||||
}
|
||||
|
||||
return NewStore(os.ExpandEnv(defaultStoreRoot))
|
||||
}
|
||||
|
||||
func (s *Store) environmentPath(name string) string {
|
||||
// FIXME: rename to environment.json ?
|
||||
return path.Join(s.root, name, "deployment.json")
|
||||
}
|
||||
|
||||
func (s *Store) loadAll() error {
|
||||
files, err := os.ReadDir(s.root)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
if err := s.loadEnvironment(f.Name()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) loadEnvironment(name string) error {
|
||||
data, err := os.ReadFile(s.environmentPath(name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var st EnvironmentState
|
||||
if err := json.Unmarshal(data, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
s.indexEnvironment(&st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) syncEnvironment(r *EnvironmentState) error {
|
||||
p := s.environmentPath(r.Name)
|
||||
|
||||
if err := os.MkdirAll(path.Dir(p), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(r, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(p, data, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.reindexEnvironment(r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) indexEnvironment(r *EnvironmentState) {
|
||||
s.environments[r.ID] = r
|
||||
s.environmentsByName[r.Name] = r
|
||||
|
||||
mapPath := func(i Input) {
|
||||
if i.Type != InputTypeDir {
|
||||
return
|
||||
}
|
||||
if s.environmentsByPath[i.Dir.Path] == nil {
|
||||
s.environmentsByPath[i.Dir.Path] = make(map[string]*EnvironmentState)
|
||||
}
|
||||
s.environmentsByPath[i.Dir.Path][r.ID] = r
|
||||
|
||||
if s.pathsByEnvironmentID[r.ID] == nil {
|
||||
s.pathsByEnvironmentID[r.ID] = make(map[string]struct{})
|
||||
}
|
||||
s.pathsByEnvironmentID[r.ID][i.Dir.Path] = struct{}{}
|
||||
}
|
||||
|
||||
mapPath(r.PlanSource)
|
||||
for _, i := range r.Inputs {
|
||||
mapPath(i.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) deindexEnvironment(id string) {
|
||||
r, ok := s.environments[id]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delete(s.environments, r.ID)
|
||||
delete(s.environmentsByName, r.Name)
|
||||
|
||||
for p := range s.pathsByEnvironmentID[r.ID] {
|
||||
delete(s.environmentsByPath[p], r.ID)
|
||||
}
|
||||
delete(s.pathsByEnvironmentID, r.ID)
|
||||
}
|
||||
|
||||
func (s *Store) reindexEnvironment(r *EnvironmentState) {
|
||||
s.deindexEnvironment(r.ID)
|
||||
s.indexEnvironment(r)
|
||||
}
|
||||
|
||||
func (s *Store) CreateEnvironment(ctx context.Context, st *EnvironmentState) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
if _, ok := s.environmentsByName[st.Name]; ok {
|
||||
return fmt.Errorf("%s: %w", st.Name, ErrEnvironmentExist)
|
||||
}
|
||||
|
||||
st.ID = uuid.New().String()
|
||||
return s.syncEnvironment(st)
|
||||
}
|
||||
|
||||
type UpdateOpts struct{}
|
||||
|
||||
func (s *Store) UpdateEnvironment(ctx context.Context, r *EnvironmentState, o *UpdateOpts) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
return s.syncEnvironment(r)
|
||||
}
|
||||
|
||||
type DeleteOpts struct{}
|
||||
|
||||
func (s *Store) DeleteEnvironment(ctx context.Context, r *EnvironmentState, o *DeleteOpts) error {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
if err := os.Remove(s.environmentPath(r.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
s.deindexEnvironment(r.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupEnvironmentByID(ctx context.Context, id string) (*EnvironmentState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
st, ok := s.environments[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: %w", id, ErrEnvironmentNotExist)
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupEnvironmentByName(ctx context.Context, name string) (*EnvironmentState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
st, ok := s.environmentsByName[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s: %w", name, ErrEnvironmentNotExist)
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (s *Store) LookupEnvironmentByPath(ctx context.Context, path string) ([]*EnvironmentState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
res := []*EnvironmentState{}
|
||||
|
||||
environments, ok := s.environmentsByPath[path]
|
||||
if !ok {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
for _, d := range environments {
|
||||
res = append(res, d)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *Store) ListEnvironments(ctx context.Context) ([]*EnvironmentState, error) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
environments := make([]*EnvironmentState, 0, len(s.environments))
|
||||
|
||||
for _, st := range s.environments {
|
||||
environments = append(environments, st)
|
||||
}
|
||||
|
||||
return environments, nil
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
package dagger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStoreLoad(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
root, err := os.MkdirTemp(os.TempDir(), "dagger-*")
|
||||
require.NoError(t, err)
|
||||
store, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = store.LookupEnvironmentByName(ctx, "notexist")
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Is(err, ErrEnvironmentNotExist))
|
||||
|
||||
st := &EnvironmentState{
|
||||
Name: "test",
|
||||
}
|
||||
require.NoError(t, store.CreateEnvironment(ctx, st))
|
||||
|
||||
checkEnvironments := func(store *Store) {
|
||||
r, err := store.LookupEnvironmentByID(ctx, st.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, r)
|
||||
require.Equal(t, "test", r.Name)
|
||||
|
||||
r, err = store.LookupEnvironmentByName(ctx, "test")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, r)
|
||||
require.Equal(t, "test", r.Name)
|
||||
|
||||
environments, err := store.ListEnvironments(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
require.Equal(t, "test", environments[0].Name)
|
||||
}
|
||||
|
||||
checkEnvironments(store)
|
||||
|
||||
// Reload the environments from disk and check again
|
||||
newStore, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
checkEnvironments(newStore)
|
||||
}
|
||||
|
||||
func TestStoreLookupByPath(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
|
||||
root, err := os.MkdirTemp(os.TempDir(), "dagger-*")
|
||||
require.NoError(t, err)
|
||||
store, err := NewStore(root)
|
||||
require.NoError(t, err)
|
||||
|
||||
st := &EnvironmentState{
|
||||
Name: "test",
|
||||
}
|
||||
require.NoError(t, st.SetInput("foo", DirInput("/test/path", []string{})))
|
||||
require.NoError(t, store.CreateEnvironment(ctx, st))
|
||||
|
||||
// Lookup by path
|
||||
environments, err := store.LookupEnvironmentByPath(ctx, "/test/path")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
require.Equal(t, st.ID, environments[0].ID)
|
||||
|
||||
// Add a new path
|
||||
require.NoError(t, st.SetInput("bar", DirInput("/test/anotherpath", []string{})))
|
||||
require.NoError(t, store.UpdateEnvironment(ctx, st, nil))
|
||||
|
||||
// Lookup by the previous path
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/path")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
require.Equal(t, st.ID, environments[0].ID)
|
||||
|
||||
// Lookup by the new path
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
require.Equal(t, st.ID, environments[0].ID)
|
||||
|
||||
// Remove a path
|
||||
require.NoError(t, st.RemoveInputs("foo"))
|
||||
require.NoError(t, store.UpdateEnvironment(ctx, st, nil))
|
||||
|
||||
// Lookup by the removed path should fail
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/path")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 0)
|
||||
|
||||
// Lookup by the other path should still work
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
|
||||
// Add another environment using the same path
|
||||
otherSt := &EnvironmentState{
|
||||
Name: "test2",
|
||||
}
|
||||
require.NoError(t, otherSt.SetInput("foo", DirInput("/test/anotherpath", []string{})))
|
||||
require.NoError(t, store.CreateEnvironment(ctx, otherSt))
|
||||
|
||||
// Lookup by path should return both environments
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 2)
|
||||
|
||||
// Remove the first environment. Lookup by path should still return the
|
||||
// second environment.
|
||||
require.NoError(t, store.DeleteEnvironment(ctx, st, nil))
|
||||
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, environments, 1)
|
||||
require.Equal(t, otherSt.ID, environments[0].ID)
|
||||
}
|
1
go.mod
1
go.mod
@ -9,7 +9,6 @@ require (
|
||||
github.com/containerd/console v1.0.2
|
||||
github.com/docker/distribution v2.7.1+incompatible
|
||||
github.com/emicklei/proto v1.9.0 // indirect
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
|
Reference in New Issue
Block a user