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.Repo, 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 repo metadata.
105func BuildRepoURL(repo *tangled.Pipeline_TriggerRepo, devMode bool) string {
106 if repo == nil {
107 return ""
108 }
109
110 scheme := "https://"
111 if devMode {
112 scheme = "http://"
113 }
114
115 // Get host from knot
116 host := repo.Knot
117
118 // In dev mode, replace localhost with host.docker.internal for Docker networking
119 if devMode && strings.Contains(host, "localhost") {
120 host = strings.ReplaceAll(host, "localhost", "host.docker.internal")
121 }
122
123 // Build URL: {scheme}{knot}/{did}/{repo}
124 return fmt.Sprintf("%s%s/%s/%s", scheme, host, repo.Did, repo.Repo)
125}
126
127// buildFetchArgs constructs the arguments for git fetch based on clone options
128func buildFetchArgs(clone tangled.Pipeline_CloneOpts, sha string) []string {
129 args := []string{}
130
131 // Set fetch depth (default to 1 for shallow clone)
132 depth := clone.Depth
133 if depth == 0 {
134 depth = 1
135 }
136 args = append(args, fmt.Sprintf("--depth=%d", depth))
137
138 // Add submodules if requested
139 if clone.Submodules {
140 args = append(args, "--recurse-submodules=yes")
141 }
142
143 // Add remote and SHA
144 args = append(args, "origin")
145 if sha != "" {
146 args = append(args, sha)
147 }
148
149 return args
150}