Skip to content

Commit 8c20ad9

Browse files
committed
deb: Add prepend/append for paths instead of replacing full $PATH
This makes the build a bit more friendly to building outside of DALEC where we don't mess with *their* $PATH except by adding the paths we need instead of replacing the whole thing. Signed-off-by: Brian Goff <[email protected]>
1 parent 5eee3e7 commit 8c20ad9

File tree

4 files changed

+135
-72
lines changed

4 files changed

+135
-72
lines changed

frontend/deb/debroot.go

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,25 @@ func sourcePatchesDir(sOpt dalec.SourceOpts, base llb.State, dir, name string, s
6565
return append(states, series), nil
6666
}
6767

68+
type SourcePkgConfig struct {
69+
// PrependPath is a list of paths to be prepended to the $PATH var in build
70+
// scripts.
71+
PrependPath []string
72+
// APpendPath is a list of paths to be appended to the $PATH var in build
73+
// scripts.
74+
AppendPath []string
75+
}
76+
77+
// Addpath creates a SourcePkgConfig where the first argument is sets
78+
// [SourcePkgConfig.PrependPath] and the 2nd argument sets
79+
// [SourcePkgConfig.AppendPath]
80+
func AddPath(pre, post []string) SourcePkgConfig {
81+
return SourcePkgConfig{
82+
PrependPath: pre,
83+
AppendPath: post,
84+
}
85+
}
86+
6887
// Debroot creates a debian root directory suitable for use with debbuild.
6988
// This does not include sources in case you want to mount sources (instead of copying them) later.
7089
//
@@ -75,7 +94,7 @@ func sourcePatchesDir(sOpt dalec.SourceOpts, base llb.State, dir, name string, s
7594
// an upgrade even if it is technically the same underlying source.
7695
// It may be left blank but is highly recommended to set this.
7796
// Use [ReadDistroVersionID] to get a suitable value.
78-
func Debroot(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, worker, in llb.State, target, dir, distroVersionID string, opts ...llb.ConstraintsOpt) (llb.State, error) {
97+
func Debroot(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, worker, in llb.State, target, dir, distroVersionID string, cfg SourcePkgConfig, opts ...llb.ConstraintsOpt) (llb.State, error) {
7998
control, err := controlFile(spec, in, target, dir)
8099
if err != nil {
81100
return llb.Scratch(), errors.Wrap(err, "error generating control file")
@@ -122,15 +141,10 @@ func Debroot(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, worke
122141
dalecDir := base.
123142
File(llb.Mkdir(filepath.Join(dir, "dalec"), 0o755), opts...)
124143

125-
pathVar, _, err := worker.GetEnv(ctx, "PATH", opts...)
126-
if err != nil {
127-
return in, fmt.Errorf("error looking up $PATH in worker image: %w", err)
128-
}
129-
130-
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/build.sh"), 0o700, createBuildScript(spec, pathVar)), opts...))
131-
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/patch.sh"), 0o700, createPatchScript(spec, pathVar)), opts...))
132-
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/fix_sources.sh"), 0o700, fixupSources(spec, pathVar)), opts...))
133-
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/fix_perms.sh"), 0o700, fixupArtifactPerms(spec, pathVar)), opts...))
144+
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/build.sh"), 0o700, createBuildScript(spec, &cfg)), opts...))
145+
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/patch.sh"), 0o700, createPatchScript(spec, &cfg)), opts...))
146+
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/fix_sources.sh"), 0o700, fixupSources(spec, &cfg)), opts...))
147+
states = append(states, dalecDir.File(llb.Mkfile(filepath.Join(dir, "dalec/fix_perms.sh"), 0o700, fixupArtifactPerms(spec, &cfg)), opts...))
134148

135149
customEnable, err := customDHInstallSystemdPostinst(spec)
136150
if err != nil {
@@ -166,9 +180,9 @@ func Debroot(ctx context.Context, sOpt dalec.SourceOpts, spec *dalec.Spec, worke
166180
return dalec.MergeAtPath(in, states, "/"), nil
167181
}
168182

169-
func fixupArtifactPerms(spec *dalec.Spec, pathVar string) []byte {
183+
func fixupArtifactPerms(spec *dalec.Spec, cfg *SourcePkgConfig) []byte {
170184
buf := bytes.NewBuffer(nil)
171-
writeScriptHeader(buf, pathVar)
185+
writeScriptHeader(buf, cfg)
172186

173187
basePath := filepath.Join("debian", spec.Name)
174188

@@ -203,9 +217,9 @@ func fixupArtifactPerms(spec *dalec.Spec, pathVar string) []byte {
203217
// to bring those back.
204218
//
205219
// This is called from `debian/rules` after the source tarball has been extracted.
206-
func fixupSources(spec *dalec.Spec, pathVar string) []byte {
220+
func fixupSources(spec *dalec.Spec, cfg *SourcePkgConfig) []byte {
207221
buf := bytes.NewBuffer(nil)
208-
writeScriptHeader(buf, pathVar)
222+
writeScriptHeader(buf, cfg)
209223

210224
// now, we need to find all the sources that are file-backed and fix them up
211225
for name, src := range spec.Sources {
@@ -229,29 +243,41 @@ func fixupSources(spec *dalec.Spec, pathVar string) []byte {
229243
// Older go versions did not have support for the `GOMODCACHE` var
230244
// This is a hack to try and make the build work by linking the go modules
231245
// we've already fetched into to module dir under $GOPATH
232-
fmt.Fprintf(buf, `test -n "$(go env GOMODCACHE)" || (GOPATH="$(go env GOPATH)"; mkdir -p "${GOPATH}/pkg" && ln -s "$(pwd)/%s" "${GOPATH}/pkg/mod")`, gomodsName)
246+
fmt.Fprintf(buf, `env | grep PATH; test -n "$(go env GOMODCACHE)" || (GOPATH="$(go env GOPATH)"; mkdir -p "${GOPATH}/pkg" && ln -s "$(pwd)/%s" "${GOPATH}/pkg/mod")`, gomodsName)
233247
// Above command does not have a newline due to quoting issues, so add that here.
234248
fmt.Fprint(buf, "\n")
235249
}
236250

237251
return buf.Bytes()
238252
}
239253

240-
func writeScriptHeader(buf io.Writer, pathVar string) {
254+
func setupPathVar(pre, post []string) string {
255+
if len(pre) == 0 && len(post) == 0 {
256+
return ""
257+
}
258+
259+
full := append(pre, "$PATH")
260+
full = append(full, post...)
261+
return strings.Join(full, ":")
262+
}
263+
264+
func writeScriptHeader(buf io.Writer, cfg *SourcePkgConfig) {
241265
fmt.Fprintln(buf, "#!/usr/bin/env sh")
242266
fmt.Fprintln(buf)
243267

244268
fmt.Fprintln(buf, "set -ex")
245269

246-
if pathVar != "" {
247-
fmt.Fprintln(buf, `export PATH="`+pathVar+`"`)
270+
if cfg != nil {
271+
if pathVar := setupPathVar(cfg.PrependPath, cfg.AppendPath); pathVar != "" {
272+
fmt.Fprintln(buf, "export PATH="+pathVar)
273+
}
248274
}
249275
}
250276

251-
func createPatchScript(spec *dalec.Spec, pathVar string) []byte {
277+
func createPatchScript(spec *dalec.Spec, cfg *SourcePkgConfig) []byte {
252278
buf := bytes.NewBuffer(nil)
253279

254-
writeScriptHeader(buf, pathVar)
280+
writeScriptHeader(buf, cfg)
255281

256282
for name, patches := range spec.Patches {
257283
for _, patch := range patches {
@@ -263,9 +289,9 @@ func createPatchScript(spec *dalec.Spec, pathVar string) []byte {
263289
return buf.Bytes()
264290
}
265291

266-
func createBuildScript(spec *dalec.Spec, pathVar string) []byte {
292+
func createBuildScript(spec *dalec.Spec, cfg *SourcePkgConfig) []byte {
267293
buf := bytes.NewBuffer(nil)
268-
writeScriptHeader(buf, pathVar)
294+
writeScriptHeader(buf, cfg)
269295

270296
sorted := dalec.SortMapKeys(spec.Build.Env)
271297
for _, k := range sorted {

frontend/deb/distro/install.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func (d *Config) InstallBuildDeps(sOpt dalec.SourceOpts, spec *dalec.Spec, targe
134134
opts := append(opts, dalec.ProgressGroup("Install build dependencies"))
135135
opts = append([]llb.ConstraintsOpt{dalec.WithConstraint(c)}, opts...)
136136

137-
srcPkg, err := deb.SourcePackage(ctx, sOpt, in, depsSpec, targetKey, "", opts...)
137+
srcPkg, err := deb.SourcePackage(ctx, sOpt, in, depsSpec, targetKey, "", deb.SourcePkgConfig{}, opts...)
138138
if err != nil {
139139
return in, err
140140
}

frontend/deb/distro/pkg.go

Lines changed: 84 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ func (d *Config) BuildDeb(ctx context.Context, worker llb.State, sOpt dalec.Sour
3030
}
3131

3232
worker = worker.With(d.InstallBuildDeps(sOpt, spec, targetKey))
33-
srcPkg, err := deb.SourcePackage(ctx, sOpt, worker.With(ensureGolang(client, spec, targetKey, opts...)), spec, targetKey, versionID, opts...)
33+
34+
var cfg deb.SourcePkgConfig
35+
extraPaths, err := prepareGo(ctx, client, &cfg, worker, spec, targetKey, opts...)
36+
if err != nil {
37+
return worker, err
38+
}
39+
40+
srcPkg, err := deb.SourcePackage(ctx, sOpt, worker.With(extraPaths), spec, targetKey, versionID, cfg, opts...)
3441
if err != nil {
3542
return worker, err
3643
}
@@ -44,60 +51,83 @@ func (d *Config) BuildDeb(ctx context.Context, worker llb.State, sOpt dalec.Sour
4451
return frontend.MaybeSign(ctx, client, st, spec, targetKey, sOpt)
4552
}
4653

47-
// ensureGolang is a work-around for the case where the base distro golang package
48-
// is too old, but other packages are provided (e.g. `golang-1.22`) and those
49-
// other packages don't actually add go tools to $PATH.
50-
// It assumes if you added one of these go packages and there is no `go` in $PATH
51-
// that you probably wanted to use that version of go.
52-
func ensureGolang(client gwclient.Client, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) llb.StateOption {
53-
return func(in llb.State) llb.State {
54-
deps := spec.GetBuildDeps(targetKey)
55-
if _, hasNormalGo := deps["golang"]; hasNormalGo {
56-
return in
54+
func noOpStateOpt(in llb.State) llb.State {
55+
return in
56+
}
57+
58+
func prepareGo(ctx context.Context, client gwclient.Client, cfg *deb.SourcePkgConfig, worker llb.State, spec *dalec.Spec, targetKey string, opts ...llb.ConstraintsOpt) (llb.StateOption, error) {
59+
goBin, err := searchForAltGolang(ctx, client, spec, targetKey, worker, opts...)
60+
if err != nil {
61+
return noOpStateOpt, errors.Wrap(err, "error while looking for alternate go bin path")
62+
}
63+
64+
if goBin == "" {
65+
return noOpStateOpt, nil
66+
}
67+
cfg.PrependPath = append(cfg.PrependPath, goBin)
68+
return addPaths([]string{goBin}, opts...), nil
69+
}
70+
71+
func searchForAltGolang(ctx context.Context, client gwclient.Client, spec *dalec.Spec, targetKey string, in llb.State, opts ...llb.ConstraintsOpt) (string, error) {
72+
if !spec.HasGomods() {
73+
return "", nil
74+
}
75+
var candidates []string
76+
77+
deps := spec.GetBuildDeps(targetKey)
78+
if _, hasNormalGo := deps["golang"]; hasNormalGo {
79+
return "", nil
80+
}
81+
82+
for dep := range deps {
83+
if strings.HasPrefix(dep, "golang-") {
84+
// Get the base version component
85+
_, ver, _ := strings.Cut(dep, "-")
86+
// Trim off any potential extra stuff like `golang-1.20-go` (ie the `-go` bit)
87+
// This is just for having definitive search paths to check it should
88+
// not be an issue if this is not like the above example and its
89+
// something else like `-doc` since we are still going to check the
90+
// binary exists anyway (plus this would be highly unlikely in any case).
91+
ver, _, _ = strings.Cut(ver, "-")
92+
candidates = append(candidates, "usr/lib/go-"+ver+"/bin")
5793
}
94+
}
5895

59-
return in.Async(func(ctx context.Context, in llb.State, c *llb.Constraints) (llb.State, error) {
60-
var candidates []string
61-
for dep := range deps {
62-
if strings.HasPrefix(dep, "golang-") {
63-
// Get the base version component
64-
_, ver, _ := strings.Cut(dep, "-")
65-
// Trim off any potential extra stuff like `golang-1.20-go` (ie the `-go` bit)
66-
// This is just for having definitive search paths to check it should
67-
// not be an issue if this is not like the above example and its
68-
// something else like `-doc` since we are still going to check the
69-
// binary exists anyway (plus this would be highly unlikely in any case).
70-
ver, _, _ = strings.Cut(ver, "-")
71-
candidates = append(candidates, "usr/lib/go-"+ver+"/bin")
72-
}
73-
}
96+
if len(candidates) == 0 {
97+
return "", nil
98+
}
7499

75-
if len(candidates) == 0 {
76-
return in, nil
77-
}
100+
stfs, err := bkfs.FromState(ctx, &in, client, opts...)
101+
if err != nil {
102+
return "", err
103+
}
78104

79-
opts := []llb.ConstraintsOpt{dalec.WithConstraint(c), dalec.WithConstraints(opts...)}
105+
for _, p := range candidates {
106+
_, err := fs.Stat(stfs, filepath.Join(p, "go"))
107+
if err == nil {
108+
// bkfs does not allow a leading `/` in the stat path per spec for [fs.FS]
109+
// Add that in here
110+
p := "/" + p
111+
return p, nil
112+
}
113+
}
80114

81-
pathVar, _, err := in.GetEnv(ctx, "PATH", opts...)
82-
if err != nil {
83-
return in, err
84-
}
115+
return "", nil
116+
}
85117

86-
stfs, err := bkfs.FromState(ctx, &in, client, opts...)
118+
// prepends the provided values to $PATH
119+
func addPaths(paths []string, opts ...llb.ConstraintsOpt) llb.StateOption {
120+
return func(in llb.State) llb.State {
121+
if len(paths) == 0 {
122+
return in
123+
}
124+
return in.Async(func(ctx context.Context, in llb.State, c *llb.Constraints) (llb.State, error) {
125+
opts := []llb.ConstraintsOpt{dalec.WithConstraint(c), dalec.WithConstraints(opts...)}
126+
pathEnv, _, err := in.GetEnv(ctx, "PATH", opts...)
87127
if err != nil {
88128
return in, err
89129
}
90-
91-
for _, p := range candidates {
92-
_, err := fs.Stat(stfs, filepath.Join(p, "go"))
93-
if err == nil {
94-
// bkfs does not allow a leading `/` in the stat path per spec for [fs.FS]
95-
// Add that in here
96-
p := "/" + p
97-
return in.AddEnv("PATH", p+":"+pathVar), nil
98-
}
99-
}
100-
return in, nil
130+
return in.AddEnv("PATH", strings.Join(append(paths, pathEnv), ":")), nil
101131
})
102132
}
103133
}
@@ -203,7 +233,14 @@ func (cfg *Config) HandleSourcePkg(ctx context.Context, client gwclient.Client)
203233
}
204234

205235
worker = worker.With(cfg.InstallBuildDeps(sOpt, spec, targetKey, pg))
206-
st, err := deb.SourcePackage(ctx, sOpt, worker.With(ensureGolang(client, spec, targetKey, pg)), spec, targetKey, versionID, pg)
236+
237+
var cfg deb.SourcePkgConfig
238+
extraPaths, err := prepareGo(ctx, client, &cfg, worker, spec, targetKey, pg)
239+
if err != nil {
240+
return nil, nil, err
241+
}
242+
243+
st, err := deb.SourcePackage(ctx, sOpt, worker.With(extraPaths), spec, targetKey, versionID, cfg, pg)
207244
if err != nil {
208245
return nil, nil, errors.Wrap(err, "error building source package")
209246
}

frontend/deb/pkg.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ func createPatches(spec *dalec.Spec, sources map[string]llb.State, worker llb.St
7171
return patches
7272
}
7373

74-
func SourcePackage(ctx context.Context, sOpt dalec.SourceOpts, worker llb.State, spec *dalec.Spec, targetKey, distroVersionID string, opts ...llb.ConstraintsOpt) (llb.State, error) {
74+
func SourcePackage(ctx context.Context, sOpt dalec.SourceOpts, worker llb.State, spec *dalec.Spec, targetKey, distroVersionID string, cfg SourcePkgConfig, opts ...llb.ConstraintsOpt) (llb.State, error) {
7575
if err := validateSpec(spec); err != nil {
7676
return llb.Scratch(), err
7777
}
78-
dr, err := Debroot(ctx, sOpt, spec, worker, llb.Scratch(), targetKey, "", distroVersionID)
78+
dr, err := Debroot(ctx, sOpt, spec, worker, llb.Scratch(), targetKey, "", distroVersionID, cfg, opts...)
7979
if err != nil {
8080
return llb.Scratch(), err
8181
}

0 commit comments

Comments
 (0)