From 19edb3a5809db35bd420536d2bf7f17352b0619e Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Mon, 30 Nov 2020 13:16:25 +0800 Subject: [PATCH] Add env and restore commands (#11) Add env and restore commands Signed-off-by: jolheiser Check for existance Signed-off-by: jolheiser Co-authored-by: jolheiser Reviewed-on: https://gitea.com/jolheiser/tmpl/pulls/11 Co-Authored-By: John Olheiser Co-Committed-By: John Olheiser --- CLI.md | 8 ++++++ DOCS.md | 9 +++++- cmd/app.go | 2 ++ cmd/env.go | 34 +++++++++++++++++++++++ cmd/list.go | 4 +-- cmd/restore.go | 39 ++++++++++++++++++++++++++ cmd/update.go | 11 +------- registry/error.go | 13 +++++++++ registry/registry.go | 65 ++++++++++++++++++++++++++++++++++++++------ registry/template.go | 2 +- 10 files changed, 165 insertions(+), 22 deletions(-) create mode 100644 cmd/env.go create mode 100644 cmd/restore.go diff --git a/CLI.md b/CLI.md index 0850bb6..c7c5a8d 100644 --- a/CLI.md +++ b/CLI.md @@ -32,6 +32,10 @@ Download a template **--branch, -b**="": Branch to clone (default: main) +## env + +Show tmpl environment variables + ## init Initialize a template @@ -44,6 +48,10 @@ List templates in the registry Remove a template +## restore + +Restore missing templates + ## save Save a local template diff --git a/DOCS.md b/DOCS.md index 57ffe31..5d70838 100644 --- a/DOCS.md +++ b/DOCS.md @@ -108,4 +108,11 @@ I realize that many users will be using GitHub, and most will likely still be us 1. `tmpl source add https://github.com github` 2. Set the env variable `TMPL_SOURCE` to `github` 2. Set the env variable `TMPL_BRANCH` to `master` -3. Happy templating! `tmpl download user/repo repo` \ No newline at end of file +3. Happy templating! `tmpl download user/repo repo` + +## Backup and Restore + +1. The simplest solution is to make a copy of your `registry.toml` (default: `~/.tmpl/registry.toml`). + * Once in the new location, you will need to use `tmpl restore`. + +2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step. \ No newline at end of file diff --git a/cmd/app.go b/cmd/app.go index 2c414c1..71c130c 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -51,9 +51,11 @@ func NewApp() *cli.App { app.Commands = []*cli.Command{ Download, + Env, Init, List, Remove, + Restore, Save, Source, Test, diff --git a/cmd/env.go b/cmd/env.go new file mode 100644 index 0000000..3ecc12d --- /dev/null +++ b/cmd/env.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "os" + + "github.com/urfave/cli/v2" + "go.jolheiser.com/beaver" + "go.jolheiser.com/beaver/color" +) + +var Env = &cli.Command{ + Name: "env", + Usage: "Show tmpl environment variables", + Description: "Show tmpl environment variables and their configuration", + Action: runEnv, +} + +func runEnv(_ *cli.Context) error { + + // Source + beaver.Infof("TMPL_SOURCE: %s", getEnv("TMPL_SOURCE")) + + // Registry Path + beaver.Infof("TMPL_REGISTRY: %s", getEnv("TMPL_REGISTRY")) + + // Branch + beaver.Infof("TMPL_BRANCH: %s", getEnv("TMPL_BRANCH")) + + return nil +} + +func getEnv(key string) string { + return color.FgHiBlue.Format(os.Getenv(key)) +} diff --git a/cmd/list.go b/cmd/list.go index ec4f5ed..4f7143d 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -25,7 +25,7 @@ func runList(_ *cli.Context) error { } wr := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - if _, err := fmt.Fprintf(wr, "NAME\tURL\tLOCAL\tUPDATED\n"); err != nil { + if _, err := fmt.Fprintf(wr, "NAME\tURL\tLOCAL\tLAST UPDATED\n"); err != nil { return err } for _, t := range reg.Templates { @@ -35,7 +35,7 @@ func runList(_ *cli.Context) error { u = t.Path local = true } - if _, err := fmt.Fprintf(wr, "%s\t%s\t%t\t%s\n", t.Name, u, local, t.Created.Format("01/02/2006")); err != nil { + if _, err := fmt.Fprintf(wr, "%s\t%s\t%t\t%s\n", t.Name, u, local, t.LastUpdate.Format("01/02/2006")); err != nil { return err } } diff --git a/cmd/restore.go b/cmd/restore.go new file mode 100644 index 0000000..b6a1ecc --- /dev/null +++ b/cmd/restore.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "os" + + "go.jolheiser.com/tmpl/cmd/flags" + "go.jolheiser.com/tmpl/registry" + + "github.com/urfave/cli/v2" + "go.jolheiser.com/beaver" +) + +var Restore = &cli.Command{ + Name: "restore", + Usage: "Restore missing templates", + Description: "Restore templates that are listed in the registry, but are missing archives", + Action: runRestore, +} + +func runRestore(_ *cli.Context) error { + reg, err := registry.Open(flags.Registry) + if err != nil { + return err + } + + var num int + for _, tmpl := range reg.Templates { + if _, err := os.Lstat(tmpl.ArchivePath()); os.IsNotExist(err) { + beaver.Infof("Restoring %s...", tmpl.Name) + if err := reg.UpdateTemplate(tmpl.Name); err != nil { + return err + } + num++ + } + } + + beaver.Infof("Restored %d templates.", num) + return nil +} diff --git a/cmd/update.go b/cmd/update.go index 38cff98..f20ae94 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -31,16 +31,7 @@ func runUpdate(ctx *cli.Context) error { return err } - if err := reg.RemoveTemplate(tmpl.Name); err != nil { - return err - } - - if tmpl.Path != "" { - _, err = reg.SaveTemplate(tmpl.Name, tmpl.Path) - } else { - _, err = reg.DownloadTemplate(tmpl.Name, tmpl.Repository, tmpl.Branch) - } - if err != nil { + if err := reg.UpdateTemplate(tmpl.Name); err != nil { return err } diff --git a/registry/error.go b/registry/error.go index d85f168..f7950f4 100644 --- a/registry/error.go +++ b/registry/error.go @@ -2,6 +2,19 @@ package registry import "fmt" +type ErrTemplateExists struct { + Name string +} + +func (e ErrTemplateExists) Error() string { + return fmt.Sprintf("template %s already exists", e.Name) +} + +func IsErrTemplateExists(err error) bool { + _, ok := err.(ErrTemplateExists) + return ok +} + type ErrTemplateNotFound struct { Name string } diff --git a/registry/registry.go b/registry/registry.go index 79eafa9..d2ddf01 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -50,12 +50,16 @@ func (r *Registry) GetTemplate(name string) (*Template, error) { // DownloadTemplate downloads and adds a new Template to the Registry func (r *Registry) DownloadTemplate(name, repo, branch string) (*Template, error) { + if _, err := r.GetTemplate(name); err == nil { + return nil, ErrTemplateExists{Name: name} + } + t := &Template{ reg: r, Name: name, Repository: repo, Branch: branch, - Created: time.Now(), + LastUpdate: time.Now(), } r.Templates = append(r.Templates, t) @@ -68,11 +72,15 @@ func (r *Registry) DownloadTemplate(name, repo, branch string) (*Template, error // SaveTemplate saves a local Template to the Registry func (r *Registry) SaveTemplate(name, path string) (*Template, error) { + if _, err := r.GetTemplate(name); err == nil { + return nil, ErrTemplateExists{Name: name} + } + t := &Template{ - reg: r, - Name: name, - Path: path, - Created: time.Now(), + reg: r, + Name: name, + Path: path, + LastUpdate: time.Now(), } r.Templates = append(r.Templates, t) @@ -89,16 +97,48 @@ func (r *Registry) RemoveTemplate(name string) error { if err != nil { return err } + for idx, t := range r.Templates { if strings.EqualFold(name, t.Name) { r.Templates = append(r.Templates[:idx], r.Templates[idx+1:]...) if err := os.Remove(t.ArchivePath()); err != nil { return err } + return r.save() } } - return r.save() + return nil +} + +// RemoveTemplate updates the Template on disk and in meta +func (r *Registry) UpdateTemplate(name string) error { + _, err := r.GetTemplate(name) + if err != nil { + return err + } + + for idx, t := range r.Templates { + if strings.EqualFold(name, t.Name) { + // If the path doesn't exist, we are replacing it regardless + if err := os.Remove(t.ArchivePath()); err != nil && !os.IsNotExist(err) { + return err + } + + // Cut it out of the template list so we don't get a duplicate + r.Templates = append(r.Templates[:idx], r.Templates[idx+1:]...) + + // If path exists, it is local + if t.Path != "" { + _, err = r.SaveTemplate(t.Name, t.Path) + } else { + _, err = r.DownloadTemplate(t.Name, t.Repository, t.Branch) + } + return err + } + } + + return nil } // GetSource retrieves a Source from the Registry @@ -159,7 +199,16 @@ func Open(dir string) (*Registry, error) { if err != nil { return nil, err } - return ®, tree.Unmarshal(®) + + if err := tree.Unmarshal(®); err != nil { + return nil, err + } + + for _, tmpl := range reg.Templates { + tmpl.reg = ® + } + + return ®, nil } func create(regFile string) error { @@ -191,7 +240,7 @@ func download(cloneURL, branch, dest string) error { return err } - // RemoveTemplate .git + // Remove .git if err := os.RemoveAll(filepath.Join(tmp, ".git")); err != nil { return err } diff --git a/registry/template.go b/registry/template.go index 6703f14..9f37184 100644 --- a/registry/template.go +++ b/registry/template.go @@ -20,7 +20,7 @@ type Template struct { Path string `toml:"path"` Repository string `toml:"repository"` Branch string `toml:"branch"` - Created time.Time `toml:"created"` + LastUpdate time.Time `toml:"last_update"` } // ArchiveName is the name given to the archive for this Template