// Package config loads the curated app list for the once-add wizard from // /etc/once-add/apps.toml, writing a default seed if the file is absent. package config import ( "os" "path/filepath" "github.com/BurntSushi/toml" ) // Path is the on-disk location of the curated app list. Edit it to add apps; // no rebuild of once-add is needed. const Path = "/etc/once-add/apps.toml" // App is one curated entry shown in the wizard's app picker. type App struct { Name string `toml:"name"` // display name, e.g. "Writebook" Image string `toml:"image"` // container image ref Description string `toml:"description"` // one-line description Suggested string `toml:"suggested"` // pre-fills the hostname field } type file struct { App []App `toml:"app"` } const defaultSeed = `# once-add curated apps. Edit freely — add [[app]] blocks; no rebuild needed. # 'suggested' pre-fills the hostname field in the wizard. [[app]] name = "Writebook" image = "ghcr.io/basecamp/writebook" description = "Books & documentation" suggested = "book" [[app]] name = "Campfire" image = "ghcr.io/basecamp/once-campfire" description = "Team chat" suggested = "chat" ` // Load reads the curated app list, writing the default seed first if the file // does not yet exist. Writing the seed requires root (once-add runs as root). func Load() ([]App, error) { if _, err := os.Stat(Path); os.IsNotExist(err) { if err := writeDefault(); err != nil { return nil, err } } var f file if _, err := toml.DecodeFile(Path, &f); err != nil { return nil, err } return f.App, nil } func writeDefault() error { if err := os.MkdirAll(filepath.Dir(Path), 0o755); err != nil { return err } return os.WriteFile(Path, []byte(defaultSeed), 0o644) }