template / commit
improvements to the repo, 1-5
.goreleaser.yaml | 3 +- .vscode/settings.json | 3 + IMPROVEMENTS.md | 149 +++++------ Makefile | 4 +- go.mod | 1 - go.sum | 2 - main.go => internal/core/app.go | 274 +++++++++------------ config.go => internal/core/config.go | 4 +- config_test.go => internal/core/config_test.go | 2 +- .../core/default_config.go | 2 +- .../core/defaultconfig_test.go | 2 +- extras.go => internal/core/extras.go | 2 +- extras_test.go => internal/core/extras_test.go | 2 +- file.go => internal/core/file.go | 2 +- file_test.go => internal/core/file_test.go | 2 +- main_test.go => internal/core/main_test.go | 10 +- monkey-patch.go => internal/core/monkey-patch.go | 2 +- name_pattern.go => internal/core/name_pattern.go | 2 +- .../core/name_pattern_test.go | 4 +- .../core/parameter_data.go | 2 +- postscript.go => internal/core/postscript.go | 2 +- .../core/postscript_test.go | 2 +- profile.go => internal/core/profile.go | 4 +- profile_test.go => internal/core/profile_test.go | 2 +- templating.go => internal/core/templating.go | 2 +- test-common.go => internal/core/test-common.go | 2 +- utils.go => internal/core/utils.go | 2 +- utils_test.go => internal/core/utils_test.go | 2 +- 28 files changed, 213 insertions(+), 279 deletions(-) diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a9bd3d7..220c59d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -6,7 +6,8 @@ before: # You may remove this if you don't use go modules. - go mod tidy builds: - - env: + - main: ./cmd/template/ + env: - CGO_ENABLED=0 goos: - linux diff --git a/.vscode/settings.json b/.vscode/settings.json index c83a831..d170926 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,9 @@ "bashdb", "filetemplate", "hackathon", + "golangci", + "gomarklint", + "goreleaser", "logrus", "Pavel", "pwsh", diff --git a/IMPROVEMENTS.md b/IMPROVEMENTS.md index 0a6d1d0..1152ee6 100644 --- a/IMPROVEMENTS.md +++ b/IMPROVEMENTS.md @@ -4,150 +4,127 @@ Ordered roughly by effort, lowest first. Each section lists the concrete changes --- -## 1. Drop `golang.org/x/exp` dependency +## ✅ 1. Drop `golang.org/x/exp` dependency -`golang.org/x/exp/slices` was a backport of the stdlib `slices` package (available since -Go 1.21). Replace every import with the stdlib package. - -Files to update: - -- [config.go](config.go) — `import "golang.org/x/exp/slices"` → `import "slices"` -- [profile.go](profile.go) — same -- [name_pattern_test.go](name_pattern_test.go) — same - -After swapping imports, run `go mod tidy` to remove the `golang.org/x/exp` entries from -`go.mod` and `go.sum`. +Replaced `golang.org/x/exp/slices` with the stdlib `slices` package in +[config.go](internal/core/config.go), [profile.go](internal/core/profile.go), and +[name_pattern_test.go](internal/core/name_pattern_test.go). Removed the dependency +from [go.mod](go.mod) via `go mod tidy`. --- -## 2. Upgrade Go version - -`go.mod` declares `go 1.24.0` / `toolchain go1.24.4`, but the GitLab CI image is already -`golang:1.25`. Align `go.mod` with the CI image so the declared minimum matches what is -actually tested against. - -Steps: +## ✅ 2. Upgrade Go version -1. Run `go mod tidy` after updating the `go` directive in [go.mod](go.mod). -2. Update `toolchain` line to match the current patch release. -3. Verify all tests still pass. +[go.mod](go.mod) now declares `go 1.25.0`, driven by the project's actual transitive +deps. The GitLab CI image (`golang:1.25`) is aligned. --- -## 3. Reorganize source files into a nested directory +## ✅ 3. Reorganize source files into a nested directory -All `.go` files currently live at the repo root alongside config files, making the layout -cluttered. The standard Go layout for a CLI tool is: +All source files moved out of the repo root into: ```text cmd/ template/ - main.go ← entry point only; wire dependencies here + main.go ← 14-line entry point; sets Version, calls core.Run() internal/ - core/ ← or any name: "engine", "template", etc. + core/ + app.go ← CLI wiring + all orchestration logic config.go - config_test.go - default_config.go - ...all other .go files... + profile.go + ... (all other files) ``` -Concrete steps: +[Makefile](Makefile) build and install targets updated to `./cmd/template/`. +[.goreleaser.yaml](.goreleaser.yaml) updated with `main: ./cmd/template/`. -1. Create `cmd/template/` and `internal/core/` directories. -2. Move `main.go` to `cmd/template/main.go`. Keep it thin — just flag parsing and calls - into `internal/core`. -3. Move every other `.go` file (including test files) to `internal/core/`. -4. Change the package declaration in all moved files from `package main` to `package core` - (or chosen name). -5. Re-export only what `cmd/template/main.go` needs. -6. Update the `Makefile` build target: `go build ./cmd/template/`. -7. Update `.goreleaser.yaml` main path if it references a specific file. -8. Run `go test ./...` to confirm nothing broke. +--- + +## ✅ 4. Tool management via separate module -**Note:** `monkey-patch.go` and `test-common.go` contain test-support code. They can live -alongside the production code in `internal/core/` (Go only compiles `_test.go` files during -tests) or move into a `internal/core/testutil/` sub-package. +Dev tools (golangci-lint, gomarklint, goreleaser) are now version-pinned in +[tools/go.mod](tools/go.mod), keeping their hundreds of transitive dependencies +out of the main [go.mod](go.mod). The [Makefile](Makefile) installs them via +`cd tools && go install ...` and exposes `make lint` and `make lint-docs` targets. --- -## 4. Improve test coverage +## ✅ 5. Reduce `main.go` size -Current tests focus on the happy path. Priority areas for new tests: +[cmd/template/main.go](cmd/template/main.go) is now 14 lines. All CLI wiring, +settings construction (`createSettings`), profile resolution (`findProfile`), and +orchestration (`runMain`) live in [internal/core/app.go](internal/core/app.go) +where they are reachable by tests without going through the CLI layer. + +--- -### 4a. Error paths in config handling ([config.go](config.go)) +## 6. Improve test coverage + +Current coverage: ~56%. Tests focus on the happy path. Priority areas for new tests: + +### 6a. Error paths in config handling ([config.go](internal/core/config.go)) - `loadConfig` — file not found, malformed YAML, invalid permissions - `saveConfig` — write failure (read-only directory) - `validateConfig` — all validation branches (duplicate names, multiple defaults) -### 4b. Error paths in file creation ([file.go](file.go)) +### 6b. Error paths in file creation ([file.go](internal/core/file.go)) -- `createFile` — template parse error, template execution error, `os.Create` failure, - `os.WriteFile` failure +- `createFile` — template parse error, template execution error, `os.Create` + failure, `os.WriteFile` failure - `readFile` — file not found -### 4c. Profile processing edge cases ([profile.go](profile.go)) +### 6c. Profile processing edge cases ([profile.go](internal/core/profile.go)) - `processProfile` with `ScriptType` (currently untested or minimally tested) - `createEntity` failure cases: mkdir failure, nested file creation failure -- `validateProfile` — all invalid combinations (missing target, unsupported OS, etc.) +- `validateProfile` — all invalid combinations (missing target, unsupported OS, + etc.) -### 4d. Extras loading ([extras.go](extras.go)) +### 6d. Extras loading ([extras.go](internal/core/extras.go)) - Missing extras file (should not error) - Malformed extras YAML -### 4e. `mainLogic` integration ([main.go](main.go)) +### 6e. App-level integration ([app.go](internal/core/app.go)) - Profile not found - Config load failure - Piped stdin with a folder-type profile (should be an error or no-op) -### 4f. Coverage tooling +### 6f. Coverage gate -Add a coverage threshold gate to the Makefile so CI fails if coverage drops below a chosen +Add a threshold to the Makefile so CI fails if coverage drops below a chosen minimum (e.g., 70%): ```makefile -test: +test: build go test ./... -coverprofile=test-coverage.out - go tool cover -func=test-coverage.out | grep total | awk '{if ($$3+0 < 70) exit 1}' + go tool cover -func=test-coverage.out | grep total \ + | awk '{if ($$3+0 < 70) exit 1}' ``` --- -## 5. Reduce `main.go` size - -At 584 lines, [main.go](main.go) mixes CLI wiring, settings construction, and business -logic. After the reorganization in item 3, consider splitting it further: - -- Keep `main.go` (in `cmd/template/`) to flag definitions and `main()` only (~100 lines). -- Move `createSettings()`, `mainLogic()`, and `findProfile()` into `internal/core/` where - they are easier to unit-test without going through the CLI layer. - ---- - -## 6. Clean up `default_config.go` +## 7. Clean up `default_config.go` -At 397 lines, [default_config.go](default_config.go) is unwieldy. Options: +At 396 lines, [default_config.go](internal/core/default_config.go) is unwieldy. +Options: - Split into per-profile files: `profiles_daily.go`, `profiles_git.go`, etc. -- Or load default profiles from an embedded YAML file (`//go:embed defaults.yaml`) so - non-Go contributors can read and edit profiles without touching Go code. +- Or load default profiles from an embedded YAML file (`//go:embed defaults.yaml`) + so non-Go contributors can read and edit profiles without touching Go code. -The embedded YAML approach also makes it easy to write a single test that validates every -default profile passes `validateProfile()`. +The embedded YAML approach also makes it easy to write a single test that validates +every default profile passes `validateProfile()`. --- -## 7. Minor housekeeping - -- **Empty `src/` directory** — remove it (or document its purpose). -- **`test-common.go`** — this file contains only a package declaration. Either add shared - test helpers to it or delete it. -- **`dev-setup` script** — is gitignored but checked-in? Verify its contents don't contain - credentials and decide if it should be tracked. -- **`.DS_Store`** — add `**/.DS_Store` to [.gitignore](.gitignore) to prevent future - commits of macOS metadata files. -- **CI image pinning** — `.gitlab-ci.yml` uses `golang:1.25` (unpinned minor). Pin to a - digest or a patch tag (`golang:1.25.0`) for reproducible builds. +## 8. Minor housekeeping + +- **Empty `src/` directory** — remove it. +- **`.DS_Store`** — add `**/.DS_Store` to [.gitignore](.gitignore) to prevent + future commits of macOS metadata files. +- **CI image pinning** — `.gitlab-ci.yml` uses `golang:1.25` (unpinned minor). + Pin to a patch tag (`golang:1.25.x`) for reproducible builds. diff --git a/Makefile b/Makefile index de0f88a..3caca2d 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ clean: go mod tidy build: prep - go build ${LDFLAGS} -o ${BINARY} . + go build ${LDFLAGS} -o ${BINARY} ./cmd/template/ lint: build tools-install golangci-lint run --timeout=10m @@ -39,7 +39,7 @@ test_coverage_html: test_coverage open "./test-coverage.html" install: build test lint - go install ${LDFLAGS} + go install ${LDFLAGS} ./cmd/template/ uninstall: go clean -i gitlab.com/PavelSafronov/template... diff --git a/go.mod b/go.mod index 5b61547..c85ca10 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/sirupsen/logrus v1.9.4 github.com/stretchr/testify v1.11.1 github.com/urfave/cli/v2 v2.27.7 - golang.org/x/exp v0.0.0-20260112195511-716be5621a96 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 95224d3..6ce3423 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,6 @@ github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAz github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/internal/core/app.go similarity index 59% rename from main.go rename to internal/core/app.go index a7cc0b0..8abcd47 100644 --- a/main.go +++ b/internal/core/app.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" @@ -12,20 +12,15 @@ import ( "strings" log "github.com/sirupsen/logrus" - "github.com/urfave/cli/v2" // imports as package "cli" + "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" ) -var ( - // Version is the version of the application - Version string -) - const defaultUserConfigPath = "~/.template.user.yaml" const defaultConfigPath = "~/.template.yaml" const defaultLogLevel = "info" -type ArgumentValues struct { +type argumentValues struct { profile string projectName string fileTemplate string @@ -36,20 +31,21 @@ type ArgumentValues struct { extrasFilePath string logLevel string } -type EnvVarValues struct { +type envVarValues struct { useDefaultConfig bool } -type Settings struct { - ArgumentValues - EnvVarValues +type settings struct { + argumentValues + envVarValues extras map[string]string fullConfigPath string config Config usr *user.User } -func main() { - argumentValues := ArgumentValues{} +// Run is the entry point for the CLI application. +func Run(args []string, version string) error { + av := argumentValues{} app := &cli.App{ Name: "template", @@ -62,66 +58,63 @@ func main() { Aliases: []string{"p"}, Value: "", Usage: "`PROFILE` to use for the template", - Destination: &argumentValues.profile, - // Value: defaultProfileName, - // Required: true, - // EnvVars: []string{"TEMPLATE_PROFILE"}, + Destination: &av.profile, }, &cli.StringFlag{ Name: "file", Aliases: []string{"f"}, Value: "", Usage: "file template to use to create a file", - Destination: &argumentValues.fileTemplate, + Destination: &av.fileTemplate, }, &cli.StringFlag{ Name: "dir", Aliases: []string{"d"}, Value: "", Usage: "folder pattern to use to create a directory", - Destination: &argumentValues.dirTemplate, + Destination: &av.dirTemplate, }, &cli.StringFlag{ Name: "script", Aliases: []string{"s"}, Value: "", Usage: "templated script to run in the shell", - Destination: &argumentValues.scriptTemplate, + Destination: &av.scriptTemplate, }, &cli.StringFlag{ Name: "name", Aliases: []string{"n"}, Usage: "name of the project to create", Value: "", - Destination: &argumentValues.projectName, + Destination: &av.projectName, }, &cli.PathFlag{ Name: "config-yaml", Aliases: []string{"c"}, Usage: "path to the config yaml file", Value: defaultConfigPath, - Destination: &argumentValues.configFilePath, + Destination: &av.configFilePath, }, &cli.PathFlag{ Name: "user-config-yaml", Aliases: []string{"u"}, Usage: "path to the user config yaml file", Value: defaultUserConfigPath, - Destination: &argumentValues.userConfigPath, + Destination: &av.userConfigPath, }, &cli.PathFlag{ Name: "extras-yaml", Aliases: []string{"e"}, Usage: "path to the extras yaml file", Value: "~/.template-extras.yaml", - Destination: &argumentValues.extrasFilePath, + Destination: &av.extrasFilePath, }, &cli.StringFlag{ Name: "log-level", Aliases: []string{"l"}, Usage: "log level", Value: defaultLogLevel, - Destination: &argumentValues.logLevel, + Destination: &av.logLevel, }, }, Commands: []*cli.Command{ @@ -129,15 +122,14 @@ func main() { Name: "list", Usage: "list available profiles", Action: func(c *cli.Context) error { - settings, err := createSettings(argumentValues, c) + s, err := createSettings(av, c) if err != nil { return fmt.Errorf("error creating settings: %v", err) } - config := settings.config - // list profiles + cfg := s.config fmt.Println("Available profiles:") - for i, p := range config.Profiles { + for i, p := range cfg.Profiles { fmt.Printf(" %d: %v\n", i+1, p.Name) fmt.Printf(" %v\n", p.Description) } @@ -154,27 +146,26 @@ func main() { Aliases: []string{"d"}, Value: "", Usage: "folder from which to create the template", - Destination: &argumentValues.dirTemplate, + Destination: &av.dirTemplate, }, &cli.StringFlag{ Name: "file", Aliases: []string{"f"}, Value: "", Usage: "file from which to create the template", - Destination: &argumentValues.fileTemplate, + Destination: &av.fileTemplate, }, }, Action: func(c *cli.Context) error { - // get args c.Args() - filePath := argumentValues.fileTemplate - dirPath := argumentValues.dirTemplate + filePath := av.fileTemplate + dirPath := av.dirTemplate if filePath == "" && dirPath == "" { return fmt.Errorf("file or dir not specified, use `file` or `dir` flag") } - profileName := argumentValues.profile + profileName := av.profile if profileName == "" { profileName = "temp" } @@ -202,39 +193,36 @@ func main() { } if filePath != "" { - file, err := readFile(filePath) + fileContent, err := readFile(filePath) if err != nil { return fmt.Errorf("error reading file %s: %w", filePath, err) } - template := ParameterizedText(file) - profile.FileTemplate = template + profile.FileTemplate = ParameterizedText(fileContent) } else { - files := []FileInFolder{} - err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error { + var files []FileInFolder + err := filepath.WalkDir(dirPath, func(p string, d fs.DirEntry, err error) error { if err != nil { - fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err) + fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", p, err) return err } - isRoot := path == dirPath + isRoot := p == dirPath isDir := d.IsDir() - shouldSkip := isRoot || isDir - if shouldSkip { + if isRoot || isDir { return nil } - relPath, err := filepath.Rel(dirPath, path) + relPath, err := filepath.Rel(dirPath, p) if err != nil { - return fmt.Errorf("error getting relative path for %s: %w", path, err) + return fmt.Errorf("error getting relative path for %s: %w", p, err) } - contents, err := readFile(path) + contents, err := readFile(p) if err != nil { - return fmt.Errorf("error reading file %s: %w", path, err) + return fmt.Errorf("error reading file %s: %w", p, err) } - fif := FileInFolder{ + files = append(files, FileInFolder{ Name: ParameterizedText(relPath), FileTemplate: ParameterizedText(contents), - } - files = append(files, fif) + }) return nil }) if err != nil { @@ -243,19 +231,13 @@ func main() { profile.Files = files } - config := Config{ - Profiles: []Profile{profile}, - } - + cfg := Config{Profiles: []Profile{profile}} writer := strings.Builder{} encoder := yaml.NewEncoder(&writer) - err := encoder.Encode(config) - if err != nil { + if err := encoder.Encode(cfg); err != nil { return fmt.Errorf("error encoding config: %v", err) } - - yamlText := writer.String() - fmt.Println(yamlText) + fmt.Println(writer.String()) return nil }, }, @@ -263,29 +245,26 @@ func main() { Name: "get", Usage: "get a profile", Action: func(c *cli.Context) error { - settings, err := createSettings(argumentValues, c) + s, err := createSettings(av, c) if err != nil { return fmt.Errorf("error creating settings: %v", err) } - config := settings.config + cfg := s.config - // get profile information - index, err := findProfileIndex(config.Profiles, settings.profile) + index, err := findProfileIndex(cfg.Profiles, s.profile) if err != nil { - return fmt.Errorf("profile '%v' not found", settings.profile) + return fmt.Errorf("profile '%v' not found", s.profile) } - profile := config.Profiles[index] + profile := cfg.Profiles[index] writer := strings.Builder{} encoder := yaml.NewEncoder(&writer) - err = encoder.Encode(profile) - if err != nil { + if err = encoder.Encode(profile); err != nil { return fmt.Errorf("error encoding profile: %v", err) } - yamlText := writer.String() - fmt.Printf("Profile '%v':\n", settings.profile) - fmt.Println(yamlText) + fmt.Printf("Profile '%v':\n", s.profile) + fmt.Println(writer.String()) fmt.Println() return nil }, @@ -298,7 +277,7 @@ func main() { if err != nil { log.Fatal(err) } - fullConfigPath := getFullPath(argumentValues.configFilePath, *usr) + fullConfigPath := getFullPath(av.configFilePath, *usr) _, err = os.Stat(fullConfigPath) if err == nil { @@ -308,8 +287,7 @@ func main() { } fmt.Printf("Setting config at '%v' to default\n", fullConfigPath) - err = saveConfig(fullConfigPath, defaultConfig()) - if err != nil { + if err = saveConfig(fullConfigPath, defaultConfig()); err != nil { return fmt.Errorf("error saving default config: %v", err) } fmt.Println() @@ -324,11 +302,10 @@ func main() { if err != nil { log.Fatal(err) } - fullConfigPath := getFullPath(argumentValues.configFilePath, *usr) + fullConfigPath := getFullPath(av.configFilePath, *usr) fmt.Printf("Resetting config at '%v' to default\n", fullConfigPath) - err = saveConfig(fullConfigPath, defaultConfig()) - if err != nil { + if err = saveConfig(fullConfigPath, defaultConfig()); err != nil { return fmt.Errorf("error saving default config: %v", err) } fmt.Println() @@ -339,19 +316,17 @@ func main() { Name: "version", Usage: "print version", Action: func(c *cli.Context) error { - fmt.Print(Version) - fmt.Println() + fmt.Println(version) return nil }, }, }, - Action: func(context *cli.Context) error { - settings, err := createSettings(argumentValues, context) + Action: func(c *cli.Context) error { + s, err := createSettings(av, c) if err != nil { return fmt.Errorf("error parsing arguments: %w", err) } - err = mainLogic(settings) - if err != nil { + if err = runMain(s); err != nil { return fmt.Errorf("error in main logic: %w", err) } fmt.Println() @@ -359,31 +334,24 @@ func main() { }, } - if err := app.Run(os.Args); err != nil { - log.Fatal(err) - } + return app.Run(args) } -func createSettings( - argumentValues ArgumentValues, - context *cli.Context, -) (Settings, error) { - envVarValues, err := readEnvVars() +func createSettings(av argumentValues, context *cli.Context) (settings, error) { + ev, err := readEnvVars() if err != nil { - return Settings{}, fmt.Errorf("error getting env var values: %w", err) + return settings{}, fmt.Errorf("error getting env var values: %w", err) } - settings := Settings{ - ArgumentValues: argumentValues, - EnvVarValues: envVarValues, + s := settings{ + argumentValues: av, + envVarValues: ev, } - // read args args := context.Args() - // configure logging var level log.Level - switch strings.ToLower(settings.logLevel) { + switch strings.ToLower(s.logLevel) { case "debug": level = log.DebugLevel case "info": @@ -397,75 +365,65 @@ func createSettings( case "panic": level = log.PanicLevel default: - return Settings{}, fmt.Errorf("invalid log level specified %v", settings.logLevel) + return settings{}, fmt.Errorf("invalid log level specified %v", s.logLevel) } log.SetLevel(level) log.SetOutput(os.Stdout) log.WithFields(log.Fields{"args": args}).Debug("Args parsed") - skipProfile := len(settings.fileTemplate) > 0 || len(settings.dirTemplate) > 0 || len(settings.scriptTemplate) > 0 + skipProfile := len(s.fileTemplate) > 0 || len(s.dirTemplate) > 0 || len(s.scriptTemplate) > 0 currentParamIndex := 0 if !skipProfile { - // get profile name from args - // if not set explicitly, use first arg, if present - if settings.profile == "" && args.Len() > currentParamIndex { - settings.profile = args.Get(currentParamIndex) + if s.profile == "" && args.Len() > currentParamIndex { + s.profile = args.Get(currentParamIndex) } - if settings.profile != "" { - fmt.Println("Profile:", settings.profile) + if s.profile != "" { + fmt.Println("Profile:", s.profile) } currentParamIndex++ } - // get project name from args - // if not set explicitly, use next arg (after profile), if present - if settings.projectName == "" && args.Len() > currentParamIndex { - settings.projectName = args.Get(currentParamIndex) - // currentParamIndex++ + if s.projectName == "" && args.Len() > currentParamIndex { + s.projectName = args.Get(currentParamIndex) } - if settings.projectName != "" { - fmt.Println("Project name:", settings.projectName) + if s.projectName != "" { + fmt.Println("Project name:", s.projectName) } - // extras strings - _, err = os.Stat(settings.extrasFilePath) + _, err = os.Stat(s.extrasFilePath) if err == nil { - extras, err := loadExtras(settings.extrasFilePath) + extras, err := loadExtras(s.extrasFilePath) if err != nil { - return settings, fmt.Errorf("error loading extras from %v: %w", settings.extrasFilePath, err) + return s, fmt.Errorf("error loading extras from %v: %w", s.extrasFilePath, err) } - settings.extras = extras + s.extras = extras } - // get user usr, err := user.Current() if err != nil { log.Fatal(err) } - settings.usr = usr + s.usr = usr - // get config from default path - fullConfigPath := getFullPath(settings.configFilePath, *usr) - settings.fullConfigPath = fullConfigPath + fullConfigPath := getFullPath(s.configFilePath, *usr) + s.fullConfigPath = fullConfigPath var fullConfig Config - if settings.useDefaultConfig { + if s.useDefaultConfig { fullConfig = defaultConfig() } else { config, err := loadConfig(fullConfigPath) if err != nil { - return settings, fmt.Errorf("error loading config from %v: %w\nConsider running `template reset` to recreate the config file, but keep in mind that `reset` will remove existing config settings", fullConfigPath, err) + return s, fmt.Errorf("error loading config from %v: %w\nConsider running `template reset` to recreate the config file, but keep in mind that `reset` will remove existing config settings", fullConfigPath, err) } fmt.Printf("Loaded config from %v\n", fullConfigPath) - - // load user config, if present - fullConfig = loadAndMergeUserConfig(settings.userConfigPath, usr, config) + fullConfig = loadAndMergeUserConfig(s.userConfigPath, usr, config) } - settings.config = fullConfig + s.config = fullConfig - return settings, nil + return s, nil } func loadAndMergeUserConfig(userConfigPath string, usr *user.User, globalConfig Config) Config { @@ -491,45 +449,44 @@ func loadAndMergeUserConfig(userConfigPath string, usr *user.User, globalConfig return fullConfig } -func readEnvVars() (EnvVarValues, error) { - envVarValues := EnvVarValues{} +func readEnvVars() (envVarValues, error) { + ev := envVarValues{} useDefaultConfigText := os.Getenv("TEMPLATE_USE_DEFAULT_CONFIG") if useDefaultConfigText != "" { useDefaultConfig, err := strconv.ParseBool(useDefaultConfigText) if err != nil { - return envVarValues, fmt.Errorf("error parsing env var TEMPLATE_USE_DEFAULT_CONFIG: \"%s\" %w", useDefaultConfigText, err) + return ev, fmt.Errorf("error parsing env var TEMPLATE_USE_DEFAULT_CONFIG: \"%s\" %w", useDefaultConfigText, err) } - envVarValues.useDefaultConfig = useDefaultConfig + ev.useDefaultConfig = useDefaultConfig } - return envVarValues, nil + return ev, nil } -func findProfile(settings Settings) (Profile, error) { - var profile = Profile{} - var err error - config := settings.config - createTempProfile := len(settings.fileTemplate) > 0 || len(settings.dirTemplate) > 0 || len(settings.scriptTemplate) > 0 +func findProfile(s settings) (Profile, error) { + var profile Profile + cfg := s.config + createTempProfile := len(s.fileTemplate) > 0 || len(s.dirTemplate) > 0 || len(s.scriptTemplate) > 0 if createTempProfile { - if len(settings.scriptTemplate) > 0 { + if len(s.scriptTemplate) > 0 { profile = Profile{ Name: "temp", Description: "temp-script-profile", Type: ScriptType, - PostScripts: []ParameterizedText{ParameterizedText(settings.scriptTemplate)}, + PostScripts: []ParameterizedText{ParameterizedText(s.scriptTemplate)}, } } else { profileType := FileType - if len(settings.dirTemplate) > 0 { + if len(s.dirTemplate) > 0 { profileType = FolderType } - patterns := []ParameterizedText{} - if len(settings.fileTemplate) > 0 { - patterns = append(patterns, ParameterizedText(settings.fileTemplate)) - } else if len(settings.dirTemplate) > 0 { - patterns = append(patterns, ParameterizedText(settings.dirTemplate)) + var patterns []ParameterizedText + if len(s.fileTemplate) > 0 { + patterns = append(patterns, ParameterizedText(s.fileTemplate)) + } else if len(s.dirTemplate) > 0 { + patterns = append(patterns, ParameterizedText(s.dirTemplate)) } profile = Profile{ Name: "temp", @@ -539,25 +496,25 @@ func findProfile(settings Settings) (Profile, error) { } } } else { - if settings.profile == "" { - profile, err = findDefaultProfile(config.Profiles) + if s.profile == "" { + var err error + profile, err = findDefaultProfile(cfg.Profiles) if err != nil { return profile, fmt.Errorf("no profile specified and no default profile found") } } else { - index, err := findProfileIndex(config.Profiles, settings.profile) + index, err := findProfileIndex(cfg.Profiles, s.profile) if err != nil { return profile, fmt.Errorf("error finding profile: %v", err) } - profile = config.Profiles[index] + profile = cfg.Profiles[index] } } return profile, nil } -func mainLogic(settings Settings) error { - // find profile - profile, err := findProfile(settings) +func runMain(s settings) error { + profile, err := findProfile(s) if err != nil { return fmt.Errorf("error finding profile: %v", err) } @@ -566,15 +523,14 @@ func mainLogic(settings Settings) error { if err != nil { return fmt.Errorf("error getting stdin stat: %w", err) } - var pipeInputScanner *bufio.Scanner = nil + var pipeInputScanner *bufio.Scanner incomingPipeData := (stat.Mode() & os.ModeCharDevice) == 0 if incomingPipeData { pipeInputScanner = bufio.NewScanner(os.Stdin) fmt.Printf("Reading from stdin\n") } - // process profile - errors := processProfile(profile, settings.projectName, settings.extras, *settings.usr, pipeInputScanner) + errors := processProfile(profile, s.projectName, s.extras, *s.usr, pipeInputScanner) if len(errors) > 0 { return fmt.Errorf("error processing profile: %v", errors) } diff --git a/config.go b/internal/core/config.go similarity index 98% rename from config.go rename to internal/core/config.go index 2e12711..5dedb03 100644 --- a/config.go +++ b/internal/core/config.go @@ -1,10 +1,10 @@ -package main +package core import ( "fmt" "os" - "golang.org/x/exp/slices" + "slices" "gopkg.in/yaml.v3" ) diff --git a/config_test.go b/internal/core/config_test.go similarity index 99% rename from config_test.go rename to internal/core/config_test.go index 3e1a45a..ed365d5 100644 --- a/config_test.go +++ b/internal/core/config_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "errors" diff --git a/default_config.go b/internal/core/default_config.go similarity index 99% rename from default_config.go rename to internal/core/default_config.go index df13d6f..e681d00 100644 --- a/default_config.go +++ b/internal/core/default_config.go @@ -1,4 +1,4 @@ -package main +package core func defaultConfig() Config { config := Config{ diff --git a/defaultconfig_test.go b/internal/core/defaultconfig_test.go similarity index 94% rename from defaultconfig_test.go rename to internal/core/defaultconfig_test.go index b06ada8..7620381 100644 --- a/defaultconfig_test.go +++ b/internal/core/defaultconfig_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "testing" diff --git a/extras.go b/internal/core/extras.go similarity index 96% rename from extras.go rename to internal/core/extras.go index 018c0f6..88d5467 100644 --- a/extras.go +++ b/internal/core/extras.go @@ -1,4 +1,4 @@ -package main +package core import ( "fmt" diff --git a/extras_test.go b/internal/core/extras_test.go similarity index 98% rename from extras_test.go rename to internal/core/extras_test.go index 96a72e5..6f80528 100644 --- a/extras_test.go +++ b/internal/core/extras_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "os" diff --git a/file.go b/internal/core/file.go similarity index 95% rename from file.go rename to internal/core/file.go index 54c2071..f88c287 100644 --- a/file.go +++ b/internal/core/file.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" diff --git a/file_test.go b/internal/core/file_test.go similarity index 99% rename from file_test.go rename to internal/core/file_test.go index 7a75ed5..815bd72 100644 --- a/file_test.go +++ b/internal/core/file_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" diff --git a/main_test.go b/internal/core/main_test.go similarity index 83% rename from main_test.go rename to internal/core/main_test.go index ac1aa3f..5088ccf 100644 --- a/main_test.go +++ b/internal/core/main_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "os" @@ -26,8 +26,8 @@ func shutdown() { func TestFindProfile(t *testing.T) { fileTemplate := "{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}.txt" - profile, err := findProfile(Settings{ - ArgumentValues: ArgumentValues{ + profile, err := findProfile(settings{ + argumentValues: argumentValues{ fileTemplate: fileTemplate, }, }) @@ -38,8 +38,8 @@ func TestFindProfile(t *testing.T) { assert.Equal(t, ParameterizedText(fileTemplate), profile.NamePatterns[0]) dirTemplate := "{{.Year}}-{{.Month}}-{{.Day}}{{if .ProjectName}}-{{.ProjectName}}{{end}}" - profile, err = findProfile(Settings{ - ArgumentValues: ArgumentValues{ + profile, err = findProfile(settings{ + argumentValues: argumentValues{ dirTemplate: dirTemplate, }, }) diff --git a/monkey-patch.go b/internal/core/monkey-patch.go similarity index 99% rename from monkey-patch.go rename to internal/core/monkey-patch.go index 0ce2d18..bf593e9 100644 --- a/monkey-patch.go +++ b/internal/core/monkey-patch.go @@ -1,4 +1,4 @@ -package main +package core import ( "os" diff --git a/name_pattern.go b/internal/core/name_pattern.go similarity index 94% rename from name_pattern.go rename to internal/core/name_pattern.go index da8db8e..b5cea6a 100644 --- a/name_pattern.go +++ b/internal/core/name_pattern.go @@ -1,4 +1,4 @@ -package main +package core import ( "errors" diff --git a/name_pattern_test.go b/internal/core/name_pattern_test.go similarity index 96% rename from name_pattern_test.go rename to internal/core/name_pattern_test.go index 9dfb76e..5a5af48 100644 --- a/name_pattern_test.go +++ b/internal/core/name_pattern_test.go @@ -1,11 +1,11 @@ -package main +package core import ( "testing" "time" "github.com/stretchr/testify/assert" - "golang.org/x/exp/slices" + "slices" ) // Mon Jan 2 15:04:05 MST 2006 diff --git a/parameter_data.go b/internal/core/parameter_data.go similarity index 95% rename from parameter_data.go rename to internal/core/parameter_data.go index 0f5c4d0..59f3d9a 100644 --- a/parameter_data.go +++ b/internal/core/parameter_data.go @@ -1,4 +1,4 @@ -package main +package core import ( "path/filepath" diff --git a/postscript.go b/internal/core/postscript.go similarity index 93% rename from postscript.go rename to internal/core/postscript.go index 4b072d1..f054fbe 100644 --- a/postscript.go +++ b/internal/core/postscript.go @@ -1,4 +1,4 @@ -package main +package core import ( "fmt" diff --git a/postscript_test.go b/internal/core/postscript_test.go similarity index 95% rename from postscript_test.go rename to internal/core/postscript_test.go index a1f9b98..4e91515 100644 --- a/postscript_test.go +++ b/internal/core/postscript_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "testing" diff --git a/profile.go b/internal/core/profile.go similarity index 99% rename from profile.go rename to internal/core/profile.go index 01cf20a..7f28409 100644 --- a/profile.go +++ b/internal/core/profile.go @@ -1,4 +1,4 @@ -package main +package core import ( "bufio" @@ -12,7 +12,7 @@ import ( "strings" "time" - "golang.org/x/exp/slices" + "slices" ) type ProfileType string diff --git a/profile_test.go b/internal/core/profile_test.go similarity index 99% rename from profile_test.go rename to internal/core/profile_test.go index 86d3f70..52f5ed7 100644 --- a/profile_test.go +++ b/internal/core/profile_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "io/fs" diff --git a/templating.go b/internal/core/templating.go similarity index 93% rename from templating.go rename to internal/core/templating.go index ed70933..1fcf455 100644 --- a/templating.go +++ b/internal/core/templating.go @@ -1,4 +1,4 @@ -package main +package core import ( "fmt" diff --git a/test-common.go b/internal/core/test-common.go similarity index 96% rename from test-common.go rename to internal/core/test-common.go index dc7b324..20ec445 100644 --- a/test-common.go +++ b/internal/core/test-common.go @@ -1,4 +1,4 @@ -package main +package core import "time" diff --git a/utils.go b/internal/core/utils.go similarity index 97% rename from utils.go rename to internal/core/utils.go index eb774fc..26b847e 100644 --- a/utils.go +++ b/internal/core/utils.go @@ -1,4 +1,4 @@ -package main +package core import ( "os/user" diff --git a/utils_test.go b/internal/core/utils_test.go similarity index 98% rename from utils_test.go rename to internal/core/utils_test.go index 851bdca..323adbd 100644 --- a/utils_test.go +++ b/internal/core/utils_test.go @@ -1,4 +1,4 @@ -package main +package core import ( "os/user"