Add TUI add and preview commands

Use charmbracelet/huh for an interactive add flow. Add a preview
subcommand to enter/exit per-project pre-release tags. Move stamp.toml
into .stamp/ and add ConfigPath helper. Prefer --snapshot then project
PreTag when computing versions and promote bumps to pre-release when
appropriate. Export CurrentVersion and add required TUI deps to go.mod.
This commit is contained in:
Thomas
2026-03-12 22:59:20 +01:00
parent fb347eaa54
commit 8049c505a0
12 changed files with 323 additions and 87 deletions

View File

@@ -8,8 +8,15 @@ import (
"github.com/BurntSushi/toml"
)
const DefaultChangesetDir = ".stamp"
const ConfigFileName = "stamp.toml"
const (
DefaultChangesetDir = ".stamp"
ConfigFileName = "stamp.toml"
)
// ConfigPath returns the path to stamp.toml inside the changeset directory.
func ConfigPath(repoRoot string) string {
return filepath.Join(repoRoot, DefaultChangesetDir, ConfigFileName)
}
// Config is the root configuration parsed from stamp.toml.
type Config struct {
@@ -19,8 +26,7 @@ type Config struct {
// GlobalConfig holds repo-level settings.
type GlobalConfig struct {
BaseBranch string `toml:"base_branch,omitempty"`
ChangesetDir string `toml:"changeset_dir,omitempty"`
BaseBranch string `toml:"base_branch,omitempty"`
}
// Project represents a single project in the monorepo.
@@ -29,6 +35,7 @@ type Project struct {
Path string `toml:"path"`
Version string `toml:"version"`
Changelog string `toml:"changelog,omitempty"`
PreTag string `toml:"pre_tag,omitempty"`
Publish PublishConfig `toml:"publish,omitempty"`
}
@@ -39,11 +46,9 @@ type PublishConfig struct {
Artifacts []string `toml:"artifacts"`
}
// ChangesetDir returns the configured changeset directory, defaulting to .stamp.
// ChangesetDir always returns the default changeset directory.
// The config file lives inside this directory, so it is always known.
func (c *Config) ChangesetDir() string {
if c.Config.ChangesetDir != "" {
return c.Config.ChangesetDir
}
return DefaultChangesetDir
}
@@ -93,9 +98,9 @@ func (p *PublishConfig) PublishReleases() bool {
return p.Releases == nil || *p.Releases
}
// Load reads and validates stamp.toml from the given directory.
func Load(dir string) (*Config, error) {
path := filepath.Join(dir, ConfigFileName)
// Load reads and validates stamp.toml from inside the changeset directory.
func Load(repoRoot string) (*Config, error) {
path := ConfigPath(repoRoot)
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading %s: %w", path, err)
@@ -113,9 +118,13 @@ func Load(dir string) (*Config, error) {
return &cfg, nil
}
// Save writes the config back to stamp.toml in the given directory.
func Save(dir string, cfg *Config) error {
path := filepath.Join(dir, ConfigFileName)
// Save writes the config back to stamp.toml inside the changeset directory.
func Save(repoRoot string, cfg *Config) error {
dir := filepath.Join(repoRoot, DefaultChangesetDir)
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("creating %s: %w", dir, err)
}
path := ConfigPath(repoRoot)
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("creating %s: %w", path, err)