diff --git a/clienv/clienv.go b/clienv/clienv.go index 627ef82a9..cb63116c4 100644 --- a/clienv/clienv.go +++ b/clienv/clienv.go @@ -20,6 +20,7 @@ type CliEnv struct { stderr io.Writer Path *PathStructure domain string + branch string nhclient *nhostclient.Client projectName string } @@ -29,6 +30,7 @@ func New( stderr io.Writer, path *PathStructure, domain string, + branch string, projectName string, ) *CliEnv { return &CliEnv{ @@ -36,6 +38,7 @@ func New( stderr: stderr, Path: path, domain: domain, + branch: branch, nhclient: nil, projectName: projectName, } @@ -57,6 +60,7 @@ func FromCLI(cCtx *cli.Context) *CliEnv { cCtx.String(flagNhostFolder), ), domain: cCtx.String(flagDomain), + branch: cCtx.String(flagBranch), projectName: sanitizeName(cCtx.String(flagProjectName)), nhclient: nil, } @@ -70,6 +74,10 @@ func (ce *CliEnv) Domain() string { return ce.domain } +func (ce *CliEnv) Branch() string { + return ce.branch +} + func (ce *CliEnv) GetNhostClient() *nhostclient.Client { if ce.nhclient == nil { ce.nhclient = nhostclient.New(ce.domain) diff --git a/clienv/flags.go b/clienv/flags.go index d5b58b1ae..0bc363153 100644 --- a/clienv/flags.go +++ b/clienv/flags.go @@ -11,6 +11,7 @@ import ( const ( flagDomain = "domain" + flagBranch = "branch" flagProjectName = "project-name" flagRootFolder = "root-folder" flagDataFolder = "data-folder" @@ -35,15 +36,17 @@ func getGitBranchName() string { return head.Name().Short() } -func Flags() ([]cli.Flag, error) { +func Flags() ([]cli.Flag, error) { //nolint:funlen fullWorkingDir, err := os.Getwd() if err != nil { return nil, fmt.Errorf("failed to get working directory: %w", err) } + branch := getGitBranchName() + workingDir := "." dotNhostFolder := filepath.Join(workingDir, ".nhost") - dataFolder := filepath.Join(dotNhostFolder, "data", getGitBranchName()) + dataFolder := filepath.Join(dotNhostFolder, "data", branch) nhostFolder := filepath.Join(workingDir, "nhost") return []cli.Flag{ @@ -54,6 +57,13 @@ func Flags() ([]cli.Flag, error) { Value: "nhost.run", Hidden: true, }, + &cli.StringFlag{ //nolint:exhaustruct + Name: flagBranch, + Usage: "Git branch name", + EnvVars: []string{"BRANCH"}, + Value: branch, + Hidden: true, + }, &cli.StringFlag{ //nolint:exhaustruct Name: flagRootFolder, Usage: "Root folder of project\n\t", diff --git a/cmd/config/validate_test.go b/cmd/config/validate_test.go index d45029ab9..5cc72417e 100644 --- a/cmd/config/validate_test.go +++ b/cmd/config/validate_test.go @@ -207,6 +207,7 @@ func TestValidate(t *testing.T) { filepath.Join("testdata", "validate", tc.path, "nhost"), ), "fakedomain", + "fakebranch", "", ) diff --git a/cmd/dev/down.go b/cmd/dev/down.go index 78565e828..990409f6e 100644 --- a/cmd/dev/down.go +++ b/cmd/dev/down.go @@ -6,13 +6,23 @@ import ( "github.com/urfave/cli/v2" ) +const ( + flagVolumes = "volumes" +) + func CommandDown() *cli.Command { return &cli.Command{ //nolint:exhaustruct Name: "down", Aliases: []string{}, Usage: "Stop local development environment", Action: commandDown, - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + &cli.BoolFlag{ //nolint:exhaustruct + Name: flagVolumes, + Usage: "Remove volumes", + Value: false, + }, + }, } } @@ -21,7 +31,7 @@ func commandDown(cCtx *cli.Context) error { dc := dockercompose.New(ce.Path.WorkingDir(), ce.Path.DockerCompose(), ce.ProjectName()) - if err := dc.Stop(cCtx.Context); err != nil { + if err := dc.Stop(cCtx.Context, cCtx.Bool(flagVolumes)); err != nil { ce.Warnln("failed to stop Nhost development environment: %s", err) } diff --git a/cmd/dev/up.go b/cmd/dev/up.go index 547a6f460..d3f05d924 100644 --- a/cmd/dev/up.go +++ b/cmd/dev/up.go @@ -207,6 +207,7 @@ func up( //nolint:funlen ce.Path.DotNhostFolder(), ce.Path.Root(), ports, + ce.Branch(), ) if err != nil { return fmt.Errorf("failed to generate docker-compose.yaml: %w", err) @@ -300,7 +301,7 @@ func Up( ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - if err := dc.Stop(ctx); err != nil { //nolint:contextcheck + if err := dc.Stop(ctx, false); err != nil { //nolint:contextcheck ce.Warnln("failed to stop Nhost development environment: %s", err) } diff --git a/cmd/dockercredentials/get.go b/cmd/dockercredentials/get.go index 7104fe3e2..e4271599f 100644 --- a/cmd/dockercredentials/get.go +++ b/cmd/dockercredentials/get.go @@ -39,6 +39,7 @@ func getToken(ctx context.Context, domain string) (string, error) { &clienv.PathStructure{}, domain, "unneeded", + "unneeded", ) session, err := ce.LoadSession(ctx) if err != nil { diff --git a/dockercompose/compose.go b/dockercompose/compose.go index 9650ff6da..3c7f0d87f 100644 --- a/dockercompose/compose.go +++ b/dockercompose/compose.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "path/filepath" + "regexp" + "strings" "github.com/nhost/be/services/mimir/model" "github.com/nhost/cli/ssl" @@ -417,6 +419,11 @@ type ExposePorts struct { Functions uint } +func sanitizeBranch(name string) string { + re := regexp.MustCompile(`[^a-zA-Z0-9_-]`) + return strings.ToLower(re.ReplaceAllString(name, "")) +} + func ComposeFileFromConfig( //nolint:funlen cfg *model.ConfigConfig, projectName string, @@ -428,6 +435,7 @@ func ComposeFileFromConfig( //nolint:funlen dotNhostFolder string, rootFolder string, ports ExposePorts, + branch string, ) (*ComposeFile, error) { minio, err := minio(dataFolder) if err != nil { @@ -444,7 +452,8 @@ func ComposeFileFromConfig( //nolint:funlen return nil, err } - postgres, err := postgres(cfg, postgresPort, dataFolder) + pgVolumeName := fmt.Sprintf("pgdata_%s", sanitizeBranch(branch)) + postgres, err := postgres(cfg, postgresPort, dataFolder, pgVolumeName) if err != nil { return nil, err } @@ -494,6 +503,7 @@ func ComposeFileFromConfig( //nolint:funlen Volumes: map[string]struct{}{ "functions_node_modules": {}, "root_node_modules": {}, + pgVolumeName: {}, }, } return c, nil diff --git a/dockercompose/dockercompose.go b/dockercompose/dockercompose.go index fa65a537b..7f3b3f0cf 100644 --- a/dockercompose/dockercompose.go +++ b/dockercompose/dockercompose.go @@ -67,7 +67,7 @@ func (dc *DockerCompose) Start(ctx context.Context) error { return nil } -func (dc *DockerCompose) Stop(ctx context.Context) error { +func (dc *DockerCompose) Stop(ctx context.Context, volumes bool) error { cmd := exec.CommandContext( //nolint:gosec ctx, "docker", "compose", @@ -76,6 +76,9 @@ func (dc *DockerCompose) Stop(ctx context.Context) error { "-p", dc.projectName, "down", ) + if volumes { + cmd.Args = append(cmd.Args, "--volumes") + } cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/dockercompose/postgres.go b/dockercompose/postgres.go index 0862d499d..40737d658 100644 --- a/dockercompose/postgres.go +++ b/dockercompose/postgres.go @@ -12,6 +12,7 @@ func postgres( //nolint:funlen cfg *model.ConfigConfig, port uint, dataFolder string, + volumeName string, ) (*Service, error) { if err := os.MkdirAll(fmt.Sprintf("%s/db/pgdata", dataFolder), 0o755); err != nil { //nolint:gomnd return nil, fmt.Errorf("failed to create postgres data folder: %w", err) @@ -75,9 +76,9 @@ func postgres( //nolint:funlen Restart: "always", Volumes: []Volume{ { - Type: "bind", - Source: fmt.Sprintf("%s/db/pgdata", dataFolder), - Target: "/var/lib/postgresql/data/pgdata:Z", + Type: "volume", + Source: volumeName, + Target: "/var/lib/postgresql/data/pgdata", }, { Type: "bind", diff --git a/dockercompose/postgres_test.go b/dockercompose/postgres_test.go index d0a364d01..1047924bd 100644 --- a/dockercompose/postgres_test.go +++ b/dockercompose/postgres_test.go @@ -41,9 +41,9 @@ func expectedPostgres(tmpdir string) *Service { Restart: "always", Volumes: []Volume{ { - Type: "bind", - Source: filepath.Join(tmpdir, "db/pgdata"), - Target: "/var/lib/postgresql/data/pgdata:Z", + Type: "volume", + Source: "pgdate_test", + Target: "/var/lib/postgresql/data/pgdata", }, { Type: "bind", @@ -77,7 +77,7 @@ func TestPostgres(t *testing.T) { tc := tc tmpdir := filepath.Join(os.TempDir(), "data") - got, err := postgres(tc.cfg(), 5432, tmpdir) + got, err := postgres(tc.cfg(), 5432, tmpdir, "pgdate_test") if err != nil { t.Errorf("got error: %v", err) }