+1
-1
spindle/engines/nixery/engine.go
+1
-1
spindle/engines/nixery/engine.go
···
109
109
setup := &setupSteps{}
110
110
111
111
setup.addStep(nixConfStep())
112
-
setup.addStep(cloneStep(twf, *tpl.TriggerMetadata, e.cfg.Server.Dev))
112
+
setup.addStep(models.BuildCloneStep(twf, *tpl.TriggerMetadata, workspaceDir, e.cfg.Server.Dev))
113
113
// this step could be empty
114
114
if s := dependencyStep(dwf.Dependencies); s != nil {
115
115
setup.addStep(*s)
-34
spindle/engines/nixery/setup_steps.go
-34
spindle/engines/nixery/setup_steps.go
···
3
3
import (
4
4
"fmt"
5
5
"strings"
6
-
7
-
"tangled.org/core/api/tangled"
8
-
"tangled.org/core/spindle/workflow"
9
6
)
10
7
11
8
func nixConfStep() Step {
···
15
12
return Step{
16
13
command: setupCmd,
17
14
name: "Configure Nix",
18
-
}
19
-
}
20
-
21
-
// cloneStep uses the shared clone step builder to generate git clone commands.
22
-
// The shared builder handles:
23
-
// - git init
24
-
// - git remote add origin <url>
25
-
// - git fetch --depth=<d> --recurse-submodules=<yes|no> <sha>
26
-
// - git checkout FETCH_HEAD
27
-
// And supports all trigger types (push, PR, manual) and clone options.
28
-
func cloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata, dev bool) Step {
29
-
info, err := workflow.GetCloneInfo(workflow.CloneOptions{
30
-
Workflow: twf,
31
-
TriggerMetadata: tr,
32
-
DevMode: dev,
33
-
WorkspaceDir: workspaceDir,
34
-
})
35
-
if err != nil {
36
-
return Step{
37
-
command: fmt.Sprintf("echo 'Failed to get clone info: %s' && exit 1", err.Error()),
38
-
name: "Clone repository into workspace (error)",
39
-
}
40
-
}
41
-
42
-
if info.Skip {
43
-
return Step{}
44
-
}
45
-
46
-
return Step{
47
-
command: strings.Join(info.Commands, "\n"),
48
-
name: "Clone repository into workspace",
49
15
}
50
16
}
51
17
+364
spindle/models/clone_test.go
+364
spindle/models/clone_test.go
···
1
+
package models
2
+
3
+
import (
4
+
"strings"
5
+
"testing"
6
+
7
+
"tangled.org/core/api/tangled"
8
+
"tangled.org/core/workflow"
9
+
)
10
+
11
+
func TestBuildCloneStep_PushTrigger(t *testing.T) {
12
+
twf := tangled.Pipeline_Workflow{
13
+
Clone: &tangled.Pipeline_CloneOpts{
14
+
Depth: 1,
15
+
Submodules: false,
16
+
Skip: false,
17
+
},
18
+
}
19
+
tr := tangled.Pipeline_TriggerMetadata{
20
+
Kind: string(workflow.TriggerKindPush),
21
+
Push: &tangled.Pipeline_PushTriggerData{
22
+
NewSha: "abc123",
23
+
OldSha: "def456",
24
+
Ref: "refs/heads/main",
25
+
},
26
+
Repo: &tangled.Pipeline_TriggerRepo{
27
+
Knot: "example.com",
28
+
Did: "did:plc:user123",
29
+
Repo: "my-repo",
30
+
},
31
+
}
32
+
33
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
34
+
35
+
if step.Kind() != StepKindSystem {
36
+
t.Errorf("Expected StepKindSystem, got %v", step.Kind())
37
+
}
38
+
39
+
if step.Name() != "Clone repository into workspace" {
40
+
t.Errorf("Expected 'Clone repository into workspace', got '%s'", step.Name())
41
+
}
42
+
43
+
commands := step.Commands()
44
+
if len(commands) != 5 {
45
+
t.Errorf("Expected 5 commands, got %d", len(commands))
46
+
}
47
+
48
+
// Verify commands contain expected git operations
49
+
allCmds := strings.Join(commands, " ")
50
+
if !strings.Contains(allCmds, "git init") {
51
+
t.Error("Commands should contain 'git init'")
52
+
}
53
+
if !strings.Contains(allCmds, "git remote add origin") {
54
+
t.Error("Commands should contain 'git remote add origin'")
55
+
}
56
+
if !strings.Contains(allCmds, "git fetch") {
57
+
t.Error("Commands should contain 'git fetch'")
58
+
}
59
+
if !strings.Contains(allCmds, "abc123") {
60
+
t.Error("Commands should contain commit SHA")
61
+
}
62
+
if !strings.Contains(allCmds, "git checkout FETCH_HEAD") {
63
+
t.Error("Commands should contain 'git checkout FETCH_HEAD'")
64
+
}
65
+
if !strings.Contains(allCmds, "https://example.com/did:plc:user123/my-repo") {
66
+
t.Error("Commands should contain expected repo URL")
67
+
}
68
+
}
69
+
70
+
func TestBuildCloneStep_PullRequestTrigger(t *testing.T) {
71
+
twf := tangled.Pipeline_Workflow{
72
+
Clone: &tangled.Pipeline_CloneOpts{
73
+
Depth: 1,
74
+
Skip: false,
75
+
},
76
+
}
77
+
tr := tangled.Pipeline_TriggerMetadata{
78
+
Kind: string(workflow.TriggerKindPullRequest),
79
+
PullRequest: &tangled.Pipeline_PullRequestTriggerData{
80
+
SourceSha: "pr-sha-789",
81
+
SourceBranch: "feature-branch",
82
+
TargetBranch: "main",
83
+
Action: "opened",
84
+
},
85
+
Repo: &tangled.Pipeline_TriggerRepo{
86
+
Knot: "example.com",
87
+
Did: "did:plc:user123",
88
+
Repo: "my-repo",
89
+
},
90
+
}
91
+
92
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
93
+
94
+
allCmds := strings.Join(step.Commands(), " ")
95
+
if !strings.Contains(allCmds, "pr-sha-789") {
96
+
t.Error("Commands should contain PR commit SHA")
97
+
}
98
+
}
99
+
100
+
func TestBuildCloneStep_ManualTrigger(t *testing.T) {
101
+
twf := tangled.Pipeline_Workflow{
102
+
Clone: &tangled.Pipeline_CloneOpts{
103
+
Depth: 1,
104
+
Skip: false,
105
+
},
106
+
}
107
+
tr := tangled.Pipeline_TriggerMetadata{
108
+
Kind: string(workflow.TriggerKindManual),
109
+
Manual: &tangled.Pipeline_ManualTriggerData{
110
+
Inputs: nil,
111
+
},
112
+
Repo: &tangled.Pipeline_TriggerRepo{
113
+
Knot: "example.com",
114
+
Did: "did:plc:user123",
115
+
Repo: "my-repo",
116
+
},
117
+
}
118
+
119
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
120
+
121
+
// Manual triggers don't have a SHA yet (TODO), so git fetch won't include a SHA
122
+
allCmds := strings.Join(step.Commands(), " ")
123
+
// Should still have basic git commands
124
+
if !strings.Contains(allCmds, "git init") {
125
+
t.Error("Commands should contain 'git init'")
126
+
}
127
+
if !strings.Contains(allCmds, "git fetch") {
128
+
t.Error("Commands should contain 'git fetch'")
129
+
}
130
+
}
131
+
132
+
func TestBuildCloneStep_SkipFlag(t *testing.T) {
133
+
twf := tangled.Pipeline_Workflow{
134
+
Clone: &tangled.Pipeline_CloneOpts{
135
+
Skip: true,
136
+
},
137
+
}
138
+
tr := tangled.Pipeline_TriggerMetadata{
139
+
Kind: string(workflow.TriggerKindPush),
140
+
Push: &tangled.Pipeline_PushTriggerData{
141
+
NewSha: "abc123",
142
+
},
143
+
Repo: &tangled.Pipeline_TriggerRepo{
144
+
Knot: "example.com",
145
+
Did: "did:plc:user123",
146
+
Repo: "my-repo",
147
+
},
148
+
}
149
+
150
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
151
+
152
+
// Empty step when skip is true
153
+
if step.Name() != "" {
154
+
t.Error("Expected empty step name when Skip is true")
155
+
}
156
+
if len(step.Commands()) != 0 {
157
+
t.Errorf("Expected no commands when Skip is true, got %d commands", len(step.Commands()))
158
+
}
159
+
}
160
+
161
+
func TestBuildCloneStep_DevMode(t *testing.T) {
162
+
twf := tangled.Pipeline_Workflow{
163
+
Clone: &tangled.Pipeline_CloneOpts{
164
+
Depth: 1,
165
+
Skip: false,
166
+
},
167
+
}
168
+
tr := tangled.Pipeline_TriggerMetadata{
169
+
Kind: string(workflow.TriggerKindPush),
170
+
Push: &tangled.Pipeline_PushTriggerData{
171
+
NewSha: "abc123",
172
+
},
173
+
Repo: &tangled.Pipeline_TriggerRepo{
174
+
Knot: "localhost:3000",
175
+
Did: "did:plc:user123",
176
+
Repo: "my-repo",
177
+
},
178
+
}
179
+
180
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", true)
181
+
182
+
// In dev mode, should use http:// and replace localhost with host.docker.internal
183
+
allCmds := strings.Join(step.Commands(), " ")
184
+
expectedURL := "http://host.docker.internal:3000/did:plc:user123/my-repo"
185
+
if !strings.Contains(allCmds, expectedURL) {
186
+
t.Errorf("Expected dev mode URL '%s' in commands", expectedURL)
187
+
}
188
+
}
189
+
190
+
func TestBuildCloneStep_DepthAndSubmodules(t *testing.T) {
191
+
twf := tangled.Pipeline_Workflow{
192
+
Clone: &tangled.Pipeline_CloneOpts{
193
+
Depth: 10,
194
+
Submodules: true,
195
+
Skip: false,
196
+
},
197
+
}
198
+
tr := tangled.Pipeline_TriggerMetadata{
199
+
Kind: string(workflow.TriggerKindPush),
200
+
Push: &tangled.Pipeline_PushTriggerData{
201
+
NewSha: "abc123",
202
+
},
203
+
Repo: &tangled.Pipeline_TriggerRepo{
204
+
Knot: "example.com",
205
+
Did: "did:plc:user123",
206
+
Repo: "my-repo",
207
+
},
208
+
}
209
+
210
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
211
+
212
+
allCmds := strings.Join(step.Commands(), " ")
213
+
if !strings.Contains(allCmds, "--depth=10") {
214
+
t.Error("Commands should contain '--depth=10'")
215
+
}
216
+
217
+
if !strings.Contains(allCmds, "--recurse-submodules=yes") {
218
+
t.Error("Commands should contain '--recurse-submodules=yes'")
219
+
}
220
+
}
221
+
222
+
func TestBuildCloneStep_DefaultDepth(t *testing.T) {
223
+
twf := tangled.Pipeline_Workflow{
224
+
Clone: &tangled.Pipeline_CloneOpts{
225
+
Depth: 0, // Default should be 1
226
+
Skip: false,
227
+
},
228
+
}
229
+
tr := tangled.Pipeline_TriggerMetadata{
230
+
Kind: string(workflow.TriggerKindPush),
231
+
Push: &tangled.Pipeline_PushTriggerData{
232
+
NewSha: "abc123",
233
+
},
234
+
Repo: &tangled.Pipeline_TriggerRepo{
235
+
Knot: "example.com",
236
+
Did: "did:plc:user123",
237
+
Repo: "my-repo",
238
+
},
239
+
}
240
+
241
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
242
+
243
+
allCmds := strings.Join(step.Commands(), " ")
244
+
if !strings.Contains(allCmds, "--depth=1") {
245
+
t.Error("Commands should default to '--depth=1'")
246
+
}
247
+
}
248
+
249
+
func TestBuildCloneStep_NilPushData(t *testing.T) {
250
+
twf := tangled.Pipeline_Workflow{
251
+
Clone: &tangled.Pipeline_CloneOpts{
252
+
Depth: 1,
253
+
Skip: false,
254
+
},
255
+
}
256
+
tr := tangled.Pipeline_TriggerMetadata{
257
+
Kind: string(workflow.TriggerKindPush),
258
+
Push: nil, // Nil push data should create error step
259
+
Repo: &tangled.Pipeline_TriggerRepo{
260
+
Knot: "example.com",
261
+
Did: "did:plc:user123",
262
+
Repo: "my-repo",
263
+
},
264
+
}
265
+
266
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
267
+
268
+
// Should return an error step
269
+
if !strings.Contains(step.Name(), "error") {
270
+
t.Error("Expected error in step name when push data is nil")
271
+
}
272
+
273
+
allCmds := strings.Join(step.Commands(), " ")
274
+
if !strings.Contains(allCmds, "Failed to get clone info") {
275
+
t.Error("Commands should contain error message")
276
+
}
277
+
if !strings.Contains(allCmds, "exit 1") {
278
+
t.Error("Commands should exit with error")
279
+
}
280
+
}
281
+
282
+
func TestBuildCloneStep_NilPRData(t *testing.T) {
283
+
twf := tangled.Pipeline_Workflow{
284
+
Clone: &tangled.Pipeline_CloneOpts{
285
+
Depth: 1,
286
+
Skip: false,
287
+
},
288
+
}
289
+
tr := tangled.Pipeline_TriggerMetadata{
290
+
Kind: string(workflow.TriggerKindPullRequest),
291
+
PullRequest: nil, // Nil PR data should create error step
292
+
Repo: &tangled.Pipeline_TriggerRepo{
293
+
Knot: "example.com",
294
+
Did: "did:plc:user123",
295
+
Repo: "my-repo",
296
+
},
297
+
}
298
+
299
+
step := BuildCloneStep(twf, tr, "/tangled/workspace", false)
300
+
301
+
// Should return an error step
302
+
if !strings.Contains(step.Name(), "error") {
303
+
t.Error("Expected error in step name when pull request data is nil")
304
+
}
305
+
306
+
allCmds := strings.Join(step.Commands(), " ")
307
+
if !strings.Contains(allCmds, "Failed to get clone info") {
308
+
t.Error("Commands should contain error message")
309
+
}
310
+
}
311
+
312
+
func TestBuildCloneStep_CustomWorkspace(t *testing.T) {
313
+
twf := tangled.Pipeline_Workflow{
314
+
Clone: &tangled.Pipeline_CloneOpts{
315
+
Depth: 1,
316
+
Skip: false,
317
+
},
318
+
}
319
+
tr := tangled.Pipeline_TriggerMetadata{
320
+
Kind: string(workflow.TriggerKindPush),
321
+
Push: &tangled.Pipeline_PushTriggerData{
322
+
NewSha: "abc123",
323
+
},
324
+
Repo: &tangled.Pipeline_TriggerRepo{
325
+
Knot: "example.com",
326
+
Did: "did:plc:user123",
327
+
Repo: "my-repo",
328
+
},
329
+
}
330
+
331
+
step := BuildCloneStep(twf, tr, "/custom/path", false)
332
+
333
+
allCmds := strings.Join(step.Commands(), " ")
334
+
if !strings.Contains(allCmds, "/custom/path") {
335
+
t.Error("Commands should use custom workspace directory")
336
+
}
337
+
}
338
+
339
+
func TestBuildCloneStep_DefaultWorkspace(t *testing.T) {
340
+
twf := tangled.Pipeline_Workflow{
341
+
Clone: &tangled.Pipeline_CloneOpts{
342
+
Depth: 1,
343
+
Skip: false,
344
+
},
345
+
}
346
+
tr := tangled.Pipeline_TriggerMetadata{
347
+
Kind: string(workflow.TriggerKindPush),
348
+
Push: &tangled.Pipeline_PushTriggerData{
349
+
NewSha: "abc123",
350
+
},
351
+
Repo: &tangled.Pipeline_TriggerRepo{
352
+
Knot: "example.com",
353
+
Did: "did:plc:user123",
354
+
Repo: "my-repo",
355
+
},
356
+
}
357
+
358
+
step := BuildCloneStep(twf, tr, "", false) // Empty should default to /tangled/workspace
359
+
360
+
allCmds := strings.Join(step.Commands(), " ")
361
+
if !strings.Contains(allCmds, "/tangled/workspace") {
362
+
t.Error("Commands should default to /tangled/workspace")
363
+
}
364
+
}
+49
-36
spindle/workflow/clone.go
spindle/models/clone.go
+49
-36
spindle/workflow/clone.go
spindle/models/clone.go
···
1
-
package workflow
1
+
package models
2
2
3
3
import (
4
4
"fmt"
···
8
8
"tangled.org/core/workflow"
9
9
)
10
10
11
-
type CloneOptions struct {
12
-
Workflow tangled.Pipeline_Workflow
13
-
TriggerMetadata tangled.Pipeline_TriggerMetadata
14
-
DevMode bool
15
-
WorkspaceDir string
11
+
type CloneStep struct {
12
+
name string
13
+
kind StepKind
14
+
commands []string
15
+
}
16
+
17
+
func (s CloneStep) Name() string {
18
+
return s.name
19
+
}
20
+
21
+
func (s CloneStep) Commands() []string {
22
+
return s.commands
23
+
}
24
+
25
+
func (s CloneStep) Command() string {
26
+
return strings.Join(s.commands, "\n")
16
27
}
17
28
18
-
type CloneInfo struct {
19
-
Commands []string
20
-
RepoURL string
21
-
CommitSHA string
22
-
Skip bool
29
+
func (s CloneStep) Kind() StepKind {
30
+
return s.kind
23
31
}
24
32
25
-
// GetCloneInfo generates git clone commands and metadata from pipeline trigger metadata
26
-
func GetCloneInfo(opts CloneOptions) (*CloneInfo, error) {
27
-
if opts.Workflow.Clone != nil && opts.Workflow.Clone.Skip {
28
-
return &CloneInfo{Skip: true}, nil
33
+
// BuildCloneStep generates git clone commands.
34
+
// The shared builder handles:
35
+
// - git init
36
+
// - git remote add origin <url>
37
+
// - git fetch --depth=<d> --recurse-submodules=<yes|no> <sha>
38
+
// - git checkout FETCH_HEAD
39
+
// And supports all trigger types (push, PR, manual) and clone options.
40
+
func BuildCloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata, workspaceDir string, dev bool) CloneStep {
41
+
if twf.Clone != nil && twf.Clone.Skip {
42
+
return CloneStep{}
29
43
}
30
44
31
-
commitSHA, err := extractCommitSHA(opts.TriggerMetadata)
45
+
commitSHA, err := extractCommitSHA(tr)
32
46
if err != nil {
33
-
return nil, fmt.Errorf("failed to extract commit SHA: %w", err)
47
+
return CloneStep{
48
+
kind: StepKindSystem,
49
+
name: "Clone repository into workspace (error)",
50
+
commands: []string{fmt.Sprintf("echo 'Failed to get clone info: %s' && exit 1", err.Error())},
51
+
}
34
52
}
35
53
36
-
repoURL := buildRepoURL(opts.TriggerMetadata, opts.DevMode)
54
+
repoURL := buildRepoURL(tr, dev)
37
55
38
-
workspaceDir := opts.WorkspaceDir
39
56
if workspaceDir == "" {
40
57
workspaceDir = "/tangled/workspace"
41
58
}
···
44
61
remoteCmd := fmt.Sprintf("git remote add origin %s", repoURL)
45
62
46
63
var cloneOpts tangled.Pipeline_CloneOpts
47
-
if opts.Workflow.Clone != nil {
48
-
cloneOpts = *opts.Workflow.Clone
64
+
if twf.Clone != nil {
65
+
cloneOpts = *twf.Clone
49
66
}
50
67
fetchArgs := buildFetchArgs(cloneOpts, commitSHA)
51
68
fetchCmd := fmt.Sprintf("git fetch %s", strings.Join(fetchArgs, " "))
52
-
53
69
checkoutCmd := "git checkout FETCH_HEAD"
54
-
55
-
commands := []string{
56
-
initCmd,
57
-
fmt.Sprintf("cd %s", workspaceDir),
58
-
remoteCmd,
59
-
fetchCmd,
60
-
checkoutCmd,
70
+
71
+
return CloneStep{
72
+
kind: StepKindSystem,
73
+
name: "Clone repository into workspace",
74
+
commands: []string{
75
+
initCmd,
76
+
fmt.Sprintf("cd %s", workspaceDir),
77
+
remoteCmd,
78
+
fetchCmd,
79
+
checkoutCmd,
80
+
},
61
81
}
62
-
63
-
return &CloneInfo{
64
-
Commands: commands,
65
-
RepoURL: repoURL,
66
-
CommitSHA: commitSHA,
67
-
Skip: false,
68
-
}, nil
69
82
}
70
83
71
84
// extractCommitSHA extracts the commit SHA from trigger metadata based on trigger type
-420
spindle/workflow/clone_test.go
-420
spindle/workflow/clone_test.go
···
1
-
package workflow
2
-
3
-
import (
4
-
"strings"
5
-
"testing"
6
-
7
-
"tangled.org/core/api/tangled"
8
-
"tangled.org/core/workflow"
9
-
)
10
-
11
-
func TestGetCloneInfo_PushTrigger(t *testing.T) {
12
-
cfg := CloneOptions{
13
-
Workflow: tangled.Pipeline_Workflow{
14
-
Clone: &tangled.Pipeline_CloneOpts{
15
-
Depth: 1,
16
-
Submodules: false,
17
-
Skip: false,
18
-
},
19
-
},
20
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
21
-
Kind: string(workflow.TriggerKindPush),
22
-
Push: &tangled.Pipeline_PushTriggerData{
23
-
NewSha: "abc123",
24
-
OldSha: "def456",
25
-
Ref: "refs/heads/main",
26
-
},
27
-
Repo: &tangled.Pipeline_TriggerRepo{
28
-
Knot: "example.com",
29
-
Did: "did:plc:user123",
30
-
Repo: "my-repo",
31
-
},
32
-
},
33
-
DevMode: false,
34
-
WorkspaceDir: "/tangled/workspace",
35
-
}
36
-
37
-
info, err := GetCloneInfo(cfg)
38
-
if err != nil {
39
-
t.Fatalf("GetCloneInfo failed: %v", err)
40
-
}
41
-
42
-
if info.Skip {
43
-
t.Error("Expected Skip to be false")
44
-
}
45
-
46
-
if info.CommitSHA != "abc123" {
47
-
t.Errorf("Expected CommitSHA 'abc123', got '%s'", info.CommitSHA)
48
-
}
49
-
50
-
expectedURL := "https://example.com/did:plc:user123/my-repo"
51
-
if info.RepoURL != expectedURL {
52
-
t.Errorf("Expected RepoURL '%s', got '%s'", expectedURL, info.RepoURL)
53
-
}
54
-
55
-
if len(info.Commands) != 5 {
56
-
t.Errorf("Expected 5 commands, got %d", len(info.Commands))
57
-
}
58
-
59
-
// Verify commands contain expected git operations
60
-
allCmds := strings.Join(info.Commands, " ")
61
-
if !strings.Contains(allCmds, "git init") {
62
-
t.Error("Commands should contain 'git init'")
63
-
}
64
-
if !strings.Contains(allCmds, "git remote add origin") {
65
-
t.Error("Commands should contain 'git remote add origin'")
66
-
}
67
-
if !strings.Contains(allCmds, "git fetch") {
68
-
t.Error("Commands should contain 'git fetch'")
69
-
}
70
-
if !strings.Contains(allCmds, "abc123") {
71
-
t.Error("Commands should contain commit SHA")
72
-
}
73
-
if !strings.Contains(allCmds, "git checkout FETCH_HEAD") {
74
-
t.Error("Commands should contain 'git checkout FETCH_HEAD'")
75
-
}
76
-
}
77
-
78
-
func TestGetCloneInfo_PullRequestTrigger(t *testing.T) {
79
-
cfg := CloneOptions{
80
-
Workflow: tangled.Pipeline_Workflow{
81
-
Clone: &tangled.Pipeline_CloneOpts{
82
-
Depth: 1,
83
-
Skip: false,
84
-
},
85
-
},
86
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
87
-
Kind: string(workflow.TriggerKindPullRequest),
88
-
PullRequest: &tangled.Pipeline_PullRequestTriggerData{
89
-
SourceSha: "pr-sha-789",
90
-
SourceBranch: "feature-branch",
91
-
TargetBranch: "main",
92
-
Action: "opened",
93
-
},
94
-
Repo: &tangled.Pipeline_TriggerRepo{
95
-
Knot: "example.com",
96
-
Did: "did:plc:user123",
97
-
Repo: "my-repo",
98
-
},
99
-
},
100
-
DevMode: false,
101
-
WorkspaceDir: "/tangled/workspace",
102
-
}
103
-
104
-
info, err := GetCloneInfo(cfg)
105
-
if err != nil {
106
-
t.Fatalf("GetCloneInfo failed: %v", err)
107
-
}
108
-
109
-
if info.CommitSHA != "pr-sha-789" {
110
-
t.Errorf("Expected CommitSHA 'pr-sha-789', got '%s'", info.CommitSHA)
111
-
}
112
-
113
-
allCmds := strings.Join(info.Commands, " ")
114
-
if !strings.Contains(allCmds, "pr-sha-789") {
115
-
t.Error("Commands should contain PR commit SHA")
116
-
}
117
-
}
118
-
119
-
func TestGetCloneInfo_ManualTrigger(t *testing.T) {
120
-
cfg := CloneOptions{
121
-
Workflow: tangled.Pipeline_Workflow{
122
-
Clone: &tangled.Pipeline_CloneOpts{
123
-
Depth: 1,
124
-
Skip: false,
125
-
},
126
-
},
127
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
128
-
Kind: string(workflow.TriggerKindManual),
129
-
Manual: &tangled.Pipeline_ManualTriggerData{
130
-
Inputs: nil,
131
-
},
132
-
Repo: &tangled.Pipeline_TriggerRepo{
133
-
Knot: "example.com",
134
-
Did: "did:plc:user123",
135
-
Repo: "my-repo",
136
-
},
137
-
},
138
-
DevMode: false,
139
-
WorkspaceDir: "/tangled/workspace",
140
-
}
141
-
142
-
info, err := GetCloneInfo(cfg)
143
-
if err != nil {
144
-
t.Fatalf("GetCloneInfo failed: %v", err)
145
-
}
146
-
147
-
// Manual triggers don't have a SHA yet (TODO)
148
-
if info.CommitSHA != "" {
149
-
t.Errorf("Expected empty CommitSHA for manual trigger, got '%s'", info.CommitSHA)
150
-
}
151
-
}
152
-
153
-
func TestGetCloneInfo_SkipFlag(t *testing.T) {
154
-
cfg := CloneOptions{
155
-
Workflow: tangled.Pipeline_Workflow{
156
-
Clone: &tangled.Pipeline_CloneOpts{
157
-
Skip: true,
158
-
},
159
-
},
160
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
161
-
Kind: string(workflow.TriggerKindPush),
162
-
Push: &tangled.Pipeline_PushTriggerData{
163
-
NewSha: "abc123",
164
-
},
165
-
Repo: &tangled.Pipeline_TriggerRepo{
166
-
Knot: "example.com",
167
-
Did: "did:plc:user123",
168
-
Repo: "my-repo",
169
-
},
170
-
},
171
-
}
172
-
173
-
info, err := GetCloneInfo(cfg)
174
-
if err != nil {
175
-
t.Fatalf("GetCloneInfo failed: %v", err)
176
-
}
177
-
178
-
if !info.Skip {
179
-
t.Error("Expected Skip to be true")
180
-
}
181
-
182
-
if len(info.Commands) != 0 {
183
-
t.Errorf("Expected no commands when Skip is true, got %d commands", len(info.Commands))
184
-
}
185
-
}
186
-
187
-
func TestGetCloneInfo_DevMode(t *testing.T) {
188
-
cfg := CloneOptions{
189
-
Workflow: tangled.Pipeline_Workflow{
190
-
Clone: &tangled.Pipeline_CloneOpts{
191
-
Depth: 1,
192
-
Skip: false,
193
-
},
194
-
},
195
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
196
-
Kind: string(workflow.TriggerKindPush),
197
-
Push: &tangled.Pipeline_PushTriggerData{
198
-
NewSha: "abc123",
199
-
},
200
-
Repo: &tangled.Pipeline_TriggerRepo{
201
-
Knot: "localhost:3000",
202
-
Did: "did:plc:user123",
203
-
Repo: "my-repo",
204
-
},
205
-
},
206
-
DevMode: true,
207
-
WorkspaceDir: "/tangled/workspace",
208
-
}
209
-
210
-
info, err := GetCloneInfo(cfg)
211
-
if err != nil {
212
-
t.Fatalf("GetCloneInfo failed: %v", err)
213
-
}
214
-
215
-
// In dev mode, should use http:// and replace localhost with host.docker.internal
216
-
expectedURL := "http://host.docker.internal:3000/did:plc:user123/my-repo"
217
-
if info.RepoURL != expectedURL {
218
-
t.Errorf("Expected dev mode URL '%s', got '%s'", expectedURL, info.RepoURL)
219
-
}
220
-
}
221
-
222
-
func TestGetCloneInfo_DepthAndSubmodules(t *testing.T) {
223
-
cfg := CloneOptions{
224
-
Workflow: tangled.Pipeline_Workflow{
225
-
Clone: &tangled.Pipeline_CloneOpts{
226
-
Depth: 10,
227
-
Submodules: true,
228
-
Skip: false,
229
-
},
230
-
},
231
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
232
-
Kind: string(workflow.TriggerKindPush),
233
-
Push: &tangled.Pipeline_PushTriggerData{
234
-
NewSha: "abc123",
235
-
},
236
-
Repo: &tangled.Pipeline_TriggerRepo{
237
-
Knot: "example.com",
238
-
Did: "did:plc:user123",
239
-
Repo: "my-repo",
240
-
},
241
-
},
242
-
DevMode: false,
243
-
WorkspaceDir: "/tangled/workspace",
244
-
}
245
-
246
-
info, err := GetCloneInfo(cfg)
247
-
if err != nil {
248
-
t.Fatalf("GetCloneInfo failed: %v", err)
249
-
}
250
-
251
-
allCmds := strings.Join(info.Commands, " ")
252
-
if !strings.Contains(allCmds, "--depth=10") {
253
-
t.Error("Commands should contain '--depth=10'")
254
-
}
255
-
256
-
if !strings.Contains(allCmds, "--recurse-submodules=yes") {
257
-
t.Error("Commands should contain '--recurse-submodules=yes'")
258
-
}
259
-
}
260
-
261
-
func TestGetCloneInfo_DefaultDepth(t *testing.T) {
262
-
cfg := CloneOptions{
263
-
Workflow: tangled.Pipeline_Workflow{
264
-
Clone: &tangled.Pipeline_CloneOpts{
265
-
Depth: 0, // Default should be 1
266
-
Skip: false,
267
-
},
268
-
},
269
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
270
-
Kind: string(workflow.TriggerKindPush),
271
-
Push: &tangled.Pipeline_PushTriggerData{
272
-
NewSha: "abc123",
273
-
},
274
-
Repo: &tangled.Pipeline_TriggerRepo{
275
-
Knot: "example.com",
276
-
Did: "did:plc:user123",
277
-
Repo: "my-repo",
278
-
},
279
-
},
280
-
WorkspaceDir: "/tangled/workspace",
281
-
}
282
-
283
-
info, err := GetCloneInfo(cfg)
284
-
if err != nil {
285
-
t.Fatalf("GetCloneInfo failed: %v", err)
286
-
}
287
-
288
-
allCmds := strings.Join(info.Commands, " ")
289
-
if !strings.Contains(allCmds, "--depth=1") {
290
-
t.Error("Commands should default to '--depth=1'")
291
-
}
292
-
}
293
-
294
-
func TestGetCloneInfo_NilPushData(t *testing.T) {
295
-
cfg := CloneOptions{
296
-
Workflow: tangled.Pipeline_Workflow{
297
-
Clone: &tangled.Pipeline_CloneOpts{
298
-
Depth: 1,
299
-
Skip: false,
300
-
},
301
-
},
302
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
303
-
Kind: string(workflow.TriggerKindPush),
304
-
Push: nil, // Nil push data should return error
305
-
Repo: &tangled.Pipeline_TriggerRepo{
306
-
Knot: "example.com",
307
-
Did: "did:plc:user123",
308
-
Repo: "my-repo",
309
-
},
310
-
},
311
-
WorkspaceDir: "/tangled/workspace",
312
-
}
313
-
314
-
_, err := GetCloneInfo(cfg)
315
-
if err == nil {
316
-
t.Error("Expected error when push data is nil")
317
-
}
318
-
319
-
if !strings.Contains(err.Error(), "push trigger metadata is nil") {
320
-
t.Errorf("Expected error about nil push metadata, got: %v", err)
321
-
}
322
-
}
323
-
324
-
func TestGetCloneInfo_NilPRData(t *testing.T) {
325
-
cfg := CloneOptions{
326
-
Workflow: tangled.Pipeline_Workflow{
327
-
Clone: &tangled.Pipeline_CloneOpts{
328
-
Depth: 1,
329
-
Skip: false,
330
-
},
331
-
},
332
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
333
-
Kind: string(workflow.TriggerKindPullRequest),
334
-
PullRequest: nil, // Nil PR data should return error
335
-
Repo: &tangled.Pipeline_TriggerRepo{
336
-
Knot: "example.com",
337
-
Did: "did:plc:user123",
338
-
Repo: "my-repo",
339
-
},
340
-
},
341
-
WorkspaceDir: "/tangled/workspace",
342
-
}
343
-
344
-
_, err := GetCloneInfo(cfg)
345
-
if err == nil {
346
-
t.Error("Expected error when pull request data is nil")
347
-
}
348
-
349
-
if !strings.Contains(err.Error(), "pull request trigger metadata is nil") {
350
-
t.Errorf("Expected error about nil PR metadata, got: %v", err)
351
-
}
352
-
}
353
-
354
-
func TestGetCloneInfo_CustomWorkspace(t *testing.T) {
355
-
cfg := CloneOptions{
356
-
Workflow: tangled.Pipeline_Workflow{
357
-
Clone: &tangled.Pipeline_CloneOpts{
358
-
Depth: 1,
359
-
Skip: false,
360
-
},
361
-
},
362
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
363
-
Kind: string(workflow.TriggerKindPush),
364
-
Push: &tangled.Pipeline_PushTriggerData{
365
-
NewSha: "abc123",
366
-
},
367
-
Repo: &tangled.Pipeline_TriggerRepo{
368
-
Knot: "example.com",
369
-
Did: "did:plc:user123",
370
-
Repo: "my-repo",
371
-
},
372
-
},
373
-
DevMode: false,
374
-
WorkspaceDir: "/custom/path",
375
-
}
376
-
377
-
info, err := GetCloneInfo(cfg)
378
-
if err != nil {
379
-
t.Fatalf("GetCloneInfo failed: %v", err)
380
-
}
381
-
382
-
allCmds := strings.Join(info.Commands, " ")
383
-
if !strings.Contains(allCmds, "/custom/path") {
384
-
t.Error("Commands should use custom workspace directory")
385
-
}
386
-
}
387
-
388
-
func TestGetCloneInfo_DefaultWorkspace(t *testing.T) {
389
-
cfg := CloneOptions{
390
-
Workflow: tangled.Pipeline_Workflow{
391
-
Clone: &tangled.Pipeline_CloneOpts{
392
-
Depth: 1,
393
-
Skip: false,
394
-
},
395
-
},
396
-
TriggerMetadata: tangled.Pipeline_TriggerMetadata{
397
-
Kind: string(workflow.TriggerKindPush),
398
-
Push: &tangled.Pipeline_PushTriggerData{
399
-
NewSha: "abc123",
400
-
},
401
-
Repo: &tangled.Pipeline_TriggerRepo{
402
-
Knot: "example.com",
403
-
Did: "did:plc:user123",
404
-
Repo: "my-repo",
405
-
},
406
-
},
407
-
DevMode: false,
408
-
WorkspaceDir: "", // Empty should default to /tangled/workspace
409
-
}
410
-
411
-
info, err := GetCloneInfo(cfg)
412
-
if err != nil {
413
-
t.Fatalf("GetCloneInfo failed: %v", err)
414
-
}
415
-
416
-
allCmds := strings.Join(info.Commands, " ")
417
-
if !strings.Contains(allCmds, "/tangled/workspace") {
418
-
t.Error("Commands should default to /tangled/workspace")
419
-
}
420
-
}