forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package models
2
3import (
4 "fmt"
5 "strings"
6
7 "tangled.org/core/api/tangled"
8 "tangled.org/core/workflow"
9)
10
11type CloneStep struct {
12 name string
13 kind StepKind
14 commands []string
15}
16
17func (s CloneStep) Name() string {
18 return s.name
19}
20
21func (s CloneStep) Commands() []string {
22 return s.commands
23}
24
25func (s CloneStep) Command() string {
26 return strings.Join(s.commands, "\n")
27}
28
29func (s CloneStep) Kind() StepKind {
30 return s.kind
31}
32
33// BuildCloneStep generates git clone commands.
34// The caller must ensure the current working directory is set to the desired
35// workspace directory before executing these commands.
36//
37// The generated commands are:
38// - git init
39// - git remote add origin <url>
40// - git fetch --depth=<d> --recurse-submodules=<yes|no> <sha>
41// - git checkout FETCH_HEAD
42//
43// Supports all trigger types (push, PR, manual) and clone options.
44func BuildCloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata, dev bool) CloneStep {
45 if twf.Clone != nil && twf.Clone.Skip {
46 return CloneStep{}
47 }
48
49 commitSHA, err := extractCommitSHA(tr)
50 if err != nil {
51 return CloneStep{
52 kind: StepKindSystem,
53 name: "Clone repository into workspace (error)",
54 commands: []string{fmt.Sprintf("echo 'Failed to get clone info: %s' && exit 1", err.Error())},
55 }
56 }
57
58 repoURL := buildRepoURL(tr, dev)
59
60 var cloneOpts tangled.Pipeline_CloneOpts
61 if twf.Clone != nil {
62 cloneOpts = *twf.Clone
63 }
64 fetchArgs := buildFetchArgs(cloneOpts, commitSHA)
65
66 return CloneStep{
67 kind: StepKindSystem,
68 name: "Clone repository into workspace",
69 commands: []string{
70 "git init",
71 fmt.Sprintf("git remote add origin %s", repoURL),
72 fmt.Sprintf("git fetch %s", strings.Join(fetchArgs, " ")),
73 "git checkout FETCH_HEAD",
74 },
75 }
76}
77
78// extractCommitSHA extracts the commit SHA from trigger metadata based on trigger type
79func extractCommitSHA(tr tangled.Pipeline_TriggerMetadata) (string, error) {
80 switch workflow.TriggerKind(tr.Kind) {
81 case workflow.TriggerKindPush:
82 if tr.Push == nil {
83 return "", fmt.Errorf("push trigger metadata is nil")
84 }
85 return tr.Push.NewSha, nil
86
87 case workflow.TriggerKindPullRequest:
88 if tr.PullRequest == nil {
89 return "", fmt.Errorf("pull request trigger metadata is nil")
90 }
91 return tr.PullRequest.SourceSha, nil
92
93 case workflow.TriggerKindManual:
94 // Manual triggers don't have an explicit SHA in the metadata
95 // For now, return empty string - could be enhanced to fetch from default branch
96 // TODO: Implement manual trigger SHA resolution (fetch default branch HEAD)
97 return "", nil
98
99 default:
100 return "", fmt.Errorf("unknown trigger kind: %s", tr.Kind)
101 }
102}
103
104// buildRepoURL constructs the repository URL from trigger metadata
105func buildRepoURL(tr tangled.Pipeline_TriggerMetadata, devMode bool) string {
106 if tr.Repo == nil {
107 return ""
108 }
109
110 // Determine protocol
111 scheme := "https://"
112 if devMode {
113 scheme = "http://"
114 }
115
116 // Get host from knot
117 host := tr.Repo.Knot
118
119 // In dev mode, replace localhost with host.docker.internal for Docker networking
120 if devMode && strings.Contains(host, "localhost") {
121 host = strings.ReplaceAll(host, "localhost", "host.docker.internal")
122 }
123
124 // Build URL: {scheme}{knot}/{did}/{repo}
125 return fmt.Sprintf("%s%s/%s/%s", scheme, host, tr.Repo.Did, tr.Repo.Repo)
126}
127
128// buildFetchArgs constructs the arguments for git fetch based on clone options
129func buildFetchArgs(clone tangled.Pipeline_CloneOpts, sha string) []string {
130 args := []string{}
131
132 // Set fetch depth (default to 1 for shallow clone)
133 depth := clone.Depth
134 if depth == 0 {
135 depth = 1
136 }
137 args = append(args, fmt.Sprintf("--depth=%d", depth))
138
139 // Add submodules if requested
140 if clone.Submodules {
141 args = append(args, "--recurse-submodules=yes")
142 }
143
144 // Add remote and SHA
145 args = append(args, "origin")
146 if sha != "" {
147 args = append(args, sha)
148 }
149
150 return args
151}