Say what a build was made from, and hand an image compiling Go only that (hq ADR 0267, issue 363)
A merge to the controller's repository moved the route proxy and the build seat's holder whatever it changed, because nothing said which files their builds read. A build of a trunk commit now says its build source per repository: a Go program's import closure, an archive's directory, an image's recipe and the package it names in the new 'compiles' field. That image is built from its build source alone, so a recipe reading past it fails by name, and its fingerprint is over what it was handed.
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
@@ -87,6 +88,23 @@ type Result struct {
|
||||
// pin the build. Two builds with one fingerprint are one build, whatever digests they made
|
||||
// (novox/hq issue 280).
|
||||
Source string
|
||||
|
||||
// Sources are what this build was made from, as files (novox/hq ADR 0267 rule 1): per repository, the
|
||||
// entries a changed file is tested against (SourceHolds). The module's own repository has an empty
|
||||
// Repository. Said only for a build of a commit on the trunk, and only for a repository whose every
|
||||
// artifact's build source is known — a Go program's import closure, an archive's directory, an image's
|
||||
// recipe and the package it compiles; for any other, nothing is said and the whole of what the build
|
||||
// sees stays its source, as before.
|
||||
Sources []BuildSource
|
||||
}
|
||||
|
||||
// BuildSource is the build source a build read in one repository (novox/hq ADR 0267).
|
||||
type BuildSource struct {
|
||||
// Repository and Ref are a context's, as the manifest names it; empty for the module's own.
|
||||
Repository string
|
||||
Ref string
|
||||
// Paths are the entries, relative to the repository's root (SourceHolds).
|
||||
Paths []string
|
||||
}
|
||||
|
||||
// GitCredential is the forge credential a clone may present when the server asks for one.
|
||||
@@ -206,6 +224,10 @@ func build(ctx context.Context, run Runner, publish Publisher,
|
||||
|
||||
// What it is made from, for its source fingerprint: the module's own tree first.
|
||||
src := newSourceInputs(manifest.Module)
|
||||
src.prefix = strings.Trim(filepath.ToSlash(filepath.Clean(path)), "/")
|
||||
if src.prefix == "." {
|
||||
src.prefix = ""
|
||||
}
|
||||
if src.tree, err = gitTree(ctx, run, tree, path); err != nil {
|
||||
src.notPinned("its tree could not be named: " + err.Error())
|
||||
}
|
||||
@@ -298,9 +320,22 @@ func build(ctx context.Context, run Runner, publish Publisher,
|
||||
if fingerprint == "" {
|
||||
say("source", "no source fingerprint: %s", orNoTree(src.unpinned))
|
||||
}
|
||||
// **Only a trunk build says its build source** (novox/hq ADR 0267): the planner maps the next merge onto
|
||||
// the build source of the trunk's last build, and a branch's closure is not the trunk's.
|
||||
var sources []BuildSource
|
||||
if trunk != "" && onTrunk {
|
||||
sources = src.buildSources()
|
||||
for _, s := range sources {
|
||||
where := "its own repository"
|
||||
if s.Repository != "" {
|
||||
where = s.Repository
|
||||
}
|
||||
say("source", "%d path(s) of %s", len(s.Paths), where)
|
||||
}
|
||||
}
|
||||
return Result{Manifest: resolved, Commit: commit, Built: built,
|
||||
Against: against(within, manifest, stoodOn), Read: readBy(manifest), Source: fingerprint,
|
||||
Trunk: trunk, OnTrunk: onTrunk, Branches: branches}, nil
|
||||
Trunk: trunk, OnTrunk: onTrunk, Branches: branches, Sources: sources}, nil
|
||||
}
|
||||
|
||||
// branchesHolding is every branch of a fresh clone's origin the commit is on, without `origin/`.
|
||||
@@ -648,15 +683,48 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
||||
} else if src != nil {
|
||||
src.contexts[a.Name] = t
|
||||
}
|
||||
// docker build accepts -f outside the context it is given; the recipe stays exactly
|
||||
// where it was read from and validated against, absolute so the working directory
|
||||
// switching to the cloned context does not change which file that is.
|
||||
buildDir = cloned
|
||||
}
|
||||
// docker build accepts -f outside the context it is given; the recipe stays exactly where it was
|
||||
// read from and validated against, absolute so a context elsewhere does not change which file
|
||||
// that is.
|
||||
if buildDir != tree || a.Compiles != "" {
|
||||
absRecipe, err := filepath.Abs(filepath.Join(tree, a.From))
|
||||
if err != nil {
|
||||
return catalogue.Built{}, fmt.Errorf("%s: %s's recipe: %w", module, a.Name, err)
|
||||
}
|
||||
recipePath = absRecipe
|
||||
buildDir = cloned
|
||||
}
|
||||
// **An image that compiles a Go program is handed its build source and nothing else** (novox/hq
|
||||
// ADR 0267 rules 1 and 3): the program's import closure, read from the context it is built in, so a
|
||||
// merge elsewhere in that repository is no change to it — and a recipe that copies a file outside
|
||||
// it fails here, naming the file, rather than building from something no merge is mapped onto.
|
||||
if a.Compiles != "" {
|
||||
paths, err := GoBuildSource(buildDir, a.Compiles)
|
||||
if err != nil {
|
||||
return catalogue.Built{}, fmt.Errorf("%s: %s compiles %s, whose build source cannot be read: %w",
|
||||
module, a.Name, a.Compiles, err)
|
||||
}
|
||||
narrowed := filepath.Join(workspace, "narrow-"+a.Name)
|
||||
sum, err := narrowTree(buildDir, narrowed, paths)
|
||||
if err != nil {
|
||||
return catalogue.Built{}, fmt.Errorf("%s: handing %s its build source: %w", module, a.Name, err)
|
||||
}
|
||||
say("image", "%s is handed its build source: %d path(s) of %s", a.Name, len(paths), a.Compiles)
|
||||
if a.Context != nil {
|
||||
src.contexts[a.Name] = sum
|
||||
src.readIn(*a.Context, paths)
|
||||
} else {
|
||||
src.ownHas(paths...)
|
||||
}
|
||||
buildDir = narrowed
|
||||
} else if a.Context != nil {
|
||||
src.readWhole(*a.Context)
|
||||
}
|
||||
if a.Compiles != "" || a.Context != nil {
|
||||
src.ownHas(a.From)
|
||||
} else {
|
||||
src.ownWhole()
|
||||
}
|
||||
invocation := append([]string{"build", "-f", recipePath, "-t", local}, args...)
|
||||
if a.Target != "" {
|
||||
@@ -708,6 +776,18 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
||||
if src != nil {
|
||||
src.toolchains[a.Name] = toolchainOf(chain, base)
|
||||
}
|
||||
// A Go program's build source is its import closure (novox/hq ADR 0267 rule 1). Not read, it is the
|
||||
// module's whole directory, as before — said, so the wider plan has a reason a person can find.
|
||||
if chain.Language == "go" {
|
||||
if paths, err := GoBuildSource(tree, a.From); err != nil {
|
||||
say("bundle", "%s's build source is its whole directory: %v", a.Name, err)
|
||||
src.ownWhole()
|
||||
} else {
|
||||
src.ownHas(paths...)
|
||||
}
|
||||
} else {
|
||||
src.ownWhole()
|
||||
}
|
||||
if chain.Language == "typescript" {
|
||||
if own, _ := ownDependencies(tree); len(own) > 0 {
|
||||
src.notPinned(a.Name + " resolves packages of its own at build time")
|
||||
@@ -754,6 +834,7 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
||||
// there is no Publisher call — the container itself publishes, with the credential the
|
||||
// build was handed.
|
||||
say("package", "building and publishing %s (%s)", a.Name, a.Language)
|
||||
src.ownWhole()
|
||||
src.notPinned(a.Name + " is a package, built from what the registry holds when it is built")
|
||||
reference, err := publishPackage(ctx, run, module, tree, a, npmrc, say)
|
||||
if err != nil {
|
||||
@@ -763,6 +844,7 @@ func one(ctx context.Context, run Runner, publish Publisher,
|
||||
return catalogue.Built{Name: a.Name, Kind: a.Kind, Reference: reference}, nil
|
||||
|
||||
case catalogue.ArtifactArchive:
|
||||
src.ownHas(strings.Trim(path.Clean("/"+filepath.ToSlash(a.From)), "/") + "/**")
|
||||
body, err := pack(filepath.Join(tree, a.From))
|
||||
if err != nil {
|
||||
return catalogue.Built{}, fmt.Errorf("%s: packing %s failed: %w", module, a.Name, err)
|
||||
|
||||
Reference in New Issue
Block a user