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
|
package main
import (
"flag"
"fmt"
"os"
)
// =============================================================================
// hosty start
// =============================================================================
func cmdStart(args []string) error {
fs := flag.NewFlagSet("start", flag.ExitOnError)
fs.Usage = func() {
fmt.Print(`Usage: hosty start [-file FILE.hosty] [-name NAME] [-port PORT] [-state-dir DIR] [-ignore-version-check] [-system]
Install and start a hosty service.
Without -system: runs as a --user systemd unit (unsandboxed, quick local dev).
With -system: attaches as a root portable service (sandboxed via portablectl,
state under /var/lib/hosty). Requires root.
-file is optional if a data.hosty already exists for the given -name.
If both -file and -name are given, -name overrides the name in the file.
If -port is omitted, a free port is chosen automatically.
If -file has a lower version than the installed one, start refuses unless -ignore-version-check is set.
Options:
`)
fs.PrintDefaults()
}
file := fs.String("file", "", "Path to .hosty file (optional if -name app is already installed)")
name := fs.String("name", "", "App name (overrides name in -file)")
port := fs.String("port", "", "Port to pass to the service via HOSTY_PORT (default: free port)")
stateDir := fs.String("state-dir", "", "State directory (default: $XDG_STATE_HOME/hosty, or /var/lib/hosty with -system)")
ignoreVersionCheck := fs.Bool("ignore-version-check", false, "Allow installing an older version over a newer one")
system := fs.Bool("system", false, "Attach as a root portable service instead of a --user unit")
if err := fs.Parse(args); err != nil {
return err
}
sd, err := resolveStateDir(*stateDir, *system)
if err != nil {
return err
}
rs, err := setup(setupConfig{
hostyFile: *file,
name: *name,
port: *port,
stateDir: sd,
ignoreVersionCheck: *ignoreVersionCheck,
system: *system,
})
if err != nil {
fs.Usage()
return err
}
if rs.system {
// portablectl attach --enable already installed + enabled the units.
// Restart the app; BindsTo pulls in the FUSE unit first (ordered by After).
if err := runCmd("systemctl", "restart", rs.name+".service"); err != nil {
return fmt.Errorf("starting service: %w", err)
}
} else {
// Enable and start the fs companion service first, then the app service.
if err := runCmd("systemctl", "--user", "enable", rs.name+"-fs.service"); err != nil {
return fmt.Errorf("enabling fs service: %w", err)
}
if err := runCmd("systemctl", "--user", "restart", rs.name+"-fs.service"); err != nil {
return fmt.Errorf("starting fs service: %w", err)
}
// Enable and restart so any updated ExecStart takes effect.
if err := runCmd("systemctl", "--user", "enable", rs.name+".service"); err != nil {
return fmt.Errorf("enabling service: %w", err)
}
if err := runCmd("systemctl", "--user", "restart", rs.name+".service"); err != nil {
return fmt.Errorf("starting service: %w", err)
}
}
fmt.Fprintf(os.Stderr, "hosty: %s started on %s\n", rs.name, termLink("http://localhost:"+rs.port, "http://localhost:"+rs.port))
return nil
}
|