fix: install versioned default theme state

This commit is contained in:
hl-valdemar
2026-10-23 10:23:28 +02:00
parent 92200c76ec
commit 90cf08729e
4 changed files with 207 additions and 8 deletions
+5 -5
View File
@@ -12,20 +12,20 @@ CONFIG_MARKER := $(CONFIG_DIR)/.tint-installed-config
STATE_MARKER := $(CONFIG_DIR)/.tint-installed-state STATE_MARKER := $(CONFIG_DIR)/.tint-installed-state
.PHONY: install uninstall .PHONY: install uninstall
install: install: default-state.toml config.toml
@command -v tinty >/dev/null || { echo "tinty is required" >&2; exit 1; } @command -v tinty >/dev/null || { echo "tinty is required" >&2; exit 1; }
@command -v dark-notify >/dev/null || { echo "dark-notify is required" >&2; exit 1; } @command -v dark-notify >/dev/null || { echo "dark-notify is required" >&2; exit 1; }
mkdir -p "$(BIN_DIR)" "$(CONFIG_DIR)" "$(HERDR_DIR)" "$(LAUNCH_AGENT_DIR)" "$(dir $(DARK_NOTIFY_LOG))" mkdir -p "$(BIN_DIR)" "$(CONFIG_DIR)" "$(HERDR_DIR)" "$(LAUNCH_AGENT_DIR)" "$(dir $(DARK_NOTIFY_LOG))"
go build -o "$(BIN_DIR)/tint" . go build -o "$(BIN_DIR)/tint" .
@if [ "$(CURDIR)" != "$(CONFIG_DIR)" ]; then \ @set -e; if [ "$(CURDIR)" != "$(CONFIG_DIR)" ]; then \
if [ -e "$(CONFIG_MARKER)" ] || [ ! -e "$(CONFIG_DIR)/config.toml" ]; then \ if [ -e "$(CONFIG_MARKER)" ] || [ ! -e "$(CONFIG_DIR)/config.toml" ]; then \
cp config.toml "$(CONFIG_DIR)/config.toml"; \ cp config.toml "$(CONFIG_DIR)/config.toml"; \
touch "$(CONFIG_MARKER)"; \ touch "$(CONFIG_MARKER)"; \
fi; \ fi; \
if [ ! -e "$(CONFIG_DIR)/state.toml" ]; then \
cp state.toml "$(CONFIG_DIR)/state.toml"; \
touch "$(STATE_MARKER)"; \
fi; \ fi; \
if [ ! -e "$(CONFIG_DIR)/state.toml" ]; then \
cp default-state.toml "$(CONFIG_DIR)/state.toml"; \
touch "$(STATE_MARKER)"; \
fi fi
@if [ ! -e "$(HERDR_CONFIG)" ]; then \ @if [ ! -e "$(HERDR_CONFIG)" ]; then \
touch "$(HERDR_CONFIG)" "$(HERDR_MARKER)"; \ touch "$(HERDR_CONFIG)" "$(HERDR_MARKER)"; \
+2 -2
View File
@@ -35,7 +35,7 @@ The installer:
- Builds `~/.local/bin/tint`. - Builds `~/.local/bin/tint`.
- Installs `config.toml` under `$XDG_CONFIG_HOME/tinted-theming/tinty`, or `~/.config/tinted-theming/tinty` when `XDG_CONFIG_HOME` is unset. - Installs `config.toml` under `$XDG_CONFIG_HOME/tinted-theming/tinty`, or `~/.config/tinted-theming/tinty` when `XDG_CONFIG_HOME` is unset.
- Copies `state.toml` only when no state file already exists. - Copies the versioned `default-state.toml` to the installed `state.toml` only when no state file already exists.
- Creates `~/.config/herdr/config.toml` if needed. - Creates `~/.config/herdr/config.toml` if needed.
- Runs `tinty sync` to download the configured theme repositories. - Runs `tinty sync` to download the configured theme repositories.
- Creates and loads a `dark-notify` LaunchAgent for automatic macOS appearance switching. - Creates and loads a `dark-notify` LaunchAgent for automatic macOS appearance switching.
@@ -180,7 +180,7 @@ After changing or updating the wrapper source:
make install make install
``` ```
Existing `state.toml` content is preserved. Existing `state.toml` content is preserved. Missing state is recreated from `default-state.toml`, including the bundled named pairs and favorites. The source directory's ignored `state.toml` is personal state and is not used by the installer.
## Uninstall ## Uninstall
+104
View File
@@ -0,0 +1,104 @@
[current]
light = "base16-swamp-light"
dark = "base16-darktooth"
[[favorites.pairs]]
name = "gruvbox-material-medium"
dark = "base16-gruvbox-material-dark-medium"
light = "base16-gruvbox-material-light-medium"
[[favorites.pairs]]
name = "gruvbox-pale"
dark = "base16-gruvbox-dark-pale"
light = "base16-gruvbox-light-soft"
[[favorites.pairs]]
name = "swamp"
dark = "base16-swamp-dark"
light = "base16-swamp-light"
[favorites]
themes = [
"base16-0x96f",
"base16-arroz-con-dulce",
"base16-ascendancy",
"base16-atelier-plateau-light",
"base16-cacao",
"base16-caroline",
"base16-chinoiserie-midnight",
"base16-corduroy",
"base16-cupcake",
"base16-darktooth",
"base16-embers",
"base16-gruvbox-dark-pale",
"base16-pastelon-de-amarillos-dark",
"base16-precious-light-warm",
"base16-rose-pine-dawn",
"base16-selenized-light",
"base16-swamp-dark",
"base16-swamp-light",
"base24-ic-orange-ppl",
"base24-later-this-evening",
"tinted8-gruvbox-dark",
]
[[others.pairs]]
name = "amarillos"
dark = "base16-pastelon-de-amarillos-dark"
light = "base16-pastelon-de-amarillos"
[[others.pairs]]
name = "charcoal"
dark = "base16-charcoal-dark"
light = "base16-charcoal-light"
[[others.pairs]]
name = "embers"
dark = "base16-embers"
light = "base16-embers-light"
[[others.pairs]]
name = "gruvbox-material-hard"
dark = "base16-gruvbox-material-dark-hard"
light = "base16-gruvbox-material-light-hard"
[[others.pairs]]
name = "gruvbox-material-soft"
dark = "base16-gruvbox-material-dark-soft"
light = "base16-gruvbox-material-light-soft"
[[others.pairs]]
name = "pale-swamp"
dark = "base16-gruvbox-dark-pale"
light = "base16-swamp-light"
[others]
themes = [
"base16-apprentice",
"base16-ashes",
"base16-atelier-heath",
"base16-atelier-heath-light",
"base16-black-metal",
"base16-black-metal-bathory",
"base16-black-metal-khold",
"base16-black-metal-mayhem",
"base16-chinoiserie-morandi",
"base16-corduroy-dark",
"base16-da-one-gray",
"base16-default-dark",
"base16-equilibrium-light",
"base16-helios",
"base16-kanagawa-dragon",
"base16-mezcal",
"base16-railscasts",
"base16-sandcastle",
"base16-tarot",
"base16-vesper",
"base16-zenbones",
"base24-alucard",
"base24-earthsong",
"base24-flexoki-light",
"base24-mountain",
"base24-pastelon-de-amarillos",
"base24-sleepy-hollow",
]
+95
View File
@@ -0,0 +1,95 @@
package main
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestInstallFromFreshCheckout(t *testing.T) {
tmp := t.TempDir()
source := filepath.Join(tmp, "source")
bin := filepath.Join(tmp, "bin")
for _, dir := range []string{source, bin} {
if err := os.Mkdir(dir, 0o755); err != nil {
t.Fatal(err)
}
}
entries, err := os.ReadDir(".")
if err != nil {
t.Fatal(err)
}
for _, entry := range entries {
if !entry.Type().IsRegular() || entry.Name() == "state.toml" {
continue
}
data, err := os.ReadFile(entry.Name())
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(source, entry.Name()), data, 0o644); err != nil {
t.Fatal(err)
}
}
writeExecutable(t, filepath.Join(bin, "tinty"), `#!/bin/sh
case "$1" in
sync|info) exit 0 ;;
apply) printf '%s\n' "$2" >> "$TINTY_LOG" ;;
*) exit 2 ;;
esac
`)
for _, name := range []string{"dark-notify", "launchctl"} {
writeExecutable(t, filepath.Join(bin, name), "#!/bin/sh\nexit 0\n")
}
home := filepath.Join(tmp, "home")
config := filepath.Join(home, ".config")
log := filepath.Join(tmp, "applied")
cache, err := exec.Command("go", "env", "GOMODCACHE").Output()
if err != nil {
t.Fatal(err)
}
t.Setenv("GOMODCACHE", strings.TrimSpace(string(cache)))
t.Setenv("PATH", bin+":"+os.Getenv("PATH"))
t.Setenv("TINTY_LOG", log)
install := func() {
t.Helper()
cmd := exec.Command("make", "install", "HOME="+home, "XDG_CONFIG_HOME="+config)
cmd.Dir = source
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("install failed: %v\n%s", err, output)
}
}
install()
statePath := filepath.Join(config, "tinted-theming", "tinty", "state.toml")
value, err := loadState(statePath)
if err != nil {
t.Fatalf("fresh install did not provide usable state: %v", err)
}
cmd := exec.Command(filepath.Join(home, ".local", "bin", "tint"), "swamp")
cmd.Env = append(os.Environ(), "TINT_STATE_FILE="+statePath, "TINTY_PAIR_APPEARANCE=dark")
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("selecting an installed pair failed: %v\n%s", err, output)
}
if got := strings.TrimSpace(readFile(t, log)); got != "base16-swamp-dark" {
t.Fatalf("applied theme = %q, want base16-swamp-dark", got)
}
value.Current = currentPair{Light: "personal-light", Dark: "personal-dark"}
if err := saveState(statePath, value); err != nil {
t.Fatal(err)
}
before, err := os.ReadFile(statePath)
if err != nil {
t.Fatal(err)
}
install()
after, err := os.ReadFile(statePath)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(before, after) {
t.Fatal("reinstall overwrote personal state")
}
}