From 8a3cff0dd7eb88cadb73a6df4e14f85450d63317 Mon Sep 17 00:00:00 2001 From: bndw Date: Sat, 24 Jan 2026 16:55:52 -0800 Subject: Add auto-generated subdomain feature When a base domain is configured on a host (e.g., apps.example.com), deployments automatically get a subdomain ({name}.apps.example.com). Custom --domain can still be provided to route both domains. - Add BaseDomain field to Host state - Add --base-domain flag to host init - Add 'ship host set-domain' command to update base domain - Update deploy flow to auto-generate subdomains - Fix error display (errors were being silently swallowed) - Remove placeholder email from Caddyfile template --- cmd/ship/host/set_domain.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 cmd/ship/host/set_domain.go (limited to 'cmd/ship/host/set_domain.go') diff --git a/cmd/ship/host/set_domain.go b/cmd/ship/host/set_domain.go new file mode 100644 index 0000000..fed3b31 --- /dev/null +++ b/cmd/ship/host/set_domain.go @@ -0,0 +1,76 @@ +package host + +import ( + "fmt" + + "github.com/bdw/ship/internal/state" + "github.com/spf13/cobra" +) + +var setDomainCmd = &cobra.Command{ + Use: "set-domain [domain]", + Short: "Set base domain for auto-generated subdomains", + Long: `Set the base domain used to auto-generate subdomains for deployments. + +When a base domain is configured (e.g., apps.example.com), every deployment +will automatically get a subdomain ({name}.apps.example.com). + +Examples: + ship host set-domain apps.example.com # Set base domain + ship host set-domain --clear # Remove base domain`, + RunE: runSetDomain, +} + +func init() { + setDomainCmd.Flags().Bool("clear", false, "Clear the base domain") +} + +func runSetDomain(cmd *cobra.Command, args []string) error { + st, err := state.Load() + if err != nil { + return fmt.Errorf("error loading state: %w", err) + } + + host, _ := cmd.Flags().GetString("host") + if host == "" { + host = st.GetDefaultHost() + } + + if host == "" { + return fmt.Errorf("--host is required") + } + + clear, _ := cmd.Flags().GetBool("clear") + + if !clear && len(args) == 0 { + // Show current base domain + hostState := st.GetHost(host) + if hostState.BaseDomain == "" { + fmt.Printf("No base domain configured for %s\n", host) + } else { + fmt.Printf("Base domain for %s: %s\n", host, hostState.BaseDomain) + } + return nil + } + + hostState := st.GetHost(host) + + if clear { + hostState.BaseDomain = "" + if err := st.Save(); err != nil { + return fmt.Errorf("error saving state: %w", err) + } + fmt.Printf("Cleared base domain for %s\n", host) + return nil + } + + hostState.BaseDomain = args[0] + if err := st.Save(); err != nil { + return fmt.Errorf("error saving state: %w", err) + } + + fmt.Printf("Set base domain for %s: %s\n", host, args[0]) + fmt.Println("\nNew deployments will automatically use subdomains like:") + fmt.Printf(" myapp.%s\n", args[0]) + return nil +} -- cgit v1.2.3