Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/content/docs/architecture/template-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ MARIADB_DATABASE: "[[ .ProjectShortName | toSnakeCase ]]_test"

---

## Always-ignored files

The following files are silently skipped during rendering, regardless of any other
configuration. They are OS/editor metadata that should never appear in scaffolded output:

| Filename | Origin |
|----------|--------|
| `.DS_Store` | macOS Finder |
| `Thumbs.db` | Windows Explorer |

These files are skipped at the walk stage — they are never copied, rendered, or passed to
`.specsverbatim`. This list is fixed; use `.specsignore` if per-template suppression is
ever added in the future.

---

## `.specsverbatim` — Verbatim Copy

A `.specsverbatim` file at the template root lists glob patterns for files that should be
Expand Down
6 changes: 2 additions & 4 deletions internal/cmd/use.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -65,11 +64,10 @@ func runUse(app *App, rawSource, targetDir string, opts executeOpts) error {
}
templateRoot = tmp
} else {
// go-git requires the destination to not exist; use a subdirectory.
cloneDir := filepath.Join(tmp, "repo")
app.Output.Info("cloning %s…", src.CloneURL)
// git layer logs clone start/complete
if err := pkggit.Clone(src.CloneURL, cloneDir, pkggit.CloneOptions{Branch: src.Branch}); err != nil {
cloneDir, err := pkggit.CloneInto(tmp, "repo", src.CloneURL, pkggit.CloneOptions{Branch: src.Branch})
if err != nil {
return err
}
templateRoot = cloneDir
Expand Down
13 changes: 7 additions & 6 deletions internal/template/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ func FuncMap(cfg Config) texttemplate.FuncMap {
}

handler := sprout.New(sprout.WithLogger(slog.Default()))
if err := handler.AddRegistries(registries...); err != nil {
// Registry registration only fails on programmer error (duplicate names);
// panic here surfaces the issue during development rather than silently
// producing an incomplete FuncMap.
mustAddRegistries(handler, registries...)
return handler.Build()
}

// mustAddRegistries registers regs into h, panicking on programmer error (duplicate names).
func mustAddRegistries(h *sprout.DefaultHandler, regs ...sprout.Registry) {
if err := h.AddRegistries(regs...); err != nil {
panic("sprout registry registration failed: " + err.Error())
}

return handler.Build()
}
11 changes: 11 additions & 0 deletions internal/util/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ type CloneOptions struct {
Depth int
}

// CloneInto clones url into a new subdirectory named name inside parent.
// parent must already exist; name must not — go-git requires a non-existent destination.
// Returns the path of the created clone directory.
func CloneInto(parent, name, url string, opts CloneOptions) (string, error) {
dir := filepath.Join(parent, name)
if err := Clone(url, dir, opts); err != nil {
return "", err
}
return dir, nil
}

// Clone clones the repository at url into dir using a shallow clone (Depth 1 by default).
// dir must not already exist — go-git creates it.
// SSH URLs (git@host:path or ssh://host/path) are detected automatically and authenticated
Expand Down
16 changes: 16 additions & 0 deletions internal/util/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,19 @@ func TestClone_InvalidURL(t *testing.T) {
func TestClone_SSH(t *testing.T) {
t.Skip("SSH clone not tested — see comment above")
}

func TestCloneInto_CreatesSubdirectory(t *testing.T) {
parent := t.TempDir()

cloneDir, err := pkggit.CloneInto(parent, "repo", "https://github.com/specsnl/php85", pkggit.CloneOptions{Depth: 1})
if err != nil {
t.Fatalf("CloneInto: %v", err)
}

if cloneDir == parent {
t.Fatal("CloneInto returned parent dir instead of subdirectory")
}
if _, err := os.Stat(cloneDir + "/README.md"); os.IsNotExist(err) {
t.Error("cloned repo missing README.md")
}
}