diff --git a/data/data.json b/data/data.json index 973a926..cc3d8a0 100644 --- a/data/data.json +++ b/data/data.json @@ -21,6 +21,10 @@ { "internal": "data/kitty", "external": "~/.config/kitty" + }, + { + "internal": "data/kitty/mocha.conf", + "url": "https://raw.githubusercontent.com/catppuccin/kitty/main/mocha.conf" } ] }, diff --git a/data/kitty/mocha.conf b/data/kitty/mocha.conf index b1e1959..3f48800 100644 --- a/data/kitty/mocha.conf +++ b/data/kitty/mocha.conf @@ -1,6 +1,6 @@ # vim:ft=kitty -## name: Catppuccin Kitty Diff Mocha +## name: Catppuccin Kitty Mocha ## author: Catppuccin Org ## license: MIT ## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf diff --git a/internal/cli/action.go b/internal/cli/action.go index 5dedd3a..f442e03 100644 --- a/internal/cli/action.go +++ b/internal/cli/action.go @@ -21,73 +21,77 @@ func (a *action) runHelp(c *cli.Context) error { } func (a *action) runInstall(c *cli.Context) error { - a.getFlags(c) - a.log("start %s\n", installCommand) - - cfg, err := a.loadConfig() + cfg, err := a.loadConfig(c, installCommand) if err != nil { return err } if err := cfg.Install(); err != nil { - return fmt.Errorf("failed to install config: %w", err) + return fmt.Errorf("config: failed to install: %w", err) } return nil } func (a *action) runUpdate(c *cli.Context) error { - a.getFlags(c) - a.log("start %s\n", updateCommand) - - cfg, err := a.loadConfig() + cfg, err := a.loadConfig(c, updateCommand) if err != nil { return err } if err := cfg.Update(); err != nil { - return fmt.Errorf("failed to update config: %w", err) + return fmt.Errorf("config: failed to update: %w", err) + } + + return nil +} + +func (a *action) runDownload(c *cli.Context) error { + cfg, err := a.loadConfig(c, downloadCommand) + if err != nil { + return err + } + + if err := cfg.Download(); err != nil { + return fmt.Errorf("config: failed to download: %w", err) } return nil } func (a *action) runClean(c *cli.Context) error { - a.getFlags(c) - a.log("start %s\n", cleanCommand) - - cfg, err := a.loadConfig() + cfg, err := a.loadConfig(c, cleanCommand) if err != nil { return err } if err := cfg.Clean(); err != nil { - return fmt.Errorf("failed to clean config: %w", err) + return fmt.Errorf("config: failed to clean: %w", err) } return nil } func (a *action) runDiff(c *cli.Context) error { - a.getFlags(c) - a.log("start %s\n", diffCommand) - - cfg, err := a.loadConfig() + cfg, err := a.loadConfig(c, diffCommand) if err != nil { return err } if err := cfg.Diff(); err != nil { - return fmt.Errorf("failed to compare config: %w", err) + return fmt.Errorf("config: failed to compare: %w", err) } return nil } -func (a *action) loadConfig() (config.Config, error) { +func (a *action) loadConfig(c *cli.Context, command string) (config.Config, error) { + a.getFlags(c) + a.log("start %s with flags %+v\n", command, a.flags) + cfgReal, cfgDemo, err := config.LoadConfig(currentDir) if err != nil { - return nil, fmt.Errorf("failed to load config: %w", err) + return nil, fmt.Errorf("config: failed to load: %w", err) } if a.flags.dryRun { diff --git a/internal/cli/app.go b/internal/cli/app.go index eb471aa..45606b6 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -13,34 +13,36 @@ const ( appName = "dotfiles" appUsage = "managing dotfiles" - // flags - verboseFlag = "verbose" - dryRunFlag = "dry-run" - - // commands installCommand = "install" - updateCommand = "update" - cleanCommand = "clean" - diffCommand = "diff" + installUsage = "install user configs from dotfiles" - // flag usages - verboseUsage = "show what is going on" - dryRunUsage = "demo mode without actually changing anything" + updateCommand = "update" + updateUsage = "update dotfiles from user configs" - // command usages - installUsage = "install user configs from dotfiles" - updateUsage = "update dotfiles from user configs" + downloadCommand = "download" + downloadUsage = "download configs from internet (theme for example)" + + cleanCommand = "clean" cleanUsage = "clean unused dotfiles" - diffUsage = "diff dotfiles with user configs" + + diffCommand = "diff" + diffUsage = "diff dotfiles with user configs" + + verboseFlag = "verbose" + verboseUsage = "show what is going on" + + dryRunFlag = "dry-run" + dryRunUsage = "demo mode without actually changing anything" currentDir = "." ) var ( - // command aliases - installAliases = []string{"i"} - updateAliases = []string{"u"} - cleanAliases = []string{"c"} + installAliases = []string{"i"} + updateAliases = []string{"u"} + downloadAliases = []string{"d"} + cleanAliases = []string{"c"} + diffAliases = []string{"df"} ) // denyOSes contains OS which is not supported @@ -92,6 +94,22 @@ func NewApp() *App { }, Action: a.runUpdate, }, + { + Name: downloadCommand, + Aliases: downloadAliases, + Usage: downloadUsage, + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: verboseFlag, + Usage: verboseUsage, + }, + &cli.BoolFlag{ + Name: dryRunFlag, + Usage: dryRunUsage, + }, + }, + Action: a.runDownload, + }, { Name: cleanCommand, Aliases: cleanAliases, @@ -109,8 +127,9 @@ func NewApp() *App { Action: a.runClean, }, { - Name: diffCommand, - Usage: diffUsage, + Name: diffCommand, + Aliases: diffAliases, + Usage: diffUsage, Flags: []cli.Flag{ &cli.BoolFlag{ Name: verboseFlag, diff --git a/internal/config/config.go b/internal/config/config.go index d2c29e2..d9ff5a4 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,8 +3,10 @@ package config import ( "encoding/json" "fmt" + "net/http" "os" "path/filepath" + "time" ) const ( @@ -17,6 +19,7 @@ type Config interface { Update() error Clean() error Diff() error + Download() error } type configApps struct { @@ -30,7 +33,8 @@ type App struct { type Path struct { Internal string `json:"internal"` - External string `json:"external"` + External string `json:"external,omitempty"` + URL string `json:"url,omitempty"` } // LoadConfig return config, configDemo @@ -47,6 +51,9 @@ func LoadConfig(path string) (*configReal, *configDemo, error) { } cfgReal := configReal{ + httpClient: &http.Client{ + Timeout: time.Second * 5, + }, configApps: cfgApps, } diff --git a/internal/config/config_demo.go b/internal/config/config_demo.go index 81f3ceb..e88f13c 100644 --- a/internal/config/config_demo.go +++ b/internal/config/config_demo.go @@ -11,6 +11,10 @@ var _ Config = (*configDemo)(nil) func (c *configDemo) Install() error { for _, app := range c.Apps { for _, p := range app.Paths { + if p.External == "" { + continue + } + fmt.Printf("Replace %s -> %s\n", p.Internal, p.External) } } @@ -21,6 +25,10 @@ func (c *configDemo) Install() error { func (c *configDemo) Update() error { for _, app := range c.Apps { for _, p := range app.Paths { + if p.External == "" { + continue + } + fmt.Printf("Replace %s -> %s\n", p.External, p.Internal) } } @@ -28,6 +36,20 @@ func (c *configDemo) Update() error { return nil } +func (c *configDemo) Download() error { + for _, app := range c.Apps { + for _, p := range app.Paths { + if p.URL == "" { + continue + } + + fmt.Printf("Download %s -> %s\n", p.URL, p.Internal) + } + } + + return nil +} + func (c *configDemo) Clean() error { unusedDirs, err := getUnusedDirs(c.Apps) if err != nil { diff --git a/internal/config/config_real.go b/internal/config/config_real.go index 7ded171..2f902df 100644 --- a/internal/config/config_real.go +++ b/internal/config/config_real.go @@ -2,6 +2,8 @@ package config import ( "fmt" + "io" + "net/http" "os" "path/filepath" @@ -10,6 +12,7 @@ import ( ) type configReal struct { + httpClient *http.Client configApps } @@ -19,6 +22,10 @@ var _ Config = (*configReal)(nil) func (c *configReal) Install() error { for _, app := range c.Apps { for _, p := range app.Paths { + if p.External == "" { + continue + } + if err := copy.Replace(p.Internal, p.External); err != nil { return fmt.Errorf("failed to replace %s -> %s: %w", p.Internal, p.External, err) } @@ -32,6 +39,10 @@ func (c *configReal) Install() error { func (c *configReal) Update() error { for _, app := range c.Apps { for _, p := range app.Paths { + if p.External == "" { + continue + } + if err := copy.Replace(p.External, p.Internal); err != nil { return fmt.Errorf("failed to replace %s -> %s: %w", p.External, p.Internal, err) } @@ -41,6 +52,34 @@ func (c *configReal) Update() error { return nil } +func (c *configReal) Download() error { + for _, app := range c.Apps { + for _, p := range app.Paths { + if p.URL == "" { + continue + } + + httpRsp, err := c.httpClient.Get(p.URL) + if err != nil { + return fmt.Errorf("http client: failed to get: %w", err) + } + + data, err := io.ReadAll(httpRsp.Body) + if err != nil { + return fmt.Errorf("io: failed to read all: %w", err) + } + + if err := os.WriteFile(p.Internal, data, 0o600); err != nil { + return fmt.Errorf("os: failed to write file: %w", err) + } + + httpRsp.Body.Close() + } + } + + return nil +} + // Clean remove unused config inside config dir func (c *configReal) Clean() error { unusedDirs, err := getUnusedDirs(c.Apps)