Skip to content

Commit f222c59

Browse files
fix: Fix CI test failures and lint issues
- Fix git tests: Set git user.name and user.email in createTestRepo - Fix OS condition: Use runtime.GOOS for platform-agnostic tests - Fix lint issues: - Add error checking for mapstructure.Decode, rand.Read, os.WriteFile - Remove redundant nil check for map - Use strings.EqualFold for OS comparison - Replace deprecated io/ioutil with os - Remove ineffectual assignment in worktree.go - Update AGENTS.md: Add golangci-lint to pre-commit checklist
1 parent e24de59 commit f222c59

10 files changed

Lines changed: 88 additions & 42 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ go test ./internal/utils/... -v
9494
- New functionality requires unit tests
9595
- CLI commands require integration tests
9696
- All tests must pass before commit
97+
- Linting must pass before commit (`golangci-lint run ./...`)
9798

9899
### Test-Driven Development (TDD)
99100

internal/git/worktree.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ func ListWorktrees(barePath string) ([]Worktree, error) {
112112
Branch: currentBranch,
113113
})
114114
currentPath = ""
115-
currentBranch = ""
116115
}
117116
}
118117
}

internal/git/worktree_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ func createTestRepo(t *testing.T) (string, string) {
2222
t.Fatalf("initializing git repo: %v", err)
2323
}
2424

25+
cmd = exec.Command("git", "config", "user.email", "test@example.com")
26+
cmd.Dir = repoDir
27+
if err := cmd.Run(); err != nil {
28+
t.Fatalf("setting git user.email: %v", err)
29+
}
30+
31+
cmd = exec.Command("git", "config", "user.name", "Test User")
32+
cmd.Dir = repoDir
33+
if err := cmd.Run(); err != nil {
34+
t.Fatalf("setting git user.name: %v", err)
35+
}
36+
2537
readmePath := filepath.Join(repoDir, "README.md")
2638
if err := os.WriteFile(readmePath, []byte("test"), 0644); err != nil {
2739
t.Fatalf("writing README: %v", err)

internal/presets/laravel.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package presets
22

33
import (
4-
"io/ioutil"
54
"os"
65
"path/filepath"
76
"strings"
@@ -47,7 +46,7 @@ func (p *Laravel) Detect(path string) bool {
4746
}
4847

4948
composerPath := filepath.Join(path, "composer.json")
50-
data, err := ioutil.ReadFile(composerPath)
49+
data, err := os.ReadFile(composerPath)
5150
if err != nil {
5251
return false
5352
}

internal/presets/presets_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package presets
22

33
import (
4-
"io/ioutil"
4+
"os"
55
"path/filepath"
66
"testing"
77

@@ -12,7 +12,7 @@ import (
1212
func TestLaravelPreset_Detect(t *testing.T) {
1313
t.Run("detects by artisan file", func(t *testing.T) {
1414
tmpDir := t.TempDir()
15-
err := ioutil.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
15+
err := os.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
1616
require.NoError(t, err)
1717

1818
preset := NewLaravel()
@@ -22,7 +22,7 @@ func TestLaravelPreset_Detect(t *testing.T) {
2222
t.Run("detects by composer.json with laravel/framework", func(t *testing.T) {
2323
tmpDir := t.TempDir()
2424
composerJSON := `{"name": "test/app", "require": {"laravel/framework": "^10.0"}}`
25-
err := ioutil.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(composerJSON), 0644)
25+
err := os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(composerJSON), 0644)
2626
require.NoError(t, err)
2727

2828
preset := NewLaravel()
@@ -31,7 +31,7 @@ func TestLaravelPreset_Detect(t *testing.T) {
3131

3232
t.Run("does not detect without laravel indicators", func(t *testing.T) {
3333
tmpDir := t.TempDir()
34-
err := ioutil.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/app"}`), 0644)
34+
err := os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/app"}`), 0644)
3535
require.NoError(t, err)
3636

3737
preset := NewLaravel()
@@ -92,7 +92,7 @@ func TestLaravelPreset_CleanupSteps(t *testing.T) {
9292
func TestPHPPreset_Detect(t *testing.T) {
9393
t.Run("detects by composer.json", func(t *testing.T) {
9494
tmpDir := t.TempDir()
95-
err := ioutil.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/app"}`), 0644)
95+
err := os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/app"}`), 0644)
9696
require.NoError(t, err)
9797

9898
preset := NewPHP()
@@ -145,7 +145,7 @@ func TestManager_RegisterAndGet(t *testing.T) {
145145

146146
func TestManager_Detect(t *testing.T) {
147147
tmpDir := t.TempDir()
148-
err := ioutil.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
148+
err := os.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
149149
require.NoError(t, err)
150150

151151
m := NewManager()
@@ -156,7 +156,7 @@ func TestManager_Detect(t *testing.T) {
156156
func TestManager_Suggest(t *testing.T) {
157157
t.Run("returns detected preset", func(t *testing.T) {
158158
tmpDir := t.TempDir()
159-
err := ioutil.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
159+
err := os.WriteFile(filepath.Join(tmpDir, "artisan"), []byte("#!/usr/bin/env php"), 0644)
160160
require.NoError(t, err)
161161

162162
m := NewManager()
@@ -166,7 +166,7 @@ func TestManager_Suggest(t *testing.T) {
166166

167167
t.Run("returns php for unknown project", func(t *testing.T) {
168168
tmpDir := t.TempDir()
169-
err := ioutil.WriteFile(filepath.Join(tmpDir, "README.md"), []byte("# Test"), 0644)
169+
err := os.WriteFile(filepath.Join(tmpDir, "README.md"), []byte("# Test"), 0644)
170170
require.NoError(t, err)
171171

172172
m := NewManager()

internal/scaffold/conditions.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ func (e *ConditionEvaluator) fileContains(value interface{}) (bool, error) {
132132

133133
switch v := value.(type) {
134134
case map[string]interface{}:
135-
mapstructure.Decode(v, &config)
135+
if err := mapstructure.Decode(v, &config); err != nil {
136+
return false, nil
137+
}
136138
case string:
137139
return false, nil
138140
}
@@ -207,7 +209,7 @@ func (e *ConditionEvaluator) osMatches(value interface{}) (bool, error) {
207209
}
208210

209211
for _, os := range osList {
210-
if strings.ToLower(os) == strings.ToLower(runtime.GOOS) {
212+
if strings.EqualFold(os, runtime.GOOS) {
211213
return true, nil
212214
}
213215
}
@@ -249,7 +251,9 @@ func (e *ConditionEvaluator) envFileContains(value interface{}) (bool, error) {
249251

250252
switch v := value.(type) {
251253
case map[string]interface{}:
252-
mapstructure.Decode(v, &config)
254+
if err := mapstructure.Decode(v, &config); err != nil {
255+
return false, nil
256+
}
253257
case string:
254258
config.Key = v
255259
config.File = ".env"

internal/scaffold/conditions_test.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scaffold
33
import (
44
"os"
55
"path/filepath"
6+
"runtime"
67
"testing"
78

89
"github.com/michaeldyrynda/arbor/internal/scaffold/types"
@@ -35,7 +36,9 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
3536
})
3637

3738
t.Run("file_exists - file exists", func(t *testing.T) {
38-
os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644)
39+
if err := os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644); err != nil {
40+
t.Fatalf("writing test file: %v", err)
41+
}
3942

4043
result, err := evaluator.Evaluate(map[string]interface{}{
4144
"file_exists": "test.txt",
@@ -53,7 +56,9 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
5356
})
5457

5558
t.Run("file_contains - file contains pattern", func(t *testing.T) {
56-
os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/package"}`), 0644)
59+
if err := os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "test/package"}`), 0644); err != nil {
60+
t.Fatalf("writing composer.json: %v", err)
61+
}
5762

5863
result, err := evaluator.Evaluate(map[string]interface{}{
5964
"file_contains": map[string]interface{}{
@@ -66,7 +71,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
6671
})
6772

6873
t.Run("file_contains - file does not contain pattern", func(t *testing.T) {
69-
os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "other/package"}`), 0644)
74+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "composer.json"), []byte(`{"name": "other/package"}`), 0644))
7075

7176
result, err := evaluator.Evaluate(map[string]interface{}{
7277
"file_contains": map[string]interface{}{
@@ -96,15 +101,26 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
96101

97102
t.Run("os matches current OS", func(t *testing.T) {
98103
result, err := evaluator.Evaluate(map[string]interface{}{
99-
"os": "darwin",
104+
"os": runtime.GOOS,
100105
})
101106
assert.NoError(t, err)
102107
assert.True(t, result)
103108
})
104109

105110
t.Run("os does not match current OS", func(t *testing.T) {
111+
var otherOS string
112+
switch runtime.GOOS {
113+
case "darwin":
114+
otherOS = "linux"
115+
case "linux":
116+
otherOS = "darwin"
117+
case "windows":
118+
otherOS = "linux"
119+
default:
120+
otherOS = "windows"
121+
}
106122
result, err := evaluator.Evaluate(map[string]interface{}{
107-
"os": "linux",
123+
"os": otherOS,
108124
})
109125
assert.NoError(t, err)
110126
assert.False(t, result)
@@ -147,7 +163,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
147163
})
148164

149165
t.Run("env_file_contains - key exists in .env file", func(t *testing.T) {
150-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=sqlite\nAPP_KEY=base64:value\n"), 0644)
166+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=sqlite\nAPP_KEY=base64:value\n"), 0644))
151167

152168
result, err := evaluator.Evaluate(map[string]interface{}{
153169
"env_file_contains": map[string]interface{}{
@@ -160,7 +176,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
160176
})
161177

162178
t.Run("env_file_contains - key exists but is empty", func(t *testing.T) {
163-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=\nAPP_KEY=base64:value\n"), 0644)
179+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=\nAPP_KEY=base64:value\n"), 0644))
164180

165181
result, err := evaluator.Evaluate(map[string]interface{}{
166182
"env_file_contains": map[string]interface{}{
@@ -173,7 +189,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
173189
})
174190

175191
t.Run("env_file_contains - key does not exist in .env file", func(t *testing.T) {
176-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644)
192+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644))
177193

178194
result, err := evaluator.Evaluate(map[string]interface{}{
179195
"env_file_contains": map[string]interface{}{
@@ -197,7 +213,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
197213
})
198214

199215
t.Run("env_file_contains - key with default .env file", func(t *testing.T) {
200-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=sqlite\n"), 0644)
216+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("DB_CONNECTION=sqlite\n"), 0644))
201217

202218
result, err := evaluator.Evaluate(map[string]interface{}{
203219
"env_file_contains": "DB_CONNECTION",
@@ -218,7 +234,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
218234
})
219235

220236
t.Run("env_file_missing - key does not exist", func(t *testing.T) {
221-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644)
237+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644))
222238

223239
result, err := evaluator.Evaluate(map[string]interface{}{
224240
"env_file_missing": map[string]interface{}{
@@ -231,7 +247,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
231247
})
232248

233249
t.Run("env_file_missing - key exists but is empty", func(t *testing.T) {
234-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=\n"), 0644)
250+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=\n"), 0644))
235251

236252
result, err := evaluator.Evaluate(map[string]interface{}{
237253
"env_file_missing": map[string]interface{}{
@@ -244,7 +260,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
244260
})
245261

246262
t.Run("env_file_missing - key exists with value", func(t *testing.T) {
247-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644)
263+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644))
248264

249265
result, err := evaluator.Evaluate(map[string]interface{}{
250266
"env_file_missing": map[string]interface{}{
@@ -257,7 +273,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
257273
})
258274

259275
t.Run("env_file_missing - key with default .env file", func(t *testing.T) {
260-
os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644)
276+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("APP_KEY=base64:value\n"), 0644))
261277

262278
result, err := evaluator.Evaluate(map[string]interface{}{
263279
"env_file_missing": "APP_KEY",
@@ -267,7 +283,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
267283
})
268284

269285
t.Run("not condition - negates true condition", func(t *testing.T) {
270-
os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644)
286+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644))
271287

272288
result, err := evaluator.Evaluate(map[string]interface{}{
273289
"not": map[string]interface{}{
@@ -289,7 +305,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
289305
})
290306

291307
t.Run("multiple conditions - all true", func(t *testing.T) {
292-
os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644)
308+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644))
293309

294310
result, err := evaluator.Evaluate(map[string]interface{}{
295311
"file_exists": "test.txt",
@@ -300,7 +316,7 @@ func TestConditionEvaluator_Evaluate(t *testing.T) {
300316
})
301317

302318
t.Run("multiple conditions - one false", func(t *testing.T) {
303-
os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644)
319+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "test.txt"), []byte("test"), 0644))
304320

305321
result, err := evaluator.Evaluate(map[string]interface{}{
306322
"file_exists": "test.txt",
@@ -324,15 +340,15 @@ func TestConditionEvaluator_fileHasScript(t *testing.T) {
324340
evaluator := NewConditionEvaluator(ctx)
325341

326342
t.Run("package.json has script", func(t *testing.T) {
327-
os.WriteFile(filepath.Join(tmpDir, "package.json"), []byte(`{"scripts": {"build": "vite build"}}`), 0644)
343+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "package.json"), []byte(`{"scripts": {"build": "vite build"}}`), 0644))
328344

329345
result, err := evaluator.fileHasScript("build")
330346
require.NoError(t, err)
331347
assert.True(t, result)
332348
})
333349

334350
t.Run("package.json does not have script", func(t *testing.T) {
335-
os.WriteFile(filepath.Join(tmpDir, "package.json"), []byte(`{"scripts": {"test": "jest"}}`), 0644)
351+
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "package.json"), []byte(`{"scripts": {"test": "jest"}}`), 0644))
336352

337353
result, err := evaluator.fileHasScript("build")
338354
require.NoError(t, err)

internal/scaffold/steps/binary.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (s *BinaryStep) Priority() int {
5050
}
5151

5252
func (s *BinaryStep) Condition(ctx types.ScaffoldContext) bool {
53-
if s.condition != nil && len(s.condition) > 0 {
53+
if len(s.condition) > 0 {
5454
evaluator := NewStepConditionEvaluator(ctx)
5555
result, err := evaluator.Evaluate(s.condition)
5656
if err != nil {
@@ -207,7 +207,9 @@ func (e *StepConditionEvaluator) fileContains(value interface{}) (bool, error) {
207207

208208
switch v := value.(type) {
209209
case map[string]interface{}:
210-
mapstructure.Decode(v, &config)
210+
if err := mapstructure.Decode(v, &config); err != nil {
211+
return false, nil
212+
}
211213
case string:
212214
return false, nil
213215
}
@@ -258,7 +260,7 @@ func (e *StepConditionEvaluator) osMatches(value interface{}) (bool, error) {
258260
}
259261

260262
for _, os := range osList {
261-
if strings.ToLower(os) == strings.ToLower(runtime.GOOS) {
263+
if strings.EqualFold(os, runtime.GOOS) {
262264
return true, nil
263265
}
264266
}
@@ -300,7 +302,9 @@ func (e *StepConditionEvaluator) envFileContains(value interface{}) (bool, error
300302

301303
switch v := value.(type) {
302304
case map[string]interface{}:
303-
mapstructure.Decode(v, &config)
305+
if err := mapstructure.Decode(v, &config); err != nil {
306+
return false, nil
307+
}
304308
case string:
305309
config.Key = v
306310
config.File = ".env"

0 commit comments

Comments
 (0)