Compare commits
3 Commits
4981a14e14
...
10689e9843
Author | SHA1 | Date |
---|---|---|
jolheiser | 10689e9843 | |
jolheiser | 4b7aa8a03e | |
jolheiser | 181aa60d1b |
2
FAQ.md
2
FAQ.md
|
@ -113,7 +113,7 @@ I realize that many users will be using GitHub, and most will likely still be us
|
||||||
|
|
||||||
## Backup and Restore
|
## Backup and Restore
|
||||||
|
|
||||||
1. The simplest solution is to make a copy of your `registry.toml` (default: `~/.tmpl/registry.toml`).
|
1. The simplest solution is to make a copy of your `registry.yaml` (default: `~/.tmpl/registry.yaml`).
|
||||||
* Once in the new location, you will need to use `tmpl restore`.
|
* 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.
|
2. Alternatively, you can copy/paste the entire registry (default: `~/.tmpl`) and skip the restore step.
|
||||||
|
|
|
@ -2,6 +2,8 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.jolheiser.com/tmpl/env"
|
"go.jolheiser.com/tmpl/env"
|
||||||
|
@ -15,7 +17,7 @@ var Download = &cli.Command{
|
||||||
Name: "download",
|
Name: "download",
|
||||||
Usage: "Download a template",
|
Usage: "Download a template",
|
||||||
Description: "Download a template and save it to the local registry",
|
Description: "Download a template and save it to the local registry",
|
||||||
ArgsUsage: "[repository URL] [name]",
|
ArgsUsage: "[repository URL] <name>",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "branch",
|
Name: "branch",
|
||||||
|
@ -29,7 +31,7 @@ var Download = &cli.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDownload(ctx *cli.Context) error {
|
func runDownload(ctx *cli.Context) error {
|
||||||
if ctx.NArg() < 2 {
|
if ctx.NArg() < 1 {
|
||||||
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
return cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ func runDownload(ctx *cli.Context) error {
|
||||||
cloneURL += ".git"
|
cloneURL += ".git"
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := reg.DownloadTemplate(ctx.Args().Get(1), cloneURL, ctx.String("branch"))
|
t, err := reg.DownloadTemplate(deriveName(ctx), cloneURL, ctx.String("branch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -75,3 +77,19 @@ func runDownload(ctx *cli.Context) error {
|
||||||
log.Info().Msgf("Added new template %q", t.Name)
|
log.Info().Msgf("Added new template %q", t.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deriveName(ctx *cli.Context) string {
|
||||||
|
if ctx.NArg() > 1 {
|
||||||
|
return ctx.Args().Get(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
envBranch, envSet := os.LookupEnv("TMPL_BRANCH")
|
||||||
|
flagBranch, flagSet := ctx.String("branch"), ctx.IsSet("branch")
|
||||||
|
if flagSet {
|
||||||
|
if !envSet || envBranch != flagBranch {
|
||||||
|
return flagBranch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.Base(ctx.Args().First())
|
||||||
|
}
|
||||||
|
|
|
@ -14,13 +14,25 @@ type Config struct {
|
||||||
Prompts []Prompt `yaml:"prompts"`
|
Prompts []Prompt `yaml:"prompts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PromptType is a type of prompt
|
||||||
|
type PromptType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PromptTypeInput PromptType = "input"
|
||||||
|
PromptTypeMultiline PromptType = "multi"
|
||||||
|
PromptTypeEditor PromptType = "editor"
|
||||||
|
PromptTypeConfirm PromptType = "confirm"
|
||||||
|
PromptTypeSelect PromptType = "select"
|
||||||
|
)
|
||||||
|
|
||||||
// Prompt is a tmpl prompt
|
// Prompt is a tmpl prompt
|
||||||
type Prompt struct {
|
type Prompt struct {
|
||||||
ID string `yaml:"id"`
|
ID string `yaml:"id"`
|
||||||
Label string `yaml:"label"`
|
Label string `yaml:"label"`
|
||||||
Help string `yaml:"help"`
|
Help string `yaml:"help"`
|
||||||
Default string `yaml:"default"`
|
Default string `yaml:"default"`
|
||||||
Options []string `yaml:"options"`
|
Options []string `yaml:"options"`
|
||||||
|
Type PromptType `yaml:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads a tmpl config
|
// Load loads a tmpl config
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
@ -55,7 +56,8 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
||||||
|
|
||||||
// Otherwise, prompt
|
// Otherwise, prompt
|
||||||
var p survey.Prompt
|
var p survey.Prompt
|
||||||
if len(prompt.Options) > 0 {
|
switch prompt.Type {
|
||||||
|
case config.PromptTypeSelect:
|
||||||
opts := make([]string, 0, len(prompt.Options))
|
opts := make([]string, 0, len(prompt.Options))
|
||||||
for idy, opt := range prompt.Options {
|
for idy, opt := range prompt.Options {
|
||||||
opts[idy] = os.ExpandEnv(opt)
|
opts[idy] = os.ExpandEnv(opt)
|
||||||
|
@ -65,7 +67,26 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
||||||
Options: opts,
|
Options: opts,
|
||||||
Help: prompt.Help,
|
Help: prompt.Help,
|
||||||
}
|
}
|
||||||
} else {
|
case config.PromptTypeConfirm:
|
||||||
|
def, _ := strconv.ParseBool(os.ExpandEnv(prompt.Default))
|
||||||
|
p = &survey.Confirm{
|
||||||
|
Message: prompt.Label,
|
||||||
|
Help: prompt.Help,
|
||||||
|
Default: def,
|
||||||
|
}
|
||||||
|
case config.PromptTypeMultiline:
|
||||||
|
p = &survey.Multiline{
|
||||||
|
Message: prompt.Label,
|
||||||
|
Default: os.ExpandEnv(prompt.Default),
|
||||||
|
Help: prompt.Help,
|
||||||
|
}
|
||||||
|
case config.PromptTypeEditor:
|
||||||
|
p = &survey.Editor{
|
||||||
|
Message: prompt.Label,
|
||||||
|
Default: os.ExpandEnv(prompt.Default),
|
||||||
|
Help: prompt.Help,
|
||||||
|
}
|
||||||
|
default:
|
||||||
p = &survey.Input{
|
p = &survey.Input{
|
||||||
Message: prompt.Label,
|
Message: prompt.Label,
|
||||||
Default: os.ExpandEnv(prompt.Default),
|
Default: os.ExpandEnv(prompt.Default),
|
||||||
|
@ -73,12 +94,13 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var a string
|
m := make(map[string]any)
|
||||||
if err := survey.AskOne(p, &a); err != nil {
|
if err := survey.AskOne(p, &m); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("could not complete prompt: %w", err)
|
||||||
}
|
}
|
||||||
|
a := m[""]
|
||||||
prompts[idx].Value = a
|
prompts[idx].Value = a
|
||||||
os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), a)
|
os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), fmt.Sprint(a))
|
||||||
}
|
}
|
||||||
|
|
||||||
return prompts, nil
|
return prompts, nil
|
||||||
|
@ -86,7 +108,7 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
||||||
|
|
||||||
type templatePrompt struct {
|
type templatePrompt struct {
|
||||||
config.Prompt
|
config.Prompt
|
||||||
Value string
|
Value any
|
||||||
}
|
}
|
||||||
|
|
||||||
type templatePrompts []templatePrompt
|
type templatePrompts []templatePrompt
|
||||||
|
|
|
@ -2,7 +2,6 @@ package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -223,7 +222,7 @@ func create(regFile string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func download(cloneURL, branch, dest string) error {
|
func download(cloneURL, branch, dest string) error {
|
||||||
tmp, err := ioutil.TempDir(os.TempDir(), "tmpl")
|
tmp, err := os.MkdirTemp(os.TempDir(), "tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package registry
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -67,7 +66,7 @@ func testGetFail(t *testing.T) {
|
||||||
|
|
||||||
func setupTemplate() {
|
func setupTemplate() {
|
||||||
var err error
|
var err error
|
||||||
tmplDir, err = ioutil.TempDir(os.TempDir(), "tmpl-setup")
|
tmplDir, err = os.MkdirTemp(os.TempDir(), "tmpl-setup")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +114,7 @@ func setupTemplate() {
|
||||||
|
|
||||||
func setupRegistry() {
|
func setupRegistry() {
|
||||||
var err error
|
var err error
|
||||||
regDir, err = ioutil.TempDir(os.TempDir(), "tmpl-reg")
|
regDir, err = os.MkdirTemp(os.TempDir(), "tmpl-reg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package registry
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -36,7 +35,7 @@ func (t *Template) ArchivePath() string {
|
||||||
|
|
||||||
// Execute runs the Template and copies to dest
|
// Execute runs the Template and copies to dest
|
||||||
func (t *Template) Execute(dest string, defaults, overwrite bool) error {
|
func (t *Template) Execute(dest string, defaults, overwrite bool) error {
|
||||||
tmp, err := ioutil.TempDir(os.TempDir(), "tmpl")
|
tmp, err := os.MkdirTemp(os.TempDir(), "tmpl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -62,7 +61,7 @@ func (t *Template) Execute(dest string, defaults, overwrite bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contents, err := ioutil.ReadFile(walkPath)
|
contents, err := os.ReadFile(walkPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -51,7 +50,7 @@ func testExecute(t *testing.T) {
|
||||||
|
|
||||||
// Check contents of file
|
// Check contents of file
|
||||||
testPath := filepath.Join(destDir, "TEST")
|
testPath := filepath.Join(destDir, "TEST")
|
||||||
contents, err := ioutil.ReadFile(testPath)
|
contents, err := os.ReadFile(testPath)
|
||||||
assert.NoErr(err) // Should be able to read TEST file
|
assert.NoErr(err) // Should be able to read TEST file
|
||||||
assert.Equal(string(contents), tmplGold) // Template should match golden file
|
assert.Equal(string(contents), tmplGold) // Template should match golden file
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ func testExecute(t *testing.T) {
|
||||||
assert.True(err != nil) // .tmplkeep file should NOT be retained
|
assert.True(err != nil) // .tmplkeep file should NOT be retained
|
||||||
|
|
||||||
// Change file to test non-overwrite
|
// Change file to test non-overwrite
|
||||||
err = ioutil.WriteFile(testPath, []byte(tmplNewGold), os.ModePerm)
|
err = os.WriteFile(testPath, []byte(tmplNewGold), os.ModePerm)
|
||||||
assert.NoErr(err) // Writing file should succeed
|
assert.NoErr(err) // Writing file should succeed
|
||||||
|
|
||||||
err = tmpl.Execute(destDir, true, false)
|
err = tmpl.Execute(destDir, true, false)
|
||||||
|
|
|
@ -43,8 +43,32 @@
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"description": "The type of prompt",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["input", "multi", "select", "confirm", "editor"]
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "select"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["options"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"not": {
|
||||||
|
"const": "select"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue