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/host.go | 3 ++ cmd/ship/host/init.go | 8 +++-- cmd/ship/host/set_domain.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 cmd/ship/host/set_domain.go (limited to 'cmd/ship/host') diff --git a/cmd/ship/host/host.go b/cmd/ship/host/host.go index 603a946..81403f9 100644 --- a/cmd/ship/host/host.go +++ b/cmd/ship/host/host.go @@ -15,4 +15,7 @@ func init() { Cmd.AddCommand(statusCmd) Cmd.AddCommand(updateCmd) Cmd.AddCommand(sshCmd) + Cmd.AddCommand(setDomainCmd) + + initCmd.Flags().String("base-domain", "", "Base domain for auto-generated subdomains (e.g., apps.example.com)") } diff --git a/cmd/ship/host/init.go b/cmd/ship/host/init.go index ea25922..27f67af 100644 --- a/cmd/ship/host/init.go +++ b/cmd/ship/host/init.go @@ -26,6 +26,7 @@ func runInit(cmd *cobra.Command, args []string) error { if host == "" { host = st.GetDefaultHost() } + baseDomain, _ := cmd.Flags().GetString("base-domain") if host == "" { return fmt.Errorf("--host is required") @@ -64,7 +65,6 @@ func runInit(cmd *cobra.Command, args []string) error { fmt.Println("-> Configuring Caddy...") caddyfile := `{ - email admin@example.com } import /etc/caddy/sites-enabled/* @@ -100,7 +100,11 @@ import /etc/caddy/sites-enabled/* fmt.Println(" Caddy is active") } - st.GetHost(host) + hostState := st.GetHost(host) + if baseDomain != "" { + hostState.BaseDomain = baseDomain + fmt.Printf(" Base domain: %s\n", baseDomain) + } if st.GetDefaultHost() == "" { st.SetDefaultHost(host) fmt.Printf(" Set %s as default host\n", host) 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