summaryrefslogtreecommitdiffstats
path: root/cmd/ship/main.go
blob: cd2b0c1abbe7c7a0ed94d6c8581b89b025339385 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package main

import (
	"fmt"
	"os"

	"github.com/bdw/ship/cmd/ship/env"
	"github.com/bdw/ship/cmd/ship/host"
	"github.com/spf13/cobra"
)

var (
	// Persistent flags
	hostFlag string

	// Version info (set via ldflags)
	version = "dev"
	commit  = "none"
	date    = "unknown"
)

const banner = `
        ~
    ___|___
   |   _   |
  _|__|_|__|_
 |   SHIP   |  Ship apps to your VPS
  \_________/   with automatic HTTPS
   ~~~~~~~~~
`

var rootCmd = &cobra.Command{
	Use:   "ship",
	Short: "Ship apps and static sites to a VPS with automatic HTTPS",
	Long: banner + `
A CLI tool for deploying applications and static sites to a VPS.

How it works:
  Ship uses only SSH to deploy - no agents, containers, or external services.
  It uploads your binary or static website, creates a systemd service, and configures Caddy
  for automatic HTTPS. Ports are assigned automatically. Your app runs directly on the VPS
  with minimal overhead.

Requirements:
  • A VPS with SSH access (use 'ship host init' to set up a new server)
  • An SSH config entry or user@host for your server
  • A domain pointing to your VPS

Examples:
  # Deploy a Go binary
  ship --binary ./myapp --domain api.example.com

  # Deploy with auto-generated subdomain (requires base domain)
  ship --binary ./myapp --name myapp

  # Deploy a static site
  ship --static --dir ./dist --domain example.com

  # Update config without redeploying binary
  ship --name myapp --memory 512M --cpu 50%
  ship --name myapp --env DEBUG=true

  # Set up a new VPS with base domain
  ship host init --host user@vps --base-domain apps.example.com`,
	RunE:          runDeploy,
	SilenceUsage:  true,
	SilenceErrors: true,
}

func init() {
	// Persistent flags available to all subcommands
	rootCmd.PersistentFlags().StringVar(&hostFlag, "host", "", "VPS host (SSH config alias or user@host)")

	// Root command (deploy) flags
	rootCmd.Flags().String("binary", "", "Path to Go binary (for app deployment)")
	rootCmd.Flags().Bool("static", false, "Deploy as static site")
	rootCmd.Flags().String("dir", ".", "Directory to deploy (for static sites)")
	rootCmd.Flags().String("domain", "", "Custom domain (optional if base domain configured)")
	rootCmd.Flags().String("name", "", "App name (default: inferred from binary or directory)")
	rootCmd.Flags().Int("port", 0, "Port override (default: auto-allocate)")
	rootCmd.Flags().StringArray("env", nil, "Environment variable (KEY=VALUE, can be specified multiple times)")
	rootCmd.Flags().String("env-file", "", "Path to .env file")
	rootCmd.Flags().String("args", "", "Arguments to pass to binary")
	rootCmd.Flags().StringArray("file", nil, "Config file to upload to working directory (can be specified multiple times)")
	rootCmd.Flags().String("memory", "", "Memory limit (e.g., 512M, 1G)")
	rootCmd.Flags().String("cpu", "", "CPU limit (e.g., 50%, 200% for 2 cores)")

	// Add subcommands
	rootCmd.AddCommand(listCmd)
	rootCmd.AddCommand(logsCmd)
	rootCmd.AddCommand(statusCmd)
	rootCmd.AddCommand(restartCmd)
	rootCmd.AddCommand(removeCmd)
	rootCmd.AddCommand(env.Cmd)
	rootCmd.AddCommand(host.Cmd)
	rootCmd.AddCommand(uiCmd)
	rootCmd.AddCommand(versionCmd)
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}