Rename "deployment" to "environment": code

Signed-off-by: Solomon Hykes <sh.github.6811@hykes.org>
This commit is contained in:
Solomon Hykes 2021-04-27 18:59:04 +00:00
parent d1853f9e4b
commit e6e8ab390d
29 changed files with 282 additions and 282 deletions

View File

@ -9,18 +9,18 @@ import (
"github.com/spf13/viper"
)
func GetCurrentDeploymentState(ctx context.Context, store *dagger.Store) *dagger.DeploymentState {
func GetCurrentEnvironmentState(ctx context.Context, store *dagger.Store) *dagger.EnvironmentState {
lg := log.Ctx(ctx)
deploymentName := viper.GetString("deployment")
if deploymentName != "" {
st, err := store.LookupDeploymentByName(ctx, deploymentName)
environmentName := viper.GetString("environment")
if environmentName != "" {
st, err := store.LookupEnvironmentByName(ctx, environmentName)
if err != nil {
lg.
Fatal().
Err(err).
Str("deploymentName", deploymentName).
Msg("failed to lookup deployment by name")
Str("environmentName", environmentName).
Msg("failed to lookup environment by name")
}
return st
}
@ -29,50 +29,50 @@ func GetCurrentDeploymentState(ctx context.Context, store *dagger.Store) *dagger
if err != nil {
lg.Fatal().Err(err).Msg("cannot get current working directory")
}
st, err := store.LookupDeploymentByPath(ctx, wd)
st, err := store.LookupEnvironmentByPath(ctx, wd)
if err != nil {
lg.
Fatal().
Err(err).
Str("deploymentPath", wd).
Msg("failed to lookup deployment by path")
Str("environmentPath", wd).
Msg("failed to lookup environment by path")
}
if len(st) == 0 {
lg.
Fatal().
Err(err).
Str("deploymentPath", wd).
Msg("no deployments match the current directory")
Str("environmentPath", wd).
Msg("no environments match the current directory")
}
if len(st) > 1 {
deployments := []string{}
environments := []string{}
for _, s := range st {
deployments = append(deployments, s.Name)
environments = append(environments, s.Name)
}
lg.
Fatal().
Err(err).
Str("deploymentPath", wd).
Strs("deployments", deployments).
Msg("multiple deployments match the current directory, select one with `--deployment`")
Str("environmentPath", wd).
Strs("environments", environments).
Msg("multiple environments match the current directory, select one with `--environment`")
}
return st[0]
}
// Re-compute a deployment (equivalent to `dagger up`).
func DeploymentUp(ctx context.Context, state *dagger.DeploymentState, noCache bool) *dagger.Deployment {
// Re-compute an environment (equivalent to `dagger up`).
func EnvironmentUp(ctx context.Context, state *dagger.EnvironmentState, noCache bool) *dagger.Environment {
lg := log.Ctx(ctx)
c, err := dagger.NewClient(ctx, "", noCache)
if err != nil {
lg.Fatal().Err(err).Msg("unable to create client")
}
result, err := c.Do(ctx, state, func(ctx context.Context, deployment *dagger.Deployment, s dagger.Solver) error {
log.Ctx(ctx).Debug().Msg("bringing deployment up")
return deployment.Up(ctx, s)
result, err := c.Do(ctx, state, func(ctx context.Context, environment *dagger.Environment, s dagger.Solver) error {
log.Ctx(ctx).Debug().Msg("bringing environment up")
return environment.Up(ctx, s)
})
if err != nil {
lg.Fatal().Err(err).Msg("failed to up deployment")
lg.Fatal().Err(err).Msg("failed to up environment")
}
return result
}

View File

@ -35,7 +35,7 @@ var computeCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
st := &dagger.DeploymentState{
st := &dagger.EnvironmentState{
ID: uuid.New().String(),
Name: "FIXME",
PlanSource: dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}),
@ -149,16 +149,16 @@ var computeCmd = &cobra.Command{
}
}
deployment := common.DeploymentUp(ctx, st, viper.GetBool("no-cache"))
environment := common.EnvironmentUp(ctx, st, viper.GetBool("no-cache"))
v := compiler.NewValue()
if err := v.FillPath(cue.MakePath(), deployment.Plan()); err != nil {
if err := v.FillPath(cue.MakePath(), environment.Plan()); err != nil {
lg.Fatal().Err(err).Msg("failed to merge")
}
if err := v.FillPath(cue.MakePath(), deployment.Input()); err != nil {
if err := v.FillPath(cue.MakePath(), environment.Input()); err != nil {
lg.Fatal().Err(err).Msg("failed to merge")
}
if err := v.FillPath(cue.MakePath(), deployment.Computed()); err != nil {
if err := v.FillPath(cue.MakePath(), environment.Computed()); err != nil {
lg.Fatal().Err(err).Msg("failed to merge")
}

View File

@ -7,7 +7,7 @@ import (
var deleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete a deployment after taking it offline (WARNING: may destroy infrastructure)",
Short: "Delete an environment after taking it offline (WARNING: may destroy infrastructure)",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:

View File

@ -7,7 +7,7 @@ import (
var downCmd = &cobra.Command{
Use: "down",
Short: "Take a deployment offline (WARNING: may destroy infrastructure)",
Short: "Take an environment offline (WARNING: may destroy infrastructure)",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:

View File

@ -7,7 +7,7 @@ import (
var historyCmd = &cobra.Command{
Use: "history",
Short: "List past changes to a deployment",
Short: "List past changes to an environment",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:

View File

@ -22,7 +22,7 @@ var containerCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentInput(ctx, args[0], dagger.DockerInput(args[1]))
updateEnvironmentInput(ctx, args[0], dagger.DockerInput(args[1]))
},
}

View File

@ -22,7 +22,7 @@ var dirCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentInput(ctx, args[0], dagger.DirInput(args[1], []string{}))
updateEnvironmentInput(ctx, args[0], dagger.DirInput(args[1], []string{}))
},
}

View File

@ -32,7 +32,7 @@ var gitCmd = &cobra.Command{
subDir = args[3]
}
updateDeploymentInput(ctx, args[0], dagger.GitInput(args[1], ref, subDir))
updateEnvironmentInput(ctx, args[0], dagger.GitInput(args[1], ref, subDir))
},
}

View File

@ -22,7 +22,7 @@ var jsonCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentInput(
updateEnvironmentInput(
ctx,
args[0],
dagger.JSONInput(readInput(ctx, args[1])),

View File

@ -17,7 +17,7 @@ import (
var listCmd = &cobra.Command{
Use: "list [TARGET] [flags]",
Short: "List for the inputs of a deployment",
Short: "List for the inputs of an environment",
Args: cobra.MaximumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:
@ -35,12 +35,12 @@ var listCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("failed to load store")
}
deployment := common.GetCurrentDeploymentState(ctx, store)
environment := common.GetCurrentEnvironmentState(ctx, store)
// print any persisted inputs
if len(deployment.Inputs) > 0 {
if len(environment.Inputs) > 0 {
fmt.Println("Saved Inputs:")
for _, input := range deployment.Inputs {
for _, input := range environment.Inputs {
// Todo, how to pull apart an input to print relevant information
fmt.Printf("%s: %v\n", input.Key, input.Value)
}
@ -49,8 +49,8 @@ var listCmd = &cobra.Command{
}
lg = lg.With().
Str("deploymentName", deployment.Name).
Str("deploymentId", deployment.ID).
Str("environmentName", environment.Name).
Str("environmentId", environment.ID).
Logger()
c, err := dagger.NewClient(ctx, "", false)
@ -58,7 +58,7 @@ var listCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("unable to create client")
}
_, err = c.Do(ctx, deployment, func(lCtx context.Context, lDeploy *dagger.Deployment, lSolver dagger.Solver) error {
_, err = c.Do(ctx, environment, func(lCtx context.Context, lDeploy *dagger.Environment, lSolver dagger.Solver) error {
inputs, err := lDeploy.ScanInputs()
if err != nil {
return err
@ -105,7 +105,7 @@ var listCmd = &cobra.Command{
})
if err != nil {
lg.Fatal().Err(err).Msg("failed to query deployment")
lg.Fatal().Err(err).Msg("failed to query environment")
}
},

View File

@ -15,7 +15,7 @@ import (
// Cmd exposes the top-level command
var Cmd = &cobra.Command{
Use: "input",
Short: "Manage a deployment's inputs",
Short: "Manage an environment's inputs",
}
func init() {
@ -31,7 +31,7 @@ func init() {
)
}
func updateDeploymentInput(ctx context.Context, target string, input dagger.Input) {
func updateEnvironmentInput(ctx context.Context, target string, input dagger.Input) {
lg := log.Ctx(ctx)
store, err := dagger.DefaultStore()
@ -39,13 +39,13 @@ func updateDeploymentInput(ctx context.Context, target string, input dagger.Inpu
lg.Fatal().Err(err).Msg("failed to load store")
}
st := common.GetCurrentDeploymentState(ctx, store)
st := common.GetCurrentEnvironmentState(ctx, store)
st.SetInput(target, input)
if err := store.UpdateDeployment(ctx, st, nil); err != nil {
lg.Fatal().Err(err).Str("deploymentId", st.ID).Str("deploymentName", st.Name).Msg("cannot update deployment")
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("deploymentId", st.ID).Str("deploymentName", st.Name).Msg("updated deployment")
lg.Info().Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("updated environment")
}
func readInput(ctx context.Context, source string) string {

View File

@ -22,7 +22,7 @@ var textCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentInput(
updateEnvironmentInput(
ctx,
args[0],
dagger.TextInput(readInput(ctx, args[1])),

View File

@ -22,7 +22,7 @@ var yamlCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentInput(
updateEnvironmentInput(
ctx,
args[0],
dagger.YAMLInput(readInput(ctx, args[1])),

View File

@ -18,7 +18,7 @@ import (
var listCmd = &cobra.Command{
Use: "list",
Short: "List available deployments",
Short: "List available environments",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:
@ -35,20 +35,20 @@ var listCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("failed to load store")
}
deployments, err := store.ListDeployments(ctx)
environments, err := store.ListEnvironments(ctx)
if err != nil {
lg.
Fatal().
Err(err).
Msg("cannot list deployments")
Msg("cannot list environments")
}
deploymentID := getCurrentDeploymentID(ctx, store)
environmentID := getCurrentEnvironmentID(ctx, store)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.TabIndent)
for _, r := range deployments {
for _, r := range environments {
line := fmt.Sprintf("%s\t%s\t", r.Name, formatPlanSource(r.PlanSource))
if r.ID == deploymentID {
line = fmt.Sprintf("%s- active deployment", line)
if r.ID == environmentID {
line = fmt.Sprintf("%s- active environment", line)
}
fmt.Fprintln(w, line)
}
@ -62,7 +62,7 @@ func init() {
}
}
func getCurrentDeploymentID(ctx context.Context, store *dagger.Store) string {
func getCurrentEnvironmentID(ctx context.Context, store *dagger.Store) string {
lg := log.Ctx(ctx)
wd, err := os.Getwd()
@ -71,7 +71,7 @@ func getCurrentDeploymentID(ctx context.Context, store *dagger.Store) string {
return ""
}
st, err := store.LookupDeploymentByPath(ctx, wd)
st, err := store.LookupEnvironmentByPath(ctx, wd)
if err != nil {
// Ignore error
return ""

View File

@ -17,7 +17,7 @@ import (
var newCmd = &cobra.Command{
Use: "new",
Short: "Create a new deployment",
Short: "Create a new environment",
Args: cobra.MaximumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:
@ -34,41 +34,41 @@ var newCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("failed to load store")
}
if viper.GetString("deployment") != "" {
if viper.GetString("environment") != "" {
lg.
Fatal().
Msg("cannot use option -d,--deployment for this command")
Msg("cannot use option -d,--environment for this command")
}
name := ""
if len(args) > 0 {
name = args[0]
} else {
name = getNewDeploymentName(ctx)
name = getNewEnvironmentName(ctx)
}
st := &dagger.DeploymentState{
st := &dagger.EnvironmentState{
Name: name,
PlanSource: getPlanSource(ctx),
}
err = store.CreateDeployment(ctx, st)
err = store.CreateEnvironment(ctx, st)
if err != nil {
lg.Fatal().Err(err).Msg("failed to create deployment")
lg.Fatal().Err(err).Msg("failed to create environment")
}
lg.
Info().
Str("deploymentId", st.ID).
Str("deploymentName", st.Name).
Msg("deployment created")
Str("environmentId", st.ID).
Str("environmentName", st.Name).
Msg("environment created")
if viper.GetBool("up") {
common.DeploymentUp(ctx, st, false)
common.EnvironmentUp(ctx, st, false)
}
},
}
func getNewDeploymentName(ctx context.Context) string {
func getNewEnvironmentName(ctx context.Context) string {
lg := log.Ctx(ctx)
workDir, err := os.Getwd()
@ -133,7 +133,7 @@ func getPlanSource(ctx context.Context) dagger.Input {
}
func init() {
newCmd.Flags().BoolP("up", "u", false, "Bring the deployment online")
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")

View File

@ -5,7 +5,7 @@ import "github.com/spf13/cobra"
// Cmd exposes the top-level command
var Cmd = &cobra.Command{
Use: "output",
Short: "Manage a deployment's outputs",
Short: "Manage an environment's outputs",
}
func init() {

View File

@ -22,7 +22,7 @@ var dirCmd = &cobra.Command{
lg := logger.New()
ctx := lg.WithContext(cmd.Context())
updateDeploymentPlan(ctx, dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}))
updateEnvironmentPlan(ctx, dagger.DirInput(args[0], []string{"*.cue", "cue.mod"}))
},
}

View File

@ -32,7 +32,7 @@ var gitCmd = &cobra.Command{
subDir = args[2]
}
updateDeploymentPlan(ctx, dagger.GitInput(args[0], ref, subDir))
updateEnvironmentPlan(ctx, dagger.GitInput(args[0], ref, subDir))
},
}

View File

@ -12,7 +12,7 @@ import (
// Cmd exposes the top-level command
var Cmd = &cobra.Command{
Use: "plan",
Short: "Manage a deployment's plan",
Short: "Manage an environment's plan",
}
func init() {
@ -24,7 +24,7 @@ func init() {
)
}
func updateDeploymentPlan(ctx context.Context, planSource dagger.Input) {
func updateEnvironmentPlan(ctx context.Context, planSource dagger.Input) {
lg := log.Ctx(ctx)
store, err := dagger.DefaultStore()
@ -32,11 +32,11 @@ func updateDeploymentPlan(ctx context.Context, planSource dagger.Input) {
lg.Fatal().Err(err).Msg("failed to load store")
}
st := common.GetCurrentDeploymentState(ctx, store)
st := common.GetCurrentEnvironmentState(ctx, store)
st.PlanSource = planSource
if err := store.UpdateDeployment(ctx, st, nil); err != nil {
lg.Fatal().Err(err).Str("deploymentId", st.ID).Str("deploymentName", st.Name).Msg("cannot update deployment")
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("deploymentId", st.ID).Str("deploymentName", st.Name).Msg("updated deployment")
lg.Info().Str("environmentId", st.ID).Str("environmentName", st.Name).Msg("updated environment")
}

View File

@ -15,7 +15,7 @@ import (
var queryCmd = &cobra.Command{
Use: "query [TARGET] [flags]",
Short: "Query the contents of a deployment",
Short: "Query the contents of an environment",
Args: cobra.MaximumNArgs(1),
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:
@ -35,11 +35,11 @@ var queryCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("failed to load store")
}
state := common.GetCurrentDeploymentState(ctx, store)
state := common.GetCurrentEnvironmentState(ctx, store)
lg = lg.With().
Str("deploymentName", state.Name).
Str("deploymentId", state.ID).
Str("environmentName", state.Name).
Str("environmentId", state.ID).
Logger()
cuePath := cue.MakePath()
@ -52,21 +52,21 @@ var queryCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("unable to create client")
}
deployment, err := c.Do(ctx, state, nil)
environment, err := c.Do(ctx, state, nil)
if err != nil {
lg.Fatal().Err(err).Msg("failed to query deployment")
lg.Fatal().Err(err).Msg("failed to query environment")
}
cueVal := compiler.NewValue()
if !viper.GetBool("no-plan") {
if err := cueVal.FillPath(cue.MakePath(), deployment.Plan()); err != nil {
if err := cueVal.FillPath(cue.MakePath(), environment.Plan()); err != nil {
lg.Fatal().Err(err).Msg("failed to merge plan")
}
}
if !viper.GetBool("no-input") {
if err := cueVal.FillPath(cue.MakePath(), deployment.Input()); err != nil {
if err := cueVal.FillPath(cue.MakePath(), environment.Input()); err != nil {
lg.Fatal().Err(err).Msg("failed to merge plan with output")
}
}
@ -139,7 +139,7 @@ func init() {
queryCmd.Flags().BoolP("show-attributes", "A", false, "Display field attributes (cue format only)")
// FIXME: implement the flags below
// queryCmd.Flags().String("revision", "latest", "Query a specific version of the deployment")
// queryCmd.Flags().String("revision", "latest", "Query a specific version of the environment")
queryCmd.Flags().StringP("format", "f", "json", "Output format (json|yaml|cue|text|env)")
queryCmd.Flags().BoolP("no-plan", "P", false, "Exclude plan from query")
queryCmd.Flags().BoolP("no-input", "I", false, "Exclude inputs from query")

View File

@ -17,13 +17,13 @@ import (
var rootCmd = &cobra.Command{
Use: "dagger",
Short: "A programmable deployment system",
Short: "A programmable environment system",
}
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("deployment", "d", "", "Select a deployment")
rootCmd.PersistentFlags().StringP("environment", "e", "", "Select an environment")
rootCmd.AddCommand(
computeCmd,

View File

@ -11,7 +11,7 @@ import (
var upCmd = &cobra.Command{
Use: "up",
Short: "Bring a deployment online with latest plan and inputs",
Short: "Bring an environment online with latest plan and inputs",
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// Fix Viper bug for duplicate flags:
@ -28,11 +28,11 @@ var upCmd = &cobra.Command{
lg.Fatal().Err(err).Msg("failed to load store")
}
state := common.GetCurrentDeploymentState(ctx, store)
result := common.DeploymentUp(ctx, state, viper.GetBool("no-cache"))
state := common.GetCurrentEnvironmentState(ctx, store)
result := common.EnvironmentUp(ctx, state, viper.GetBool("no-cache"))
state.Computed = result.Computed().JSON().String()
if err := store.UpdateDeployment(ctx, state, nil); err != nil {
lg.Fatal().Err(err).Msg("failed to update deployment")
if err := store.UpdateEnvironment(ctx, state, nil); err != nil {
lg.Fatal().Err(err).Msg("failed to update environment")
}
},
}

View File

@ -29,27 +29,27 @@ import (
description: "Write code to deploy your code"
doc: """
A Dagger deployment is a continuously running workflow delivering a specific application in a specific way.
A Dagger environment is a continuously running workflow delivering a specific application in a specific way.
The same application can be delivered via different deployments, each with a different configuration.
For example a production deployment might include manual validation and addition performance testing,
while a staging deployment might automatically deploy from a git branch, load test data into the database,
The same application can be delivered via different environments, each with a different configuration.
For example a production environment might include manual validation and addition performance testing,
while a staging environment might automatically deploy from a git branch, load test data into the database,
and run on a separate cluster.
A deployment is made of 3 parts: a deployment plan, inputs, and outputs.
An environment is made of 3 parts: a plan, inputs, and outputs.
"""
flag: {
"--deployment": {
"--environment": {
alt: "-d"
description:
"""
Select a deployment
Select an environment
If no deployment is specified, dagger searches for deployments using the current
If no environment is specified, dagger searches for environments using the current
directory as input.
* If exactly one deployment matches the search, it is selected.
* If exactly one environment matches the search, it is selected.
* If there is more than one match, the user is prompted to select one.
* If there is no match, the command returns an error.
"""
@ -69,25 +69,25 @@ import (
command: {
new: {
description: "Create a new deployment"
description: "Create a new environment"
flag: {
"--name": {
alt: "-n"
description: "Specify a deployment name"
description: "Specify an environment name"
default: "name of current directory"
}
"--plan-dir": description: "Load deployment plan from a local directory"
"--plan-dir": description: "Load plan from a local directory"
"--plan-git": description: "Load deployment plan from a git repository"
"--plan-git": description: "Load plan from a git repository"
"--plan-package": description: "Load deployment plan from a cue package"
"--plan-package": description: "Load plan from a cue package"
"--plan-file": description: "Load deployment plan from a cue or json file"
"--plan-file": description: "Load plan from a cue or json file"
"--up": {
alt: "-u"
description: "Bring the deployment online"
description: "Bring the environment online"
}
"--setup": {
@ -97,26 +97,26 @@ import (
}
}
list: description: "List available deployments"
list: description: "List available environments"
query: {
arg: "[EXPR] [flags]"
description: "Query the contents of a deployment"
description: "Query the contents of an environment"
doc:
"""
EXPR may be any valid CUE expression. The expression is evaluated against the deployment contents. The deployment is not changed.
EXPR may be any valid CUE expression. The expression is evaluated against the environment contents. The environment is not changed.
Examples:
# Print the entire deployment plan with inputs merged in (but no outputs)
# Print the entire plan with inputs merged in (but no outputs)
$ dagger query --no-output
# Print the deployment plan, inputs and outputs of a particular step
# Print the plan, inputs and outputs of a particular step
$ dagger query www.build
# Print the URL of a deployed service
$ dagger query api.url
# Export environment variables from a deployment
# Export environment variables from an environment
$ dagger query -f json api.environment
"""
@ -127,7 +127,7 @@ import (
// Use --revision or --change or --change-id instead?
"--version": {
alt: "-v"
description: "Query a specific version of the deployment"
description: "Query a specific version of the environment"
default: "latest"
}
@ -147,29 +147,29 @@ import (
}
"--no-plan": {
alt: "-L"
description: "Exclude deployment plan from query"
description: "Exclude plan from query"
}
}
}
up: {
description: "Bring a deployment online with latest deployment plan and inputs"
description: "Bring an environment online with latest plan and inputs"
flag: "--no-cache": description: "Disable all run cache"
}
down: {
description: "Take a deployment offline (WARNING: may destroy infrastructure)"
description: "Take an environment offline (WARNING: may destroy infrastructure)"
flag: "--no-cache": description: "Disable all run cache"
}
history: description: "List past changes to a deployment"
history: description: "List past changes to an environment"
delete: {
description: "Delete a deployment after taking it offline (WARNING: may destroy infrastructure)"
description: "Delete an environment after taking it offline (WARNING: may destroy infrastructure)"
}
plan: {
description: "Manage a deployment plan"
description: "Manage an environment plan"
command: {
package: {
@ -216,7 +216,7 @@ import (
}
input: {
description: "Manage a deployment's inputs"
description: "Manage an environment's inputs"
command: {
// FIXME: details of individual input commands
@ -229,10 +229,10 @@ import (
}
output: {
description: "Manage a deployment's outputs"
description: "Manage an environment's outputs"
// FIXME: bind output values or artifacts
// to local dir or file
// BONUS: bind a deployment output to another deployment's input?
// BONUS: bind an environment output to another environment's input?
}
login: description: "Login to Dagger Cloud"

View File

@ -60,14 +60,14 @@ func NewClient(ctx context.Context, host string, noCache bool) (*Client, error)
}, nil
}
type ClientDoFunc func(context.Context, *Deployment, Solver) 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 *DeploymentState, fn ClientDoFunc) (*Deployment, error) {
func (c *Client) Do(ctx context.Context, state *EnvironmentState, fn ClientDoFunc) (*Environment, error) {
lg := log.Ctx(ctx)
eg, gctx := errgroup.WithContext(ctx)
deployment, err := NewDeployment(state)
environment, err := NewEnvironment(state)
if err != nil {
return nil, err
}
@ -83,17 +83,17 @@ func (c *Client) Do(ctx context.Context, state *DeploymentState, fn ClientDoFunc
// Spawn build function
eg.Go(func() error {
return c.buildfn(gctx, deployment, fn, events)
return c.buildfn(gctx, environment, fn, events)
})
return deployment, eg.Wait()
return environment, eg.Wait()
}
func (c *Client) buildfn(ctx context.Context, deployment *Deployment, fn ClientDoFunc, ch chan *bk.SolveStatus) error {
func (c *Client) buildfn(ctx context.Context, environment *Environment, fn ClientDoFunc, ch chan *bk.SolveStatus) error {
lg := log.Ctx(ctx)
// Scan local dirs to grant access
localdirs := deployment.LocalDirs()
localdirs := environment.LocalDirs()
for label, dir := range localdirs {
abs, err := filepath.Abs(dir)
if err != nil {
@ -121,24 +121,24 @@ func (c *Client) buildfn(ctx context.Context, deployment *Deployment, fn ClientD
s := NewSolver(c.c, gw, ch, auth, c.noCache)
lg.Debug().Msg("loading configuration")
if err := deployment.LoadPlan(ctx, s); err != nil {
if err := environment.LoadPlan(ctx, s); err != nil {
return nil, err
}
// Compute output overlay
if fn != nil {
if err := fn(ctx, deployment, s); err != nil {
if err := fn(ctx, environment, s); err != nil {
return nil, compiler.Err(err)
}
}
// Export deployment to a cue directory
// Export environment to a cue directory
// FIXME: this should be elsewhere
lg.Debug().Msg("exporting deployment")
span, _ := opentracing.StartSpanFromContext(ctx, "Deployment.Export")
lg.Debug().Msg("exporting environment")
span, _ := opentracing.StartSpanFromContext(ctx, "Environment.Export")
defer span.Finish()
computed := deployment.Computed().JSON().PrettyString()
computed := environment.Computed().JSON().PrettyString()
st := llb.
Scratch().
File(

View File

@ -19,8 +19,8 @@ import (
"github.com/rs/zerolog/log"
)
type Deployment struct {
state *DeploymentState
type Environment struct {
state *EnvironmentState
// Layer 1: plan configuration
plan *compiler.Value
@ -32,8 +32,8 @@ type Deployment struct {
computed *compiler.Value
}
func NewDeployment(st *DeploymentState) (*Deployment, error) {
d := &Deployment{
func NewEnvironment(st *EnvironmentState) (*Environment, error) {
d := &Environment{
state: st,
plan: compiler.NewValue(),
@ -60,33 +60,33 @@ func NewDeployment(st *DeploymentState) (*Deployment, error) {
return d, nil
}
func (d *Deployment) ID() string {
func (d *Environment) ID() string {
return d.state.ID
}
func (d *Deployment) Name() string {
func (d *Environment) Name() string {
return d.state.Name
}
func (d *Deployment) PlanSource() Input {
func (d *Environment) PlanSource() Input {
return d.state.PlanSource
}
func (d *Deployment) Plan() *compiler.Value {
func (d *Environment) Plan() *compiler.Value {
return d.plan
}
func (d *Deployment) Input() *compiler.Value {
func (d *Environment) Input() *compiler.Value {
return d.input
}
func (d *Deployment) Computed() *compiler.Value {
func (d *Environment) Computed() *compiler.Value {
return d.computed
}
// LoadPlan loads the plan
func (d *Deployment) LoadPlan(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "deployment.LoadPlan")
func (d *Environment) LoadPlan(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "environment.LoadPlan")
defer span.Finish()
planSource, err := d.state.PlanSource.Compile()
@ -114,11 +114,11 @@ func (d *Deployment) LoadPlan(ctx context.Context, s Solver) error {
return nil
}
// Scan all scripts in the deployment for references to local directories (do:"local"),
// Scan all scripts in the environment for references to local directories (do:"local"),
// and return all referenced directory names.
// This is used by clients to grant access to local directories when they are referenced
// by user-specified scripts.
func (d *Deployment) LocalDirs() map[string]string {
func (d *Environment) LocalDirs() map[string]string {
dirs := map[string]string{}
localdirs := func(code ...*compiler.Value) {
Analyze(
@ -140,7 +140,7 @@ func (d *Deployment) LocalDirs() map[string]string {
code...,
)
}
// 1. Scan the deployment state
// 1. Scan the environment state
// FIXME: use a common `flow` instance to avoid rescanning the tree.
src, err := compiler.InstanceMerge(d.plan, d.input)
if err != nil {
@ -165,9 +165,9 @@ func (d *Deployment) LocalDirs() map[string]string {
return dirs
}
// Up missing values in deployment configuration, and write them to state.
func (d *Deployment) Up(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "deployment.Up")
// Up missing values in environment configuration, and write them to state.
func (d *Environment) Up(ctx context.Context, s Solver) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "environment.Up")
defer span.Finish()
// Reset the computed values
@ -194,7 +194,7 @@ func (d *Deployment) Up(ctx context.Context, s Solver) error {
type DownOpts struct{}
func (d *Deployment) Down(ctx context.Context, _ *DownOpts) error {
func (d *Environment) Down(ctx context.Context, _ *DownOpts) error {
panic("NOT IMPLEMENTED")
}
@ -292,7 +292,7 @@ func newPipelineRunner(inst *cue.Instance, computed *compiler.Value, s Solver) c
})
}
func (d *Deployment) ScanInputs() ([]cue.Value, error) {
func (d *Environment) ScanInputs() ([]cue.Value, error) {
vals, err := cuetils.ScanForInputs(d.plan.Cue())
if err != nil {
return nil, err

View File

@ -7,15 +7,15 @@ import (
)
func TestInputDir(t *testing.T) {
st := &DeploymentState{
st := &EnvironmentState{
PlanSource: DirInput("/tmp/source", []string{}),
}
require.NoError(t, st.SetInput("www.source", DirInput("/", []string{})))
deployment, err := NewDeployment(st)
environment, err := NewEnvironment(st)
require.NoError(t, err)
localdirs := deployment.LocalDirs()
localdirs := environment.LocalDirs()
require.Len(t, localdirs, 2)
require.Contains(t, localdirs, "/")
require.Contains(t, localdirs, "/tmp/source")

View File

@ -1,16 +1,16 @@
package dagger
// Contents of a deployment serialized to a file
type DeploymentState struct {
// Globally unique deployment ID
// Contents of an environment serialized to a file
type EnvironmentState struct {
// Globally unique environment ID
ID string `json:"id,omitempty"`
// Human-friendly deployment name.
// A deployment may have more than one name.
// 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 deployment plan
// Cue module containing the environment plan
// The input's top-level artifact is used as a module directory.
PlanSource Input `json:"plan,omitempty"`
@ -26,7 +26,7 @@ type inputKV struct {
Value Input `json:"value,omitempty"`
}
func (s *DeploymentState) SetInput(key string, value Input) error {
func (s *EnvironmentState) SetInput(key string, value Input) error {
for i, inp := range s.Inputs {
if inp.Key != key {
continue
@ -42,7 +42,7 @@ func (s *DeploymentState) 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 *DeploymentState) RemoveInputs(key string) error {
func (s *EnvironmentState) RemoveInputs(key string) error {
newInputs := make([]inputKV, 0, len(s.Inputs))
for _, i := range s.Inputs {
if i.Key == key {

View File

@ -13,8 +13,8 @@ import (
)
var (
ErrDeploymentExist = errors.New("deployment already exists")
ErrDeploymentNotExist = errors.New("deployment doesn't exist")
ErrEnvironmentExist = errors.New("environment already exists")
ErrEnvironmentNotExist = errors.New("environment doesn't exist")
)
const (
@ -26,26 +26,26 @@ type Store struct {
l sync.RWMutex
// ID -> Deployment
deployments map[string]*DeploymentState
// ID -> Environment
environments map[string]*EnvironmentState
// Name -> Deployment
deploymentsByName map[string]*DeploymentState
// Name -> Environment
environmentsByName map[string]*EnvironmentState
// Path -> (ID->Deployment)
deploymentsByPath map[string]map[string]*DeploymentState
// Path -> (ID->Environment)
environmentsByPath map[string]map[string]*EnvironmentState
// ID -> (Path->{})
pathsByDeploymentID map[string]map[string]struct{}
pathsByEnvironmentID map[string]map[string]struct{}
}
func NewStore(root string) (*Store, error) {
store := &Store{
root: root,
deployments: make(map[string]*DeploymentState),
deploymentsByName: make(map[string]*DeploymentState),
deploymentsByPath: make(map[string]map[string]*DeploymentState),
pathsByDeploymentID: make(map[string]map[string]struct{}),
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()
}
@ -58,8 +58,8 @@ func DefaultStore() (*Store, error) {
return NewStore(os.ExpandEnv(defaultStoreRoot))
}
func (s *Store) deploymentPath(name string) string {
return path.Join(s.root, name, "deployment.json")
func (s *Store) environmentPath(name string) string {
return path.Join(s.root, name, "environment.json")
}
func (s *Store) loadAll() error {
@ -75,7 +75,7 @@ func (s *Store) loadAll() error {
if !f.IsDir() {
continue
}
if err := s.loadDeployment(f.Name()); err != nil {
if err := s.loadEnvironment(f.Name()); err != nil {
return err
}
}
@ -83,21 +83,21 @@ func (s *Store) loadAll() error {
return nil
}
func (s *Store) loadDeployment(name string) error {
data, err := os.ReadFile(s.deploymentPath(name))
func (s *Store) loadEnvironment(name string) error {
data, err := os.ReadFile(s.environmentPath(name))
if err != nil {
return err
}
var st DeploymentState
var st EnvironmentState
if err := json.Unmarshal(data, &st); err != nil {
return err
}
s.indexDeployment(&st)
s.indexEnvironment(&st)
return nil
}
func (s *Store) syncDeployment(r *DeploymentState) error {
p := s.deploymentPath(r.Name)
func (s *Store) syncEnvironment(r *EnvironmentState) error {
p := s.environmentPath(r.Name)
if err := os.MkdirAll(path.Dir(p), 0755); err != nil {
return err
@ -112,28 +112,28 @@ func (s *Store) syncDeployment(r *DeploymentState) error {
return err
}
s.reindexDeployment(r)
s.reindexEnvironment(r)
return nil
}
func (s *Store) indexDeployment(r *DeploymentState) {
s.deployments[r.ID] = r
s.deploymentsByName[r.Name] = r
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.deploymentsByPath[i.Dir.Path] == nil {
s.deploymentsByPath[i.Dir.Path] = make(map[string]*DeploymentState)
if s.environmentsByPath[i.Dir.Path] == nil {
s.environmentsByPath[i.Dir.Path] = make(map[string]*EnvironmentState)
}
s.deploymentsByPath[i.Dir.Path][r.ID] = r
s.environmentsByPath[i.Dir.Path][r.ID] = r
if s.pathsByDeploymentID[r.ID] == nil {
s.pathsByDeploymentID[r.ID] = make(map[string]struct{})
if s.pathsByEnvironmentID[r.ID] == nil {
s.pathsByEnvironmentID[r.ID] = make(map[string]struct{})
}
s.pathsByDeploymentID[r.ID][i.Dir.Path] = struct{}{}
s.pathsByEnvironmentID[r.ID][i.Dir.Path] = struct{}{}
}
mapPath(r.PlanSource)
@ -142,108 +142,108 @@ func (s *Store) indexDeployment(r *DeploymentState) {
}
}
func (s *Store) deindexDeployment(id string) {
r, ok := s.deployments[id]
func (s *Store) deindexEnvironment(id string) {
r, ok := s.environments[id]
if !ok {
return
}
delete(s.deployments, r.ID)
delete(s.deploymentsByName, r.Name)
delete(s.environments, r.ID)
delete(s.environmentsByName, r.Name)
for p := range s.pathsByDeploymentID[r.ID] {
delete(s.deploymentsByPath[p], r.ID)
for p := range s.pathsByEnvironmentID[r.ID] {
delete(s.environmentsByPath[p], r.ID)
}
delete(s.pathsByDeploymentID, r.ID)
delete(s.pathsByEnvironmentID, r.ID)
}
func (s *Store) reindexDeployment(r *DeploymentState) {
s.deindexDeployment(r.ID)
s.indexDeployment(r)
func (s *Store) reindexEnvironment(r *EnvironmentState) {
s.deindexEnvironment(r.ID)
s.indexEnvironment(r)
}
func (s *Store) CreateDeployment(ctx context.Context, st *DeploymentState) error {
func (s *Store) CreateEnvironment(ctx context.Context, st *EnvironmentState) error {
s.l.Lock()
defer s.l.Unlock()
if _, ok := s.deploymentsByName[st.Name]; ok {
return fmt.Errorf("%s: %w", st.Name, ErrDeploymentExist)
if _, ok := s.environmentsByName[st.Name]; ok {
return fmt.Errorf("%s: %w", st.Name, ErrEnvironmentExist)
}
st.ID = uuid.New().String()
return s.syncDeployment(st)
return s.syncEnvironment(st)
}
type UpdateOpts struct{}
func (s *Store) UpdateDeployment(ctx context.Context, r *DeploymentState, o *UpdateOpts) error {
func (s *Store) UpdateEnvironment(ctx context.Context, r *EnvironmentState, o *UpdateOpts) error {
s.l.Lock()
defer s.l.Unlock()
return s.syncDeployment(r)
return s.syncEnvironment(r)
}
type DeleteOpts struct{}
func (s *Store) DeleteDeployment(ctx context.Context, r *DeploymentState, o *DeleteOpts) error {
func (s *Store) DeleteEnvironment(ctx context.Context, r *EnvironmentState, o *DeleteOpts) error {
s.l.Lock()
defer s.l.Unlock()
if err := os.Remove(s.deploymentPath(r.Name)); err != nil {
if err := os.Remove(s.environmentPath(r.Name)); err != nil {
return err
}
s.deindexDeployment(r.ID)
s.deindexEnvironment(r.ID)
return nil
}
func (s *Store) LookupDeploymentByID(ctx context.Context, id string) (*DeploymentState, error) {
func (s *Store) LookupEnvironmentByID(ctx context.Context, id string) (*EnvironmentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.deployments[id]
st, ok := s.environments[id]
if !ok {
return nil, fmt.Errorf("%s: %w", id, ErrDeploymentNotExist)
return nil, fmt.Errorf("%s: %w", id, ErrEnvironmentNotExist)
}
return st, nil
}
func (s *Store) LookupDeploymentByName(ctx context.Context, name string) (*DeploymentState, error) {
func (s *Store) LookupEnvironmentByName(ctx context.Context, name string) (*EnvironmentState, error) {
s.l.RLock()
defer s.l.RUnlock()
st, ok := s.deploymentsByName[name]
st, ok := s.environmentsByName[name]
if !ok {
return nil, fmt.Errorf("%s: %w", name, ErrDeploymentNotExist)
return nil, fmt.Errorf("%s: %w", name, ErrEnvironmentNotExist)
}
return st, nil
}
func (s *Store) LookupDeploymentByPath(ctx context.Context, path string) ([]*DeploymentState, error) {
func (s *Store) LookupEnvironmentByPath(ctx context.Context, path string) ([]*EnvironmentState, error) {
s.l.RLock()
defer s.l.RUnlock()
res := []*DeploymentState{}
res := []*EnvironmentState{}
deployments, ok := s.deploymentsByPath[path]
environments, ok := s.environmentsByPath[path]
if !ok {
return res, nil
}
for _, d := range deployments {
for _, d := range environments {
res = append(res, d)
}
return res, nil
}
func (s *Store) ListDeployments(ctx context.Context) ([]*DeploymentState, error) {
func (s *Store) ListEnvironments(ctx context.Context) ([]*EnvironmentState, error) {
s.l.RLock()
defer s.l.RUnlock()
deployments := make([]*DeploymentState, 0, len(s.deployments))
environments := make([]*EnvironmentState, 0, len(s.environments))
for _, st := range s.deployments {
deployments = append(deployments, st)
for _, st := range s.environments {
environments = append(environments, st)
}
return deployments, nil
return environments, nil
}

View File

@ -17,38 +17,38 @@ func TestStoreLoad(t *testing.T) {
store, err := NewStore(root)
require.NoError(t, err)
_, err = store.LookupDeploymentByName(ctx, "notexist")
_, err = store.LookupEnvironmentByName(ctx, "notexist")
require.Error(t, err)
require.True(t, errors.Is(err, ErrDeploymentNotExist))
require.True(t, errors.Is(err, ErrEnvironmentNotExist))
st := &DeploymentState{
st := &EnvironmentState{
Name: "test",
}
require.NoError(t, store.CreateDeployment(ctx, st))
require.NoError(t, store.CreateEnvironment(ctx, st))
checkDeployments := func(store *Store) {
r, err := store.LookupDeploymentByID(ctx, st.ID)
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.LookupDeploymentByName(ctx, "test")
r, err = store.LookupEnvironmentByName(ctx, "test")
require.NoError(t, err)
require.NotNil(t, r)
require.Equal(t, "test", r.Name)
deployments, err := store.ListDeployments(ctx)
environments, err := store.ListEnvironments(ctx)
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Equal(t, "test", deployments[0].Name)
require.Len(t, environments, 1)
require.Equal(t, "test", environments[0].Name)
}
checkDeployments(store)
checkEnvironments(store)
// Reload the deployments from disk and check again
// Reload the environments from disk and check again
newStore, err := NewStore(root)
require.NoError(t, err)
checkDeployments(newStore)
checkEnvironments(newStore)
}
func TestStoreLookupByPath(t *testing.T) {
@ -59,65 +59,65 @@ func TestStoreLookupByPath(t *testing.T) {
store, err := NewStore(root)
require.NoError(t, err)
st := &DeploymentState{
st := &EnvironmentState{
Name: "test",
}
require.NoError(t, st.SetInput("foo", DirInput("/test/path", []string{})))
require.NoError(t, store.CreateDeployment(ctx, st))
require.NoError(t, store.CreateEnvironment(ctx, st))
// Lookup by path
deployments, err := store.LookupDeploymentByPath(ctx, "/test/path")
environments, err := store.LookupEnvironmentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
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.UpdateDeployment(ctx, st, nil))
require.NoError(t, store.UpdateEnvironment(ctx, st, nil))
// Lookup by the previous path
deployments, err = store.LookupDeploymentByPath(ctx, "/test/path")
environments, err = store.LookupEnvironmentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
require.Len(t, environments, 1)
require.Equal(t, st.ID, environments[0].ID)
// Lookup by the new path
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Equal(t, st.ID, deployments[0].ID)
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.UpdateDeployment(ctx, st, nil))
require.NoError(t, store.UpdateEnvironment(ctx, st, nil))
// Lookup by the removed path should fail
deployments, err = store.LookupDeploymentByPath(ctx, "/test/path")
environments, err = store.LookupEnvironmentByPath(ctx, "/test/path")
require.NoError(t, err)
require.Len(t, deployments, 0)
require.Len(t, environments, 0)
// Lookup by the other path should still work
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 1)
require.Len(t, environments, 1)
// Add another deployment using the same path
otherSt := &DeploymentState{
// 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.CreateDeployment(ctx, otherSt))
require.NoError(t, store.CreateEnvironment(ctx, otherSt))
// Lookup by path should return both deployments
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
// Lookup by path should return both environments
environments, err = store.LookupEnvironmentByPath(ctx, "/test/anotherpath")
require.NoError(t, err)
require.Len(t, deployments, 2)
require.Len(t, environments, 2)
// Remove the first deployment. Lookup by path should still return the
// second deployment.
require.NoError(t, store.DeleteDeployment(ctx, st, nil))
deployments, err = store.LookupDeploymentByPath(ctx, "/test/anotherpath")
// 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, deployments, 1)
require.Equal(t, otherSt.ID, deployments[0].ID)
require.Len(t, environments, 1)
require.Equal(t, otherSt.ID, environments[0].ID)
}