From 778bef5ee6941056e06326d1eaaa6956d7307a85 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 18 Apr 2026 14:40:17 -0700 Subject: Remove Go implementation — ship is skills-only now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skills/ directory fully replaces the old Go CLI. Drop all Go source, build files, planning docs, and the stale SECURITY.md (which described the old git-user push-deploy model that no longer exists). Trim .gitignore to match the new tree. --- cmd/ship/deploy.go | 210 ----------------------------------------------------- 1 file changed, 210 deletions(-) delete mode 100644 cmd/ship/deploy.go (limited to 'cmd/ship/deploy.go') diff --git a/cmd/ship/deploy.go b/cmd/ship/deploy.go deleted file mode 100644 index 7d498b2..0000000 --- a/cmd/ship/deploy.go +++ /dev/null @@ -1,210 +0,0 @@ -package main - -import ( - "crypto/rand" - "encoding/hex" - "fmt" - "regexp" - "strings" - "time" - - "github.com/bdw/ship/internal/detect" - "github.com/bdw/ship/internal/output" - "github.com/bdw/ship/internal/state" -) - -// deployV2 implements the new agent-first deploy interface. -// Usage: ship [PATH] [FLAGS] -// PATH defaults to "." if not provided. -func deployV2(path string, opts deployV2Options) { - start := time.Now() - - // Validate name if provided - if opts.Name != "" { - if err := validateNameV2(opts.Name); err != nil { - output.PrintAndExit(err) - } - } - - // Parse TTL if provided - var ttlDuration time.Duration - if opts.TTL != "" { - var err error - ttlDuration, err = parseTTL(opts.TTL) - if err != nil { - output.PrintAndExit(output.Err(output.ErrInvalidTTL, err.Error())) - } - } - - // Get host configuration - st, err := state.Load() - if err != nil { - output.PrintAndExit(output.Err(output.ErrHostNotConfigured, "failed to load state: "+err.Error())) - } - - hostName := opts.Host - if hostName == "" { - hostName = st.DefaultHost - } - if hostName == "" { - output.PrintAndExit(output.Err(output.ErrHostNotConfigured, "no host specified and no default host configured. Run: ship host init")) - } - - hostConfig := st.GetHost(hostName) - if hostConfig.BaseDomain == "" { - output.PrintAndExit(output.Err(output.ErrHostNotConfigured, fmt.Sprintf("host %q has no base domain configured. Run: ship host init", hostName))) - } - - // Auto-detect project type - result := detect.Detect(path) - if result.Error != nil { - output.PrintAndExit(result.Error) - } - - // Generate name if not provided - name := opts.Name - if name == "" { - name = generateName() - } - - // Build URL: use custom domain if provided, otherwise use subdomain - var url string - if opts.Domain != "" { - url = fmt.Sprintf("https://%s", opts.Domain) - } else { - url = fmt.Sprintf("https://%s.%s", name, hostConfig.BaseDomain) - } - - // Build deploy context - ctx := &deployContext{ - SSHHost: hostName, - HostConfig: hostConfig, - Name: name, - Path: result.Path, - URL: url, - Opts: opts, - } - - // Deploy based on type - var deployErr *output.ErrorResponse - switch result.Type { - case detect.TypeStatic: - deployErr = deployStaticV2(ctx) - case detect.TypeDocker: - deployErr = deployDockerV2(ctx) - case detect.TypeBinary: - deployErr = deployBinaryV2(ctx) - } - - if deployErr != nil { - deployErr.Name = name - deployErr.URL = url - output.PrintAndExit(deployErr) - } - - // Set TTL if specified - if ttlDuration > 0 { - if err := setTTLV2(ctx, ttlDuration); err != nil { - // Non-fatal, deploy succeeded - // TODO: log warning - } - } - - // Health check - var healthResult *output.HealthResult - if opts.Health != "" || result.Type == detect.TypeStatic { - endpoint := opts.Health - if endpoint == "" { - endpoint = "/" - } - healthResult, deployErr = runHealthCheck(url, endpoint) - if deployErr != nil { - deployErr.Name = name - deployErr.URL = url - output.PrintAndExit(deployErr) - } - } - - // Build response - resp := &output.DeployResponse{ - Status: "ok", - Name: name, - URL: url, - Type: string(result.Type), - TookMs: time.Since(start).Milliseconds(), - Health: healthResult, - } - - if ttlDuration > 0 { - resp.Expires = time.Now().Add(ttlDuration).UTC().Format(time.RFC3339) - } - - output.PrintAndExit(resp) -} - -type deployV2Options struct { - Name string - Host string - Domain string - Health string - TTL string - Env []string - EnvFile string - ContainerPort int // Port the container listens on (default 80 for Docker) - Pretty bool -} - -// deployContext holds all info needed for a deploy -type deployContext struct { - SSHHost string // SSH connection string (config alias or user@host) - HostConfig *state.Host // Host configuration - Name string // Deploy name - Path string // Local path to deploy - URL string // Full URL after deploy - Opts deployV2Options -} - -// validateNameV2 checks if name matches allowed pattern -func validateNameV2(name string) *output.ErrorResponse { - // Must be lowercase alphanumeric with hyphens, 1-63 chars - pattern := regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$|^[a-z0-9]$`) - if !pattern.MatchString(name) { - return output.Err(output.ErrInvalidName, - "name must be lowercase alphanumeric with hyphens, 1-63 characters") - } - return nil -} - -// generateName creates a random deploy name -func generateName() string { - bytes := make([]byte, 3) - rand.Read(bytes) - return "ship-" + hex.EncodeToString(bytes) -} - -// parseTTL converts duration strings like "1h", "7d" to time.Duration -func parseTTL(s string) (time.Duration, error) { - s = strings.TrimSpace(s) - if s == "" { - return 0, nil - } - - // Handle days specially (not supported by time.ParseDuration) - if strings.HasSuffix(s, "d") { - days := strings.TrimSuffix(s, "d") - var d int - _, err := fmt.Sscanf(days, "%d", &d) - if err != nil { - return 0, fmt.Errorf("invalid TTL: %s", s) - } - return time.Duration(d) * 24 * time.Hour, nil - } - - d, err := time.ParseDuration(s) - if err != nil { - return 0, fmt.Errorf("invalid TTL: %s", s) - } - return d, nil -} - -// Deploy implementations are in deploy_impl_v2.go -- cgit v1.2.3