Skip to content

Commit

Permalink
feat: Adding file.MigrateTo, a system for migrating old files to new …
Browse files Browse the repository at this point in the history
…templates (#235)

Do you want to move a file around in your target repositories, which
already has customizations in blocks that you don't want to lose?
MigrateTo now moves this file for you, letting the new template pick it
up. The next pass of the template rendering will then pick up the
migrated file in the new location under the new template and render any
updates with the new template, as if the user had moved the file
themselves.

<!--
  !!!! README !!!! Please fill this out.

  Please follow conventional commit naming conventions:

  https://www.conventionalcommits.org/en/v1.0.0/#summary
-->

<!-- A short description of what your PR does and what it solves. -->

## What this PR does / why we need it

<!-- Notes that may be helpful for anyone reviewing this PR -->

## Notes for your reviewers
  • Loading branch information
deregtd authored Jan 27, 2025
1 parent 511a643 commit ee9e3d4
Show file tree
Hide file tree
Showing 24 changed files with 198 additions and 27 deletions.
17 changes: 17 additions & 0 deletions docs/funcs/file.MigrateTo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
order: 1004
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->

# file.MigrateTo

MigrateTo migrates the current file to a new path. If the old file
doesn't exist, it is treated as a `file.Skip`. If the old file still
exists, then it is moved to the new path (via a create and write, then
delete of the old path, not a filesystem move). If the MigrateTo target
file already exists, it is overwritten.

```go
{{- file.MigrateTo "new/path/to/file.txt" }}
```
2 changes: 1 addition & 1 deletion docs/funcs/file.Once.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1004
order: 1005
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.Path.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1005
order: 1006
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.RemoveAll.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1006
order: 1007
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.SetContents.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1007
order: 1008
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.SetPath.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1008
order: 1009
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.Skip.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1009
order: 1010
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/file.Static.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1010
order: 1011
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/module.Call.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1011
order: 1012
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/module.Export.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1012
order: 1013
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.AddToModuleHook.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1013
order: 1014
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.ApplyTemplate.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1014
order: 1015
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.Arg.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1015
order: 1016
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.Debug.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1016
order: 1017
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.Exists.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1017
order: 1018
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.GetGlobal.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1018
order: 1019
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.GetModuleHook.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1019
order: 1020
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.Include.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1020
order: 1021
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.ReadBlocks.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1021
order: 1022
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.ReadDir.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1022
order: 1023
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.ReadFile.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1023
order: 1024
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
2 changes: 1 addition & 1 deletion docs/funcs/stencil.SetGlobal.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
order: 1024
order: 1025
---

<!-- Generated by tools/docgen. DO NOT EDIT. -->
Expand Down
49 changes: 43 additions & 6 deletions internal/codegen/tpl_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,22 +170,59 @@ func (f *TplFile) Path() string {
// {{- file.Create (printf "cmd/%s.go" $commandName) 0600 now }}
// {{- stencil.Include "command" | file.SetContents }}
// {{- end }}
func (f *TplFile) Create(path string, mode os.FileMode, modTime time.Time) (out, err error) {
func (f *TplFile) Create(path string, mode os.FileMode, modTime time.Time) (out string, err error) {
f.f, err = NewFile(path, mode, modTime, f.t)
if err != nil {
return err, err
return "", err
}

f.t.Files = append(f.t.Files, f.f)
return nil, nil
return "", nil
}

// RemoveAll deletes all the contents in the provided path
//
// {{- file.RemoveAll "path" }}
func (f *TplFile) RemoveAll(path string) (out, err error) {
func (f *TplFile) RemoveAll(path string) (out string, err error) {
if err := os.RemoveAll(path); err != nil {
return err, err
return "", err
}
return "", nil
}

// MigrateTo migrates the current file to a new path. If the old file doesn't exist, it is
// treated as a `file.Skip`. If the old file still exists, then it is moved to the new
// path (via a create and write, then delete of the old path, not a filesystem move). If
// the MigrateTo target file already exists, it is overwritten.
//
// {{- file.MigrateTo "new/path/to/file.txt" }}
func (f *TplFile) MigrateTo(path string) (out string, err error) {
if _, err := osfs.Default.Stat(f.f.path); err != nil {
f.log.With("template", f.t.Path, "path", f.f.path).
Debug("Skipping MigrateTo because the file doesn't exist")
return f.Skip("MigrateTo file input doesn't exist")
}

f.log.With("path", f.f.path).With("to", path).
Debug("Migrating file to new path")
contents, err := os.ReadFile(f.f.path)
if err != nil {
return "", err
}
return nil, nil

fn, err := osfs.Default.Create(path)
if err != nil {
return "", err
}
defer fn.Close()

if _, err := fn.Write(contents); err != nil {
return "", err
}

f.log.With("path", f.f.path).
Debug("Deleting original file after migration")
f.f.Deleted = true

return "", nil
}
117 changes: 117 additions & 0 deletions internal/codegen/tpl_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package codegen

import (
"os"
"path"
"testing"

"go.rgst.io/stencil/v2/pkg/slogext"
Expand Down Expand Up @@ -152,3 +153,119 @@ func TestTplFile_OnceLockHasHistoryOfFile(t *testing.T) {
assert.Equal(t, "", fo)
assert.Equal(t, true, tplf.f.Skipped)
}

func TestTplFile_MigrateToSrcFileExistsNoDestFile(t *testing.T) {
tplf := TplFile{
f: &File{path: path.Join(t.TempDir(), "test.go")},
log: slogext.NewTestLogger(t),
}

// Set up the initial state
contents := []byte("test")
assert.NilError(t, os.WriteFile(tplf.f.path, contents, 0o644))

newPath := path.Join(t.TempDir(), "testnew.go")
os.Remove(newPath)

fo, err := tplf.MigrateTo(newPath)
assert.NilError(t, err)
assert.Equal(t, "", fo)
// Deleted should be true but file should still exist (deleted is processed later)
assert.Equal(t, true, tplf.f.Deleted)
_, err = os.Stat(tplf.f.path)
assert.NilError(t, err)

_, err = os.Stat(newPath)
assert.NilError(t, err)

contentsNew, err := os.ReadFile(newPath)
assert.NilError(t, err)
assert.Equal(t, string(contentsNew), string(contents))
}

func TestTplFile_MigrateToSrcFileExistsDestFileExists(t *testing.T) {
tplf := TplFile{
f: &File{path: path.Join(t.TempDir(), "test.go")},
log: slogext.NewTestLogger(t),
}

// Set up the initial state
contents := []byte("test")
assert.NilError(t, os.WriteFile(tplf.f.path, contents, 0o644))

newPath := path.Join(t.TempDir(), "testnew.go")
contentsNew := []byte("testnew")
assert.NilError(t, os.WriteFile(newPath, contentsNew, 0o644))

fo, err := tplf.MigrateTo(newPath)
assert.NilError(t, err)
assert.Equal(t, "", fo)
// Deleted should be true but file should still exist (deleted is processed later)
assert.Equal(t, true, tplf.f.Deleted)
_, err = os.Stat(tplf.f.path)
assert.NilError(t, err)

_, err = os.Stat(newPath)
assert.NilError(t, err)

contentsNewNew, err := os.ReadFile(newPath)
assert.NilError(t, err)
assert.Equal(t, string(contentsNewNew), string(contents))
}

func TestTplFile_MigrateToSrcFileNoExists(t *testing.T) {
tplf := TplFile{
f: &File{path: path.Join(t.TempDir(), "test.go")},
t: &Template{},
log: slogext.NewTestLogger(t),
}

// Set up the initial state
os.Remove(tplf.f.path)

newPath := path.Join(t.TempDir(), "testnew.go")

fo, err := tplf.MigrateTo(newPath)
assert.NilError(t, err)
assert.Equal(t, "", fo)

assert.Equal(t, false, tplf.f.Deleted)
assert.Equal(t, true, tplf.f.Skipped)

_, err = os.Stat(tplf.f.path)
assert.ErrorContains(t, err, "no such file")
_, err = os.Stat(newPath)
assert.ErrorContains(t, err, "no such file")
}

func TestTplFile_RemoveAll(t *testing.T) {
tplf := TplFile{
f: &File{path: "test.go"},
}

wd, err := os.Getwd()
assert.NilError(t, err, "failed to get working directory")
td := os.TempDir()
err = os.Chdir(td)
assert.NilError(t, err, "failed to change working directory")
defer os.Chdir(wd)

os.MkdirAll("test", 0o755)
os.WriteFile("test/test.go", []byte("test"), 0o644)
os.WriteFile("test/test2.go", []byte("test2"), 0o644)
_, err = os.Stat("test/test.go")
assert.NilError(t, err)
_, err = os.Stat("test/test2.go")
assert.NilError(t, err)

fo, err := tplf.RemoveAll("test")
assert.NilError(t, err)
assert.Equal(t, "", fo)

_, err = os.Stat("test")
assert.ErrorContains(t, err, "no such file")
_, err = os.Stat("test/test.go")
assert.ErrorContains(t, err, "no such file")
_, err = os.Stat("test/test2.go")
assert.ErrorContains(t, err, "no such file")
}

0 comments on commit ee9e3d4

Please sign in to comment.