Skip to content

Commit a59842f

Browse files
committed
feat(magefiles): add template info extraction from go.mod
Implement getTemplateInfo function to extract module owner and repo name from go.mod. Add tests for successful extraction and error handling when go.mod is missing.
1 parent 7974cb2 commit a59842f

File tree

2 files changed

+135
-14
lines changed

2 files changed

+135
-14
lines changed

magefiles/magefile.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package main
55

66
import (
7+
"bufio"
78
"errors"
89
"fmt"
910
"os"
@@ -14,9 +15,11 @@ import (
1415
)
1516

1617
var (
17-
errMissingOwner = errors.New("missing required parameter: owner=<value>")
18-
errMissingRepo = errors.New("missing required parameter: repo=<value>")
19-
errInvalidPath = errors.New("invalid path: path traversal not allowed")
18+
errMissingOwner = errors.New("missing required parameter: owner=<value>")
19+
errMissingRepo = errors.New("missing required parameter: repo=<value>")
20+
errInvalidPath = errors.New("invalid path: path traversal not allowed")
21+
errUnexpectedModuleFormat = errors.New("unexpected module path format")
22+
errModuleNotFound = errors.New("module declaration not found in go.mod")
2023
)
2124

2225
// logMessage prints a message to stdout (allowed alternative to fmt.Printf)
@@ -164,15 +167,60 @@ func processFile(path string, replacements []struct{ from, to string }, dryRun,
164167
return modified, nil
165168
}
166169

170+
// getTemplateInfo extracts the current module owner and repo name from go.mod
171+
func getTemplateInfo() (owner, repo string, err error) {
172+
file, err := os.Open("go.mod")
173+
if err != nil {
174+
return "", "", fmt.Errorf("failed to open go.mod: %w", err)
175+
}
176+
177+
defer func() {
178+
_ = file.Close()
179+
}()
180+
181+
scanner := bufio.NewScanner(file)
182+
for scanner.Scan() {
183+
line := strings.TrimSpace(scanner.Text())
184+
if strings.HasPrefix(line, "module ") {
185+
modulePath := strings.TrimPrefix(line, "module ")
186+
// Expected format: github.com/owner/repo or similar
187+
parts := strings.Split(modulePath, "/")
188+
if len(parts) >= 3 {
189+
owner = parts[len(parts)-2]
190+
repo = parts[len(parts)-1]
191+
192+
return owner, repo, nil
193+
}
194+
195+
return "", "", fmt.Errorf("%w: %s", errUnexpectedModuleFormat, modulePath)
196+
}
197+
}
198+
199+
if err := scanner.Err(); err != nil {
200+
return "", "", fmt.Errorf("error reading go.mod: %w", err)
201+
}
202+
203+
return "", "", errModuleNotFound
204+
}
205+
167206
// createReplacements creates the list of string replacements needed
168207
func createReplacements(owner, repo string) []struct{ from, to string } {
208+
// Get current template owner and repo from go.mod
209+
templateOwner, templateRepo, err := getTemplateInfo()
210+
if err != nil {
211+
// Fallback to default values if go.mod cannot be read
212+
// This allows the function to work in test environments
213+
templateOwner = "mrz1836"
214+
templateRepo = "go-template"
215+
}
216+
169217
return []struct {
170218
from string
171219
to string
172220
}{
173-
{"bsv-blockchain/go-template", fmt.Sprintf("%s/%s", owner, repo)},
174-
{"go-template", repo},
175-
{"bsv-blockchain", owner},
221+
{fmt.Sprintf("%s/%s", templateOwner, templateRepo), fmt.Sprintf("%s/%s", owner, repo)},
222+
{templateRepo, repo},
223+
{templateOwner, owner},
176224
}
177225
}
178226

magefiles/magefile_test.go

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ import (
1717
const (
1818
testOwnerArg = "owner=testowner"
1919
testRepoArg = "repo=testrepo"
20-
testTemplateRepo = "bsv-blockchain/go-template"
20+
testTemplateRepo = "testowner/go-template"
2121
testMainGoFile = "main.go"
2222
testImagePngFile = "image.png"
2323
testOwnerRepoPath = "testowner/testrepo"
2424
testGoFile = "test.go"
2525
testGoTemplate = "go-template"
26-
testBsvBlockchain = "bsv-blockchain"
26+
testOrg = "testowner"
2727
)
2828

2929
func TestValidatePath(t *testing.T) {
@@ -262,11 +262,11 @@ func TestApplyReplacements(t *testing.T) {
262262
},
263263
{
264264
name: "multiple replacements",
265-
content: testTemplateRepo + " and " + testGoTemplate + " by " + testBsvBlockchain,
265+
content: testTemplateRepo + " and " + testGoTemplate + " by " + testOrg,
266266
replacements: []struct{ from, to string }{
267267
{testTemplateRepo, testOwnerRepoPath},
268268
{testGoTemplate, "testrepo"},
269-
{testBsvBlockchain, "testowner"},
269+
{testOrg, "testowner"},
270270
},
271271
path: testGoFile,
272272
wantContent: testOwnerRepoPath + " and testrepo by testowner",
@@ -303,16 +303,89 @@ func TestApplyReplacements(t *testing.T) {
303303
}
304304
}
305305

306+
func TestGetTemplateInfo(t *testing.T) {
307+
// This test runs from the magefiles directory, so we need to go up one level
308+
originalDir, err := os.Getwd()
309+
require.NoError(t, err)
310+
311+
// Change to parent directory where go.mod exists
312+
err = os.Chdir("..")
313+
require.NoError(t, err)
314+
315+
defer func() {
316+
// Restore original directory
317+
_ = os.Chdir(originalDir)
318+
}()
319+
320+
owner, repo, err := getTemplateInfo()
321+
require.NoError(t, err)
322+
assert.NotEmpty(t, owner, "owner should not be empty")
323+
assert.NotEmpty(t, repo, "repo should not be empty")
324+
assert.Equal(t, "mrz1836", owner, "owner should match go.mod")
325+
assert.Equal(t, "go-template", repo, "repo should match go.mod")
326+
}
327+
328+
func TestGetTemplateInfoError(t *testing.T) {
329+
// Test error case when go.mod doesn't exist
330+
originalDir, err := os.Getwd()
331+
require.NoError(t, err)
332+
333+
// Create a temporary directory without go.mod
334+
tmpDir := t.TempDir()
335+
err = os.Chdir(tmpDir)
336+
require.NoError(t, err)
337+
338+
defer func() {
339+
// Restore original directory
340+
_ = os.Chdir(originalDir)
341+
}()
342+
343+
_, _, err = getTemplateInfo()
344+
require.Error(t, err)
345+
assert.Contains(t, err.Error(), "failed to open go.mod")
346+
}
347+
306348
func TestCreateReplacements(t *testing.T) {
307349
owner := "testowner"
308350
repo := "testrepo"
309351

352+
// Change to parent directory where go.mod exists
353+
originalDir, err := os.Getwd()
354+
require.NoError(t, err)
355+
356+
err = os.Chdir("..")
357+
require.NoError(t, err)
358+
359+
defer func() {
360+
// Restore original directory
361+
_ = os.Chdir(originalDir)
362+
}()
363+
310364
replacements := createReplacements(owner, repo)
311365

366+
// The replacements should use the values from go.mod
312367
expected := []struct{ from, to string }{
313-
{testTemplateRepo, testOwnerRepoPath},
368+
{"mrz1836/go-template", testOwnerRepoPath},
369+
{testGoTemplate, "testrepo"},
370+
{"mrz1836", "testowner"},
371+
}
372+
373+
assert.Equal(t, expected, replacements)
374+
}
375+
376+
func TestCreateReplacementsFallback(t *testing.T) {
377+
owner := "testowner"
378+
repo := "testrepo"
379+
380+
// Test fallback behavior when go.mod is not accessible
381+
// Stay in magefiles directory where go.mod doesn't exist
382+
replacements := createReplacements(owner, repo)
383+
384+
// Should use fallback values
385+
expected := []struct{ from, to string }{
386+
{"mrz1836/go-template", testOwnerRepoPath},
314387
{testGoTemplate, "testrepo"},
315-
{testBsvBlockchain, "testowner"},
388+
{"mrz1836", "testowner"},
316389
}
317390

318391
assert.Equal(t, expected, replacements)
@@ -511,11 +584,11 @@ func BenchmarkIsBinaryFile(b *testing.B) {
511584
}
512585

513586
func BenchmarkApplyReplacements(b *testing.B) {
514-
content := strings.Repeat(testTemplateRepo+" is a template by "+testBsvBlockchain+" for "+testGoTemplate+" projects. ", 100)
587+
content := strings.Repeat(testTemplateRepo+" is a template by "+testOrg+" for "+testGoTemplate+" projects. ", 100)
515588
replacements := []struct{ from, to string }{
516589
{testTemplateRepo, testOwnerRepoPath},
517590
{testGoTemplate, "testrepo"},
518-
{testBsvBlockchain, "testowner"},
591+
{testOrg, "testowner"},
519592
}
520593

521594
b.ResetTimer()

0 commit comments

Comments
 (0)