+102
spindle/models/pipeline.go
+102
spindle/models/pipeline.go
···
1
+
package models
2
+
3
+
import (
4
+
"path"
5
+
6
+
"tangled.sh/tangled.sh/core/api/tangled"
7
+
)
8
+
9
+
type Pipeline struct {
10
+
Workflows []Workflow
11
+
}
12
+
13
+
type Step struct {
14
+
Command string
15
+
Name string
16
+
Environment map[string]string
17
+
}
18
+
19
+
type Workflow struct {
20
+
Steps []Step
21
+
Environment map[string]string
22
+
Name string
23
+
Image string
24
+
}
25
+
26
+
// setupSteps get added to start of Steps
27
+
type setupSteps []Step
28
+
29
+
// addStep adds a step to the beginning of the workflow's steps.
30
+
func (ss *setupSteps) addStep(step Step) {
31
+
*ss = append(*ss, step)
32
+
}
33
+
34
+
// ToPipeline converts a tangled.Pipeline into a model.Pipeline.
35
+
// In the process, dependencies are resolved: nixpkgs deps
36
+
// are constructed atop nixery and set as the Workflow.Image,
37
+
// and ones from custom registries
38
+
func ToPipeline(pl tangled.Pipeline, dev bool) *Pipeline {
39
+
workflows := []Workflow{}
40
+
41
+
for _, twf := range pl.Workflows {
42
+
swf := &Workflow{}
43
+
for _, tstep := range twf.Steps {
44
+
sstep := Step{}
45
+
sstep.Environment = stepEnvToMap(tstep.Environment)
46
+
sstep.Command = tstep.Command
47
+
sstep.Name = tstep.Name
48
+
swf.Steps = append(swf.Steps, sstep)
49
+
}
50
+
swf.Name = twf.Name
51
+
swf.Environment = workflowEnvToMap(twf.Environment)
52
+
swf.Image = workflowImage(twf.Dependencies)
53
+
54
+
swf.addNixProfileToPath()
55
+
setup := &setupSteps{}
56
+
57
+
setup.addStep(cloneStep(*twf, *pl.TriggerMetadata.Repo, dev))
58
+
setup.addStep(checkoutStep(*twf, *pl.TriggerMetadata))
59
+
setup.addStep(dependencyStep(*twf))
60
+
61
+
// append setup steps in order to the start of workflow steps
62
+
swf.Steps = append(*setup, swf.Steps...)
63
+
64
+
workflows = append(workflows, *swf)
65
+
}
66
+
return &Pipeline{Workflows: workflows}
67
+
}
68
+
69
+
func workflowEnvToMap(envs []*tangled.Pipeline_Workflow_Environment_Elem) map[string]string {
70
+
envMap := map[string]string{}
71
+
for _, env := range envs {
72
+
envMap[env.Key] = env.Value
73
+
}
74
+
return envMap
75
+
}
76
+
77
+
func stepEnvToMap(envs []*tangled.Pipeline_Step_Environment_Elem) map[string]string {
78
+
envMap := map[string]string{}
79
+
for _, env := range envs {
80
+
envMap[env.Key] = env.Value
81
+
}
82
+
return envMap
83
+
}
84
+
85
+
func workflowImage(deps []tangled.Pipeline_Dependencies_Elem) string {
86
+
var dependencies string
87
+
for _, d := range deps {
88
+
if d.Registry == "nixpkgs" {
89
+
dependencies = path.Join(d.Packages...)
90
+
}
91
+
}
92
+
93
+
// load defaults from somewhere else
94
+
dependencies = path.Join(dependencies, "bash", "git", "coreutils", "nix")
95
+
96
+
// TODO: this should use nixery from the config
97
+
return path.Join("nixery.dev", dependencies)
98
+
}
99
+
100
+
func (wf *Workflow) addNixProfileToPath() {
101
+
wf.Environment["PATH"] = "$PATH:/.nix-profile/bin"
102
+
}
+108
spindle/models/setup_steps.go
+108
spindle/models/setup_steps.go
···
1
+
package models
2
+
3
+
import (
4
+
"fmt"
5
+
"path"
6
+
"strings"
7
+
8
+
"tangled.sh/tangled.sh/core/api/tangled"
9
+
)
10
+
11
+
// checkoutStep checks out the specified ref in the cloned repository.
12
+
func checkoutStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerMetadata) Step {
13
+
if twf.Clone.Skip {
14
+
return Step{}
15
+
}
16
+
17
+
var ref string
18
+
switch tr.Kind {
19
+
case "push":
20
+
ref = tr.Push.Ref
21
+
case "pull_request":
22
+
ref = tr.PullRequest.TargetBranch
23
+
24
+
// TODO: this needs to be specified in lexicon
25
+
case "manual":
26
+
ref = tr.Repo.DefaultBranch
27
+
}
28
+
29
+
checkoutCmd := fmt.Sprintf("git config advice.detachedHead false; git checkout --progress --force %s", ref)
30
+
31
+
return Step{
32
+
Command: checkoutCmd,
33
+
Name: "Checkout ref " + ref,
34
+
}
35
+
}
36
+
37
+
// cloneOptsAsSteps processes clone options and adds corresponding steps
38
+
// to the beginning of the workflow's step list if cloning is not skipped.
39
+
func cloneStep(twf tangled.Pipeline_Workflow, tr tangled.Pipeline_TriggerRepo, dev bool) Step {
40
+
if twf.Clone.Skip {
41
+
return Step{}
42
+
}
43
+
44
+
uri := "https://"
45
+
if dev {
46
+
uri = "http://"
47
+
tr.Knot = strings.ReplaceAll(tr.Knot, "localhost", "host.docker.internal")
48
+
}
49
+
50
+
cloneUrl := uri + path.Join(tr.Knot, tr.Did, tr.Repo)
51
+
cloneCmd := []string{"git", "clone", cloneUrl, "."}
52
+
53
+
// default clone depth is 1
54
+
cloneDepth := 1
55
+
if twf.Clone.Depth > 1 {
56
+
cloneDepth = int(twf.Clone.Depth)
57
+
cloneCmd = append(cloneCmd, []string{"--depth", fmt.Sprintf("%d", cloneDepth)}...)
58
+
}
59
+
60
+
if twf.Clone.Submodules {
61
+
cloneCmd = append(cloneCmd, "--recursive")
62
+
}
63
+
64
+
fmt.Println(strings.Join(cloneCmd, " "))
65
+
66
+
cloneStep := Step{
67
+
Command: strings.Join(cloneCmd, " "),
68
+
Name: "Clone repository into workspace",
69
+
}
70
+
return cloneStep
71
+
}
72
+
73
+
// dependencyStep processes dependencies defined in the workflow.
74
+
// For dependencies using a custom registry (i.e. not nixpkgs), it collects
75
+
// all packages and adds a single 'nix profile install' step to the
76
+
// beginning of the workflow's step list.
77
+
func dependencyStep(twf tangled.Pipeline_Workflow) Step {
78
+
var customPackages []string
79
+
80
+
for _, d := range twf.Dependencies {
81
+
registry := d.Registry
82
+
packages := d.Packages
83
+
84
+
if registry == "nixpkgs" {
85
+
continue
86
+
}
87
+
88
+
// collect packages from custom registries
89
+
for _, pkg := range packages {
90
+
customPackages = append(customPackages, fmt.Sprintf("'%s#%s'", registry, pkg))
91
+
}
92
+
}
93
+
94
+
if len(customPackages) > 0 {
95
+
installCmd := "nix --extra-experimental-features nix-command --extra-experimental-features flakes profile install"
96
+
cmd := fmt.Sprintf("%s %s", installCmd, strings.Join(customPackages, " "))
97
+
installStep := Step{
98
+
Command: cmd,
99
+
Name: "Install custom dependencies",
100
+
Environment: map[string]string{
101
+
"NIX_NO_COLOR": "1",
102
+
"NIX_SHOW_DOWNLOAD_PROGRESS": "0",
103
+
},
104
+
}
105
+
return installStep
106
+
}
107
+
return Step{}
108
+
}