diff options
| author | Claude <noreply@anthropic.com> | 2026-08-05 23:22:25 -0500 |
|---|---|---|
| committer | Claude <noreply@anthropic.com> | 2026-08-05 23:22:25 -0500 |
| commit | fc0c6951d7ac36dd8f30ace929991197dc99ae54 (patch) | |
| tree | 46fa11cc103ebe7805bf42bc3e4bb105e6b49721 | |
| parent | 9f7c9f2f31acd7bc544dd988af1c8c2fe63ec504 (diff) | |
Simplify: centralize paths in layout, dedup zfs snapshot refs, remove dead code
- Add layout.Config methods (ReleasesDir, PostReceiveHookPath, RCScriptPath,
RCConfPath) so main.go and jailmgr no longer hand-build paths that layout
already owns.
- Extract zfsutil.snapRef to replace three inline dataset@tag Sprintfs.
- Remove seven unused exported functions (zfsutil.Exists, jailmgr.Stop,
sh.RunWithStdinFile, and four dead layout.Config path methods) and their
now-unused imports.
| -rw-r--r-- | cmd/skiff/main.go | 6 | ||||
| -rw-r--r-- | internal/jailmgr/jailmgr.go | 14 | ||||
| -rw-r--r-- | internal/layout/layout.go | 49 | ||||
| -rw-r--r-- | internal/sh/sh.go | 21 | ||||
| -rw-r--r-- | internal/zfsutil/zfsutil.go | 12 |
5 files changed, 33 insertions, 69 deletions
diff --git a/cmd/skiff/main.go b/cmd/skiff/main.go index e709227..33aaf5e 100644 --- a/cmd/skiff/main.go +++ b/cmd/skiff/main.go @@ -7,7 +7,6 @@ import ( "flag" "fmt" "os" - "path/filepath" "strings" "skiff/internal/appmeta" @@ -90,7 +89,7 @@ func cmdNewApp(args []string) error { return fmt.Errorf("clone base dataset: %w", err) } - if err := os.MkdirAll(filepath.Join(cfg.AppDir(*name), "releases"), 0755); err != nil { + if err := os.MkdirAll(cfg.ReleasesDir(*name), 0755); err != nil { return fmt.Errorf("create app dir: %w", err) } @@ -115,8 +114,7 @@ func cmdNewApp(args []string) error { if err != nil { return err } - hookPath := filepath.Join(cfg.RepoPath(*name), "hooks", "post-receive") - if err := os.WriteFile(hookPath, []byte(hook), 0755); err != nil { + if err := os.WriteFile(cfg.PostReceiveHookPath(*name), []byte(hook), 0755); err != nil { return fmt.Errorf("install post-receive hook: %w", err) } diff --git a/internal/jailmgr/jailmgr.go b/internal/jailmgr/jailmgr.go index 54cf614..b6a5d28 100644 --- a/internal/jailmgr/jailmgr.go +++ b/internal/jailmgr/jailmgr.go @@ -17,11 +17,7 @@ import ( // WriteConf renders jail.conf.d/<app>.conf and the jail's own rc.d service // script + rc.conf enable line. Called once, from `skiff new-app`. func WriteConf(cfg layout.Config, app, ip string) error { - conf, err := tmpl.JailConf(tmpl.JailConfData{ - Name: app, - JailRoot: cfg.JailRoot(app), - IP: ip, - }) + conf, err := tmpl.JailConf(tmpl.JailConfData{Name: app, JailRoot: cfg.JailRoot(app), IP: ip}) if err != nil { return err } @@ -33,7 +29,7 @@ func WriteConf(cfg layout.Config, app, ip string) error { if err != nil { return err } - rcPath := filepath.Join(cfg.JailRoot(app), "usr", "local", "etc", "rc.d", app) + rcPath := cfg.RCScriptPath(app) if err := os.MkdirAll(filepath.Dir(rcPath), 0755); err != nil { return err } @@ -42,7 +38,7 @@ func WriteConf(cfg layout.Config, app, ip string) error { } rcConfLine := fmt.Sprintf("\n%s_enable=\"YES\"\n", app) - f, err := os.OpenFile(filepath.Join(cfg.JailRoot(app), "etc", "rc.conf"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + f, err := os.OpenFile(cfg.RCConfPath(app), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("open jail rc.conf: %w", err) } @@ -57,10 +53,6 @@ func Start(ctx context.Context, out io.Writer, app string) error { return sh.Run(ctx, out, "", "service", "jail", "start", app) } -func Stop(ctx context.Context, out io.Writer, app string) error { - return sh.Run(ctx, out, "", "service", "jail", "stop", app) -} - // Restart restarts an already-running jail, or starts it if this is its // first deploy and it isn't up yet. func Restart(ctx context.Context, out io.Writer, app string) error { diff --git a/internal/layout/layout.go b/internal/layout/layout.go index 89e6848..636b8ce 100644 --- a/internal/layout/layout.go +++ b/internal/layout/layout.go @@ -55,6 +55,20 @@ func (c Config) NginxConfPath(app string) string { return filepath.Join(c.NginxConfDir, app+".conf") } +func (c Config) PostReceiveHookPath(app string) string { + return filepath.Join(c.RepoPath(app), "hooks", "post-receive") +} + +// RCScriptPath and RCConfPath are paths inside the jail's own chroot, not +// the host's rc.d/rc.conf. +func (c Config) RCScriptPath(app string) string { + return filepath.Join(c.JailRoot(app), "usr", "local", "etc", "rc.d", app) +} + +func (c Config) RCConfPath(app string) string { + return filepath.Join(c.JailRoot(app), "etc", "rc.conf") +} + // Everything below is Capistrano-style: each deploy gets its own immutable // releases/<sha> checkout (built in place, inside the jail's own dataset), // `current` is a symlink to whichever release is live, and data.db/env are @@ -63,8 +77,12 @@ func (c Config) AppDir(app string) string { return filepath.Join(c.JailRoot(app), "app") } +func (c Config) ReleasesDir(app string) string { + return filepath.Join(c.AppDir(app), "releases") +} + func (c Config) ReleaseDir(app, sha string) string { - return filepath.Join(c.AppDir(app), "releases", sha) + return filepath.Join(c.ReleasesDir(app), sha) } func (c Config) CurrentRelease(app string) string { @@ -73,35 +91,14 @@ func (c Config) CurrentRelease(app string) string { // ReleaseDirInJail is releases/<sha> as seen from inside the jail's own // chroot, not the host. The `current` symlink must be written with this -// form as its target: it's dereferenced by the jail's rc.d script (execing -// CurrentBin), where a host-absolute path like ReleaseDir's resolves -// relative to the jail root instead and never exists. +// form as its target: it's dereferenced by the jail's rc.d script (which +// execs /app/current/app, per internal/tmpl/rc.d.tmpl's command_args), +// where a host-absolute path like ReleaseDir's resolves relative to the +// jail root instead and never exists. func (c Config) ReleaseDirInJail(sha string) string { return filepath.Join("/app", "releases", sha) } -// CurrentBin is the path the jail's rc.d script execs - must match -// internal/tmpl/rc.d.tmpl's command_args. -func (c Config) CurrentBin(app string) string { - return filepath.Join(c.CurrentRelease(app), "app") -} - -func (c Config) MakefilePath(app, sha string) string { - return filepath.Join(c.ReleaseDir(app, sha), "Makefile") -} - -func (c Config) DataDB(app string) string { - return filepath.Join(c.AppDir(app), "data.db") -} - -func (c Config) EnvFile(app string) string { - return filepath.Join(c.AppDir(app), "env") -} - func (c Config) MetaPath(app string) string { return filepath.Join(c.AppDir(app), "meta.json") } - -func (c Config) SnapshotName(app, sha string) string { - return fmt.Sprintf("%s@pre-%s", c.AppDataset(app), sha) -} diff --git a/internal/sh/sh.go b/internal/sh/sh.go index b5f6f49..6e12886 100644 --- a/internal/sh/sh.go +++ b/internal/sh/sh.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "io" - "os" "os/exec" ) @@ -43,23 +42,3 @@ func CombinedOutput(ctx context.Context, dir string, name string, args ...string b, err := cmd.CombinedOutput() return string(b), err } - -// RunWithStdinFile runs a command with stdinPath fed to its stdin, e.g. -// piping a migration file into `sqlite3 db`. -func RunWithStdinFile(ctx context.Context, out io.Writer, dir, stdinPath, name string, args ...string) error { - f, err := os.Open(stdinPath) - if err != nil { - return fmt.Errorf("open %s: %w", stdinPath, err) - } - defer f.Close() - - cmd := exec.CommandContext(ctx, name, args...) - cmd.Dir = dir - cmd.Stdin = f - cmd.Stdout = out - cmd.Stderr = out - if err := cmd.Run(); err != nil { - return fmt.Errorf("%s %v < %s: %w", name, args, stdinPath, err) - } - return nil -} diff --git a/internal/zfsutil/zfsutil.go b/internal/zfsutil/zfsutil.go index 3d3bd40..8f1b0e2 100644 --- a/internal/zfsutil/zfsutil.go +++ b/internal/zfsutil/zfsutil.go @@ -11,20 +11,18 @@ import ( "skiff/internal/sh" ) -func Exists(ctx context.Context, dataset string) bool { - _, err := sh.Output(ctx, "", "zfs", "list", "-H", "-o", "name", dataset) - return err == nil +func snapRef(dataset, tag string) string { + return fmt.Sprintf("%s@%s", dataset, tag) } // Clone creates a new dataset from a (read-only) snapshot of base, mounted // at mountpoint. base must already have a snapshot named baseSnapshot. func Clone(ctx context.Context, out io.Writer, base, baseSnapshot, dataset, mountpoint string) error { - src := fmt.Sprintf("%s@%s", base, baseSnapshot) - return sh.Run(ctx, out, "", "zfs", "clone", "-o", "mountpoint="+mountpoint, src, dataset) + return sh.Run(ctx, out, "", "zfs", "clone", "-o", "mountpoint="+mountpoint, snapRef(base, baseSnapshot), dataset) } func Snapshot(ctx context.Context, out io.Writer, dataset, tag string) error { - return sh.Run(ctx, out, "", "zfs", "snapshot", fmt.Sprintf("%s@%s", dataset, tag)) + return sh.Run(ctx, out, "", "zfs", "snapshot", snapRef(dataset, tag)) } // Rollback discards everything written to dataset since the snapshot named @@ -32,7 +30,7 @@ func Snapshot(ctx context.Context, out io.Writer, dataset, tag string) error { // used both for the automatic post-health-check-failure rollback and for a // manual `skiff rollback` days later. func Rollback(ctx context.Context, out io.Writer, dataset, tag string) error { - return sh.Run(ctx, out, "", "zfs", "rollback", "-r", fmt.Sprintf("%s@%s", dataset, tag)) + return sh.Run(ctx, out, "", "zfs", "rollback", "-r", snapRef(dataset, tag)) } // Snapshots lists snapshot names (without the dataset@ prefix), newest last. |
