Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions cmd/command/up/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,13 @@ func (p *Plural) choseCluster() (name, url string, err error) {
return
}

func appDomainAlreadyConfigured(project *manifest.ProjectManifest) bool {
if project == nil {
return false
}
return project.AppDomainConfigured || project.AppDomain != ""
}

func askAppDomain(project *manifest.ProjectManifest) error {
skip, ok := utils.GetEnvBoolValue("PLURAL_UP_SKIP_APP_DOMAIN")
if ok && skip {
Expand All @@ -248,6 +255,11 @@ func askAppDomain(project *manifest.ProjectManifest) error {
return fmt.Errorf("project manifest is required to set app domain")
}

if appDomainAlreadyConfigured(project) {
utils.Highlight("App domain already configured, skipping...\n")
return nil
}

var domain string

switch project.Provider {
Expand All @@ -256,7 +268,7 @@ func askAppDomain(project *manifest.ProjectManifest) error {
if err != nil {
utils.Error("Failed to fetch hosted zones from AWS: %s\n", err)
fmt.Println("ignoring domain setup...")
break
return nil
}

if err := survey.AskOne(
Expand All @@ -274,12 +286,12 @@ func askAppDomain(project *manifest.ProjectManifest) error {
if err != nil {
utils.Error("Failed to fetch DNS zones from Azure: %s\n", err)
fmt.Println("ignoring domain setup...")
break
return nil
}

// Skip domain setup if no DNS zones exist in the resource group.
if len(dnsZones) == 0 {
break
return nil
}

if err := survey.AskOne(
Expand Down Expand Up @@ -309,10 +321,16 @@ func askAppDomain(project *manifest.ProjectManifest) error {
return processAppDomain(domain, project)
}

func persistAppDomain(project *manifest.ProjectManifest, domain string) error {
project.AppDomain = domain
project.AppDomainConfigured = true
return project.Flush()
}

func processAppDomain(domain string, project *manifest.ProjectManifest) error {
if lo.IsEmpty(domain) {
// No domain was provided, domain checks and setup can be skipped.
return nil
// No domain was provided; persist the skip so resume does not re-prompt.
return persistAppDomain(project, "")
}

if project.Provider == api.ProviderGCP {
Expand Down Expand Up @@ -357,9 +375,7 @@ func processAppDomain(domain string, project *manifest.ProjectManifest) error {
project.Context["ManagedZone"] = managedZone
}

// Save the domain and other changes to the project manifest.
project.AppDomain = domain
return project.Flush()
return persistAppDomain(project, domain)
}

func getCluster(cd *cdpkg.Plural) (id string, err error) {
Expand Down
117 changes: 117 additions & 0 deletions cmd/command/up/up_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package up

import (
"path/filepath"
"testing"

"github.com/pluralsh/plural-cli/pkg/manifest"
)

func TestAppDomainAlreadyConfigured(t *testing.T) {
tests := []struct {
name string
project *manifest.ProjectManifest
want bool
}{
{
name: "nil project",
project: nil,
want: false,
},
{
name: "never asked",
project: &manifest.ProjectManifest{},
want: false,
},
{
name: "legacy manifest with app domain",
project: &manifest.ProjectManifest{AppDomain: "apps.example.com"},
want: true,
},
{
name: "explicit skip",
project: &manifest.ProjectManifest{AppDomainConfigured: true},
want: true,
},
{
name: "configured with domain",
project: &manifest.ProjectManifest{
AppDomain: "apps.example.com",
AppDomainConfigured: true,
},
want: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := appDomainAlreadyConfigured(tt.project); got != tt.want {
t.Errorf("appDomainAlreadyConfigured() = %v, want %v", got, tt.want)
}
})
}
}

func TestProcessAppDomainPersistsSkip(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)

project := &manifest.ProjectManifest{Cluster: "test"}
if err := project.Write(filepath.Join(dir, "workspace.yaml")); err != nil {
t.Fatal(err)
}

if err := processAppDomain("", project); err != nil {
t.Fatal(err)
}

if !project.AppDomainConfigured {
t.Fatal("expected AppDomainConfigured after skipping domain")
}
if project.AppDomain != "" {
t.Fatalf("expected empty AppDomain, got %q", project.AppDomain)
}

loaded, err := manifest.ReadProject(filepath.Join(dir, "workspace.yaml"))
if err != nil {
t.Fatal(err)
}
if !loaded.AppDomainConfigured {
t.Fatal("expected AppDomainConfigured to be flushed to workspace.yaml")
}
if loaded.AppDomain != "" {
t.Fatalf("expected flushed AppDomain to be empty, got %q", loaded.AppDomain)
}
}

func TestProcessAppDomainPersistsDomain(t *testing.T) {
dir := t.TempDir()
t.Chdir(dir)

project := &manifest.ProjectManifest{Cluster: "test", Provider: "aws"}
if err := project.Write(filepath.Join(dir, "workspace.yaml")); err != nil {
t.Fatal(err)
}

if err := processAppDomain("apps.example.com", project); err != nil {
t.Fatal(err)
}

if !project.AppDomainConfigured {
t.Fatal("expected AppDomainConfigured after setting domain")
}
if project.AppDomain != "apps.example.com" {
t.Fatalf("AppDomain = %q, want apps.example.com", project.AppDomain)
}

loaded, err := manifest.ReadProject(filepath.Join(dir, "workspace.yaml"))
if err != nil {
t.Fatal(err)
}
if !loaded.AppDomainConfigured {
t.Fatal("expected AppDomainConfigured to be flushed to workspace.yaml")
}
if loaded.AppDomain != "apps.example.com" {
t.Fatalf("flushed AppDomain = %q, want apps.example.com", loaded.AppDomain)
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ require (
golang.org/x/crypto v0.55.0
golang.org/x/oauth2 v0.36.0
google.golang.org/api v0.287.0
google.golang.org/grpc v1.82.1
google.golang.org/grpc v1.83.1
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -928,8 +928,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 h1:
google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800/go.mod h1:FPk7EXUKMtImne7AmknoYjT4QXqKIzzRbeQIXzLk6fQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 h1:qEHAMpSaUhtD0p3NbEEI83HwNGFxEwaSJ1G9PLnCBZE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/grpc v1.83.1 h1:HIO0+BEtBP6soyqvqC8sNUjZ7bTs+0hFQuFF+RAy++Y=
google.golang.org/grpc v1.83.1/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
51 changes: 29 additions & 22 deletions pkg/manifest/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,42 @@ type ProjectManifest struct {
BucketPrefix string `yaml:"bucketPrefix"`
Context map[string]interface{}
AppDomain string `yaml:"appDomain,omitempty"`
// AppDomainConfigured is true after the app-domain prompt was answered,
// including when the user chose None. AppDomain alone cannot represent
// that skip: an empty value is omitted from workspace.yaml, so it looks
// the same as never asked.
AppDomainConfigured bool `yaml:"appDomainConfigured,omitempty"`
}

func (pm *ProjectManifest) MarshalJSON() ([]byte, error) {
json := jsoniter.ConfigCompatibleWithStandardLibrary

return json.Marshal(&struct {
Cluster string `json:"cluster"`
Bucket string `json:"bucket"`
Project string `json:"project"`
Provider string `json:"provider"`
Region string `json:"region"`
Owner *Owner `json:"owner"`
Network *NetworkConfig `json:"network"`
AvailabilityZones []string `json:"availabilityZones"`
BucketPrefix string `yaml:"bucketPrefix" json:"bucketPrefix"`
Context map[string]interface{} `json:"context"`
AppDomain string `json:"appDomain,omitempty"`
Cluster string `json:"cluster"`
Bucket string `json:"bucket"`
Project string `json:"project"`
Provider string `json:"provider"`
Region string `json:"region"`
Owner *Owner `json:"owner"`
Network *NetworkConfig `json:"network"`
AvailabilityZones []string `json:"availabilityZones"`
BucketPrefix string `yaml:"bucketPrefix" json:"bucketPrefix"`
Context map[string]interface{} `json:"context"`
AppDomain string `json:"appDomain,omitempty"`
AppDomainConfigured bool `json:"appDomainConfigured,omitempty"`
}{
Cluster: pm.Cluster,
Bucket: pm.Bucket,
Project: pm.Project,
Provider: pm.Provider,
Region: pm.Region,
Owner: pm.Owner,
Network: pm.Network,
AvailabilityZones: pm.AvailabilityZones,
BucketPrefix: pm.BucketPrefix,
Context: pm.Context,
AppDomain: pm.AppDomain,
Cluster: pm.Cluster,
Bucket: pm.Bucket,
Project: pm.Project,
Provider: pm.Provider,
Region: pm.Region,
Owner: pm.Owner,
Network: pm.Network,
AvailabilityZones: pm.AvailabilityZones,
BucketPrefix: pm.BucketPrefix,
Context: pm.Context,
AppDomain: pm.AppDomain,
AppDomainConfigured: pm.AppDomainConfigured,
})
}

Expand Down
7 changes: 6 additions & 1 deletion pkg/up/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (c *Context) runCheckpoint(current, checkpoint string, fn func() error) err
if current == "" || priorities[checkpoint] > priorities[current] {
err := fn()
if err == nil {
c.Manifest.Checkpoint = checkpoint
return c.completeCheckpoint(checkpoint)
}
return err
}
Expand All @@ -59,6 +59,11 @@ func (c *Context) runCheckpoint(current, checkpoint string, fn func() error) err
return nil
}

func (c *Context) completeCheckpoint(checkpoint string) error {
c.Manifest.Checkpoint = checkpoint
return c.Manifest.Flush()
Comment thread
zreigz marked this conversation as resolved.
}

func (c *Context) Deploy(commit func() error) error {
if c.Provider.Name() == api.BYOK && c.Cloud {
return nil
Expand Down
Loading
Loading