dalec icon indicating copy to clipboard operation
dalec copied to clipboard

Support multiple specs

Open cpuguy83 opened this issue 1 year ago • 2 comments

Right now we can only build one spec at a time, which works well enough for simple cases but is not ideal for multi-component images as it requires multiple build requests.

Options

The yaml parser supports multiple documents in one file (since this is part of the yaml spec). Example:

name: my-package1
# other fields

---

name: my-package2
# other fields

Alternatively we could change the spec such that the spec file a list of specs instead of a single flat spec. Example:

specs:
  - name: my-package1
    # other fields
  - name: my-package2
    # other fields

The advantage of this approach is specs can share a single set of build arguments and potentially other things (via yaml anchors) whereas in the yaml multi-doc version these are completely separate documents that can't share anything (per yaml spec).

I think overall I prefer the simplicity of option 1, meanwhile if needed we could add support for option 2 later without breaking anything:

type Project struct {
  *Spec
  Specs []*Spec
}

Build UX

When multiple docs are included this would add an additional namespace to all build targets. As an example, today we have build targets like mariner2/rpm, mariner2/toolkitroot, mariner2/container. With multi-doc this would add the package name as a prefix to each target, e.g. my-package1/mariner2/rpm. All existing semantics regarding default targets would remain... e.g. mariner2/container is the default target when the user does not specify one, with multi-doc it a target of docker build --target=my-package1 would default to my-package1/mariner2/container.

When a user does not specify any target (docker build .) this would need to select the last package specified. This is similar to how the dockerfile frontend works (last build stage in the dockerfile is used as the default).

Critical to making this useful: the spec for a package should be able to list one of the other packages as a build or runtime dependency, which would trigger a build of that package.

Example:

name: my-package1
# other fields

---

name: my-package2
dependencies:
  runtime:
    my-package1:

When executed with docker build --target=my-package2/mariner2/container (or no --target since it is last and as such the default), this should produce a container with both my-package2 and my-package1 installed and my-package1 would be built as part of the build invocation rather than installed from some repository.

This allows us to have inter-package dependencies without needing to make them available in a repository first.

cpuguy83 avatar Dec 01 '23 00:12 cpuguy83