From e0c720681bbce364910df241658dc987851e579a Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 4 Feb 2026 14:41:18 +0900 Subject: internal/pkg: standardise artifact IR This should hopefully provide good separation between the artifact curing backend implementation and the (still work in progress) language. Making the IR parseable also guarantees uniqueness of the representation. Signed-off-by: Ophestra --- internal/pkg/exec.go | 140 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 27 deletions(-) (limited to 'internal/pkg/exec.go') diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index f5270801..39955637 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -103,8 +103,7 @@ type execArtifact struct { args []string // Duration the initial process is allowed to run. The zero value is - // equivalent to execTimeoutDefault. This value is never encoded in Params - // because it cannot affect outcome. + // equivalent to [ExecTimeoutDefault]. timeout time.Duration // Caller-supplied exclusivity value, returned as is by IsExclusive. @@ -129,12 +128,6 @@ func (a *execNetArtifact) Checksum() Checksum { return a.checksum } // Kind returns the hardcoded [Kind] constant. func (*execNetArtifact) Kind() Kind { return KindExecNet } -// Params is [Checksum] concatenated with [KindExec] params. -func (a *execNetArtifact) Params(ctx *IContext) { - ctx.GetHash().Write(a.checksum[:]) - a.execArtifact.Params(ctx) -} - // Cure cures the [Artifact] in the container described by the caller. The // container retains host networking. func (a *execNetArtifact) Cure(f *FContext) error { @@ -198,38 +191,131 @@ func (*execArtifact) Kind() Kind { return KindExec } // Params writes paths, executable pathname and args. func (a *execArtifact) Params(ctx *IContext) { - h := ctx.GetHash() + ctx.WriteString(a.name) - _0, _1 := []byte{0}, []byte{1} + ctx.WriteUint32(uint32(len(a.paths))) for _, p := range a.paths { - if p.W { - h.Write(_1) - } else { - h.Write(_0) - } if p.P != nil { - h.Write([]byte(p.P.String())) + ctx.WriteString(p.P.String()) } else { - h.Write([]byte("invalid P\x00")) + ctx.WriteString("invalid P\x00") } - h.Write(_0) + + ctx.WriteUint32(uint32(len(p.A))) for _, d := range p.A { ctx.WriteIdent(d) } - h.Write(_0) + + if p.W { + ctx.WriteUint32(1) + } else { + ctx.WriteUint32(0) + } } - h.Write(_0) - h.Write([]byte(a.dir.String())) - h.Write(_0) + + ctx.WriteString(a.dir.String()) + + ctx.WriteUint32(uint32(len(a.env))) for _, e := range a.env { - h.Write([]byte(e)) + ctx.WriteString(e) } - h.Write(_0) - h.Write([]byte(a.path.String())) - h.Write(_0) + + ctx.WriteString(a.path.String()) + + ctx.WriteUint32(uint32(len(a.args))) for _, arg := range a.args { - h.Write([]byte(arg)) + ctx.WriteString(arg) + } + + ctx.WriteUint32(uint32(a.timeout & 0xffffffff)) + ctx.WriteUint32(uint32(a.timeout >> 32)) + + if a.exclusive { + ctx.WriteUint32(1) + } else { + ctx.WriteUint32(0) + } +} + +// readExecArtifact interprets IR values and returns the address of execArtifact +// or execNetArtifact. +func readExecArtifact(r *IRReader, net bool) Artifact { + r.DiscardAll() + + name := r.ReadString() + + sz := r.ReadUint32() + if sz > irMaxDeps { + panic(ErrIRDepend) + } + paths := make([]ExecPath, sz) + for i := range paths { + paths[i].P = check.MustAbs(r.ReadString()) + + sz = r.ReadUint32() + if sz > irMaxDeps { + panic(ErrIRDepend) + } + paths[i].A = make([]Artifact, sz) + for j := range paths[i].A { + paths[i].A[j] = r.ReadIdent() + } + + paths[i].W = r.ReadUint32() != 0 + } + + dir := check.MustAbs(r.ReadString()) + + sz = r.ReadUint32() + if sz > irMaxValues { + panic(ErrIRValues) + } + env := make([]string, sz) + for i := range env { + env[i] = r.ReadString() + } + + pathname := check.MustAbs(r.ReadString()) + + sz = r.ReadUint32() + if sz > irMaxValues { + panic(ErrIRValues) + } + args := make([]string, sz) + for i := range args { + args[i] = r.ReadString() } + + timeout := time.Duration(r.ReadUint32()) + timeout |= time.Duration(r.ReadUint32()) << 32 + + exclusive := r.ReadUint32() != 0 + + checksum, ok := r.Finalise() + + var checksumP *Checksum + if net { + if !ok { + panic(ErrExpectedChecksum) + } + checksumVal := checksum.Value() + checksumP = &checksumVal + } else { + if ok { + panic(ErrUnexpectedChecksum) + } + } + + return NewExec( + name, checksumP, timeout, exclusive, dir, env, pathname, args, paths..., + ) +} + +func init() { + register(KindExec, + func(r *IRReader) Artifact { return readExecArtifact(r, false) }) + register(KindExecNet, + func(r *IRReader) Artifact { return readExecArtifact(r, true) }) } // Dependencies returns a slice of all artifacts collected from caller-supplied -- cgit v1.3.1