A small Go tool that wraps the `/etc/hosts` + `once deploy --disable-tls` recipe needed to host a Once app on a single-label `<name>.local`. Adds the hosts entry, runs the deploy, polls /up, and on removal cleans the hosts line that the Once UI orphans (including the orphan-only case, where the app is already gone). Bubble Tea TUI for interactive use; non-interactive CLI for scripts. Curated apps live in /etc/once-add/apps.toml; seeded with Writebook + Campfire on first run.
64 lines
1.8 KiB
Go
64 lines
1.8 KiB
Go
// 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)
|
|
}
|