github.com/benchkram/bob@v0.0.0-20240314204020-b7a57f2f9be9/bobrun/run.go (about) 1 package bobrun 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strconv" 8 9 "github.com/benchkram/errz" 10 "gopkg.in/yaml.v3" 11 12 "github.com/benchkram/bob/pkg/ctl" 13 "github.com/benchkram/bob/pkg/execctl" 14 "github.com/benchkram/bob/pkg/nix" 15 ) 16 17 var ErrInvalidRunType = fmt.Errorf("invalid run type") 18 19 type Run struct { 20 Type RunType 21 22 // ComposePath is the path to a docker-compose file or binary 23 // Default filename is used when empty. 24 Path string 25 26 // DependsOn run or build tasks 27 DependsOn []string `yaml:"dependsOn"` 28 29 // InitDirty runs run after this task has started and `initOnce`conpleted. 30 InitDirty string `yaml:"init"` 31 // init see InitDirty 32 init []string 33 34 // InitOnceDirty runs once during the lifetime of a run 35 // after the actual task has started. 36 InitOnceDirty string `yaml:"initOnce"` 37 // initOnce see InitOnceDirty 38 initOnce []string 39 40 // DependenciesDirty read from the bobfile 41 DependenciesDirty []string `yaml:"dependencies"` 42 43 // dependencies contain the actual dependencies merged 44 // with the global dependencies defined in the Bobfile 45 // in the order which they need to be added to PATH 46 dependencies []nix.Dependency 47 48 // URL of nixpkgs used. If empty, will use local <nixpkgs> channel 49 nixpkgs string 50 51 dir string 52 53 // env holds key=value pairs passed to the environment 54 // when the task is executed. 55 env []string 56 57 name string 58 } 59 60 func (r *Run) Name() string { 61 return r.name 62 } 63 64 func (r *Run) SetEnv(env []string) { 65 r.env = env 66 } 67 68 func (r *Run) SetNixpkgs(nixpkgs string) { 69 r.nixpkgs = nixpkgs 70 } 71 72 func (r *Run) Env() []string { 73 return r.env 74 } 75 76 func (r *Run) SetName(name string) { 77 r.name = name 78 } 79 80 func (r *Run) Dir() string { 81 return r.dir 82 } 83 84 func (r *Run) SetDir(dir string) { 85 r.dir = dir 86 } 87 88 func (r *Run) Dependencies() []nix.Dependency { 89 return r.dependencies 90 } 91 func (r *Run) SetDependencies(dependencies []nix.Dependency) { 92 r.dependencies = dependencies 93 } 94 95 func (r *Run) UnmarshalYAML(value *yaml.Node) (err error) { 96 defer errz.Recover(&err) 97 98 var values struct { 99 Lowercase []string `yaml:"dependson"` 100 Camelcase []string `yaml:"dependsOn"` 101 } 102 103 err = value.Decode(&values) 104 errz.Fatal(err) 105 106 if len(values.Lowercase) > 0 && len(values.Camelcase) > 0 { 107 errz.Fatal(errors.New("both `dependson` and `dependsOn` nodes detected near line " + strconv.Itoa(value.Line))) 108 } 109 110 dependsOn := make([]string, 0) 111 if values.Lowercase != nil && len(values.Lowercase) > 0 { 112 dependsOn = values.Lowercase 113 } 114 if values.Camelcase != nil && len(values.Camelcase) > 0 { 115 dependsOn = values.Camelcase 116 } 117 118 // new type needed to avoid infinite loop 119 type TmpRun Run 120 var tmpRun TmpRun 121 122 err = value.Decode(&tmpRun) 123 errz.Fatal(err) 124 125 tmpRun.DependsOn = dependsOn 126 127 *r = Run(tmpRun) 128 129 return nil 130 } 131 132 // Command creates a run cmd and returns a Command interface to control it. 133 // To shutdown a Command() use a cancelable context. 134 func (r *Run) Command(ctx context.Context) (rc ctl.Command, err error) { 135 defer errz.Recover(&err) 136 fmt.Printf("Creating control for run task [%s]\n", r.name) 137 138 switch r.Type { 139 case RunTypeBinary: 140 rc, err = execctl.NewCmd( 141 r.name, 142 r.Path, 143 execctl.WithEnv(r.Env()), 144 ) 145 errz.Fatal(err) 146 case RunTypeCompose: 147 rc, err = r.composeCommand(ctx) 148 errz.Fatal(err) 149 default: 150 return nil, ErrInvalidRunType 151 } 152 153 rc, err = r.WrapWithInit(ctx, rc) 154 errz.Fatal(err) 155 156 return rc, nil 157 }