Bring F3 up to its release branch, with both sides' store tests (part 6 of 14)
mesh/merge-gate head 423b24d4ee8b merged into trunk fcdecb436117 — fail: head 423b24d4ee8b does not merge cleanly into trunk fcdecb436117: in conflict cmd/…
mesh/repo-check head 423b24d4ee8b merged into trunk fcdecb436117 — fail: head 423b24d4ee8b does not merge cleanly into trunk fcdecb436117: in conflict cmd/…
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate head 423b24d4ee8b merged into trunk fcdecb436117 — fail: head 423b24d4ee8b does not merge cleanly into trunk fcdecb436117: in conflict cmd/…
mesh/repo-check head 423b24d4ee8b merged into trunk fcdecb436117 — fail: head 423b24d4ee8b does not merge cleanly into trunk fcdecb436117: in conflict cmd/…
mesh/delivery superseded: a newer head of the same pull request
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/catalogue"
|
||||
)
|
||||
|
||||
// withTemplateTexts is what was built with each template's text, for a module whose files name templates
|
||||
// (novox/hq ADR 0349 §2): its templates archive gains the text of every file in it, read back from the packed
|
||||
// bytes, by its path inside the archive. Resolve reads them into the files' content.
|
||||
//
|
||||
// **Read from the archive, proven by its digest.** The module's files/ is packed again, as the build packs it,
|
||||
// and the bytes are read only when they hash to the digest the build made or took: a fresh build's own archive,
|
||||
// or a check's build a walk copied by digest without building (novox/hq ADR 0350 §2). Packing is deterministic,
|
||||
// so one source packs to one digest; a tree that packs to another is not the archive, and its text is refused.
|
||||
func withTemplateTexts(manifest catalogue.Manifest, within string, built []catalogue.Built) ([]catalogue.Built, error) {
|
||||
if !manifest.Templated() {
|
||||
return built, nil
|
||||
}
|
||||
name := catalogue.TemplatesArtifact(manifest)
|
||||
at := -1
|
||||
for i, b := range built {
|
||||
if b.Name == name {
|
||||
at = i
|
||||
}
|
||||
}
|
||||
if name == "" || at < 0 {
|
||||
return nil, fmt.Errorf("%s names templates and built no archive of %s/ to read them from",
|
||||
manifest.Module, catalogue.TemplatesDir)
|
||||
}
|
||||
body, err := pack(filepath.Join(within, catalogue.TemplatesDir))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: packing %s/ to read its templates back failed: %w", manifest.Module,
|
||||
catalogue.TemplatesDir, err)
|
||||
}
|
||||
sum := sha256.Sum256(body)
|
||||
digest := "sha256:" + hex.EncodeToString(sum[:])
|
||||
if built[at].Digest != digest {
|
||||
return nil, fmt.Errorf("%s: %s/ packs to the digest %s, and the archive %q built or taken is %s: the "+
|
||||
"templates are read only from the archive the mesh records", manifest.Module, catalogue.TemplatesDir,
|
||||
digest, name, orNone(built[at].Digest))
|
||||
}
|
||||
texts, err := unpackTexts(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: reading the archive %q back failed: %w", manifest.Module, name, err)
|
||||
}
|
||||
out := append([]catalogue.Built(nil), built...)
|
||||
out[at].Texts = texts
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// unpackTexts is every file of a packed archive (pack), by its path inside it.
|
||||
func unpackTexts(body []byte) (map[string]string, error) {
|
||||
zipped, err := gzip.NewReader(bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer zipped.Close()
|
||||
reader := tar.NewReader(zipped)
|
||||
texts := map[string]string{}
|
||||
for {
|
||||
header, err := reader.Next()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return texts, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if header.Typeflag != tar.TypeReg {
|
||||
continue
|
||||
}
|
||||
text, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
texts[header.Name] = string(text)
|
||||
}
|
||||
}
|
||||
|
||||
func orNone(digest string) string {
|
||||
if digest == "" {
|
||||
return "no digest"
|
||||
}
|
||||
return digest
|
||||
}
|
||||
Reference in New Issue
Block a user