package manifest import ( "github.com/osbuild/osbuild-composer/internal/osbuild" "github.com/osbuild/osbuild-composer/internal/rpmmd" "github.com/osbuild/osbuild-composer/internal/runner" ) // A Build represents the build environment for other pipelines. As a // general rule, tools required to build pipelines are used from the build // environment, rather than from the pipeline itself. Without a specified // build environment, the build host's root filesystem would be used, which // is not predictable nor reproducible. For the purposes of building the // build pipeline, we do use the build host's filesystem, this means we should // make minimal assumptions about what's available there. type Build struct { Base runner runner.Runner dependents []Pipeline repos []rpmmd.RepoConfig packageSpecs []rpmmd.PackageSpec } // NewBuild creates a new build pipeline from the repositories in repos // and the specified packages. func NewBuild(m *Manifest, runner runner.Runner, repos []rpmmd.RepoConfig) *Build { pipeline := &Build{ Base: NewBase(m, "build", nil), runner: runner, dependents: make([]Pipeline, 0), repos: repos, } m.addPipeline(pipeline) return pipeline } func (p *Build) addDependent(dep Pipeline) { p.dependents = append(p.dependents, dep) } func (p *Build) getPackageSetChain() []rpmmd.PackageSet { // TODO: make the /usr/bin/cp dependency conditional // TODO: make the /usr/bin/xz dependency conditional packages := []string{ "selinux-policy-targeted", // needed to build the build pipeline "coreutils", // /usr/bin/cp - used all over "xz", // usage unclear } packages = append(packages, p.runner.GetBuildPackages()...) for _, pipeline := range p.dependents { packages = append(packages, pipeline.getBuildPackages()...) } return []rpmmd.PackageSet{ { Include: packages, Repositories: p.repos, }, } } func (p *Build) getPackageSpecs() []rpmmd.PackageSpec { return p.packageSpecs } func (p *Build) serializeStart(packages []rpmmd.PackageSpec) { if len(p.packageSpecs) > 0 { panic("double call to serializeStart()") } p.packageSpecs = packages } func (p *Build) serializeEnd() { if len(p.packageSpecs) == 0 { panic("serializeEnd() call when serialization not in progress") } p.packageSpecs = nil } func (p *Build) serialize() osbuild.Pipeline { if len(p.packageSpecs) == 0 { panic("serialization not started") } pipeline := p.Base.serialize() pipeline.Runner = p.runner.String() pipeline.AddStage(osbuild.NewRPMStage(osbuild.NewRPMStageOptions(p.repos), osbuild.NewRpmStageSourceFilesInputs(p.packageSpecs))) pipeline.AddStage(osbuild.NewSELinuxStage(&osbuild.SELinuxStageOptions{ FileContexts: "etc/selinux/targeted/contexts/files/file_contexts", Labels: p.getSELinuxLabels(), }, )) return pipeline } // Returns a map of paths to labels for the SELinux stage based on specific // packages found in the pipeline. func (p *Build) getSELinuxLabels() map[string]string { labels := make(map[string]string) for _, pkg := range p.getPackageSpecs() { switch pkg.Name { case "coreutils": labels["/usr/bin/cp"] = "system_u:object_r:install_exec_t:s0" case "tar": labels["/usr/bin/tar"] = "system_u:object_r:install_exec_t:s0" } } return labels }