Monorepo for Tangled tangled.org

spindle/models: Pipline model and setup steps

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

authored by anirudh.fi and committed by Tangled fe615590 20a4c1a7

Changed files
+210
spindle
+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
··· 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 + }