internal/pkg: write per-artifact logs
All checks were successful
Test / Create distribution (push) Successful in 59s
Test / Sandbox (push) Successful in 2m29s
Test / Hakurei (push) Successful in 3m36s
Test / ShareFS (push) Successful in 3m42s
Test / Sandbox (race detector) (push) Successful in 4m53s
Test / Hakurei (race detector) (push) Successful in 5m50s
Test / Flake checks (push) Successful in 1m28s
All checks were successful
Test / Create distribution (push) Successful in 59s
Test / Sandbox (push) Successful in 2m29s
Test / Hakurei (push) Successful in 3m36s
Test / ShareFS (push) Successful in 3m42s
Test / Sandbox (race detector) (push) Successful in 4m53s
Test / Hakurei (race detector) (push) Successful in 5m50s
Test / Flake checks (push) Successful in 1m28s
This is currently only used by execArtifact. A later patch will add additional logging facilities. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -28,15 +28,21 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container/check"
|
||||
"hakurei.app/internal/info"
|
||||
"hakurei.app/internal/lockedfile"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
|
||||
const (
|
||||
// programName is the string identifying this build system.
|
||||
programName = "internal/pkg"
|
||||
)
|
||||
|
||||
type (
|
||||
// A Checksum is a SHA-384 checksum computed for a cured [Artifact].
|
||||
Checksum = [sha512.Size384]byte
|
||||
|
||||
// An ID is a unique identifier returned by [Artifact.ID]. This value must
|
||||
// An ID is a unique identifier returned by [KnownIdent.ID]. This value must
|
||||
// be deterministically determined ahead of time.
|
||||
ID Checksum
|
||||
)
|
||||
@@ -81,20 +87,75 @@ type TContext struct {
|
||||
// Populated during [Cache.Cure].
|
||||
work, temp *check.Absolute
|
||||
|
||||
// Target [Artifact] encoded identifier.
|
||||
ids string
|
||||
// Pathname status was created at.
|
||||
statusPath *check.Absolute
|
||||
// File statusHeader and logs are written to.
|
||||
status *os.File
|
||||
// Error value during prepareStatus.
|
||||
statusErr error
|
||||
|
||||
common
|
||||
}
|
||||
|
||||
// statusHeader is the header written to all status files in dirStatus.
|
||||
var statusHeader = func() string {
|
||||
s := programName
|
||||
if v := info.Version(); v != info.FallbackVersion {
|
||||
s += " " + v
|
||||
}
|
||||
s += " (" + runtime.GOARCH + ")"
|
||||
if name, err := os.Hostname(); err == nil {
|
||||
s += " on " + name
|
||||
}
|
||||
s += "\n\n"
|
||||
return s
|
||||
}()
|
||||
|
||||
// prepareStatus initialises the status file once.
|
||||
func (t *TContext) prepareStatus() error {
|
||||
if t.statusPath != nil || t.status != nil {
|
||||
return t.statusErr
|
||||
}
|
||||
|
||||
t.statusPath = t.cache.base.Append(
|
||||
dirStatus,
|
||||
t.ids,
|
||||
)
|
||||
if t.status, t.statusErr = os.OpenFile(
|
||||
t.statusPath.String(),
|
||||
syscall.O_CREAT|syscall.O_EXCL|syscall.O_WRONLY,
|
||||
0400,
|
||||
); t.statusErr != nil {
|
||||
return t.statusErr
|
||||
}
|
||||
|
||||
_, t.statusErr = t.status.WriteString(statusHeader)
|
||||
return t.statusErr
|
||||
}
|
||||
|
||||
// GetStatusWriter returns a [io.Writer] for build logs. The caller must not
|
||||
// seek this writer before the position it was first returned in.
|
||||
func (t *TContext) GetStatusWriter() (io.Writer, error) {
|
||||
err := t.prepareStatus()
|
||||
return t.status, err
|
||||
}
|
||||
|
||||
// destroy destroys the temporary directory and joins its errors with the error
|
||||
// referred to by errP. If the error referred to by errP is non-nil, the work
|
||||
// directory is removed similarly. [Cache] is responsible for making sure work
|
||||
// is never left behind for a successful [Cache.Cure].
|
||||
//
|
||||
// If implementation had requested status, it is closed with error joined with
|
||||
// the error referred to by errP. If the error referred to by errP is non-nil,
|
||||
// the status file is removed from the filesystem.
|
||||
//
|
||||
// destroy must be deferred by [Cache.Cure] if [TContext] is passed to any Cure
|
||||
// implementation. It should not be called prior to that point.
|
||||
func (t *TContext) destroy(errP *error) {
|
||||
if chmodErr, removeErr := removeAll(t.temp); chmodErr != nil || removeErr != nil {
|
||||
*errP = errors.Join(*errP, chmodErr, removeErr)
|
||||
return
|
||||
}
|
||||
|
||||
if *errP != nil {
|
||||
@@ -106,6 +167,16 @@ func (t *TContext) destroy(errP *error) {
|
||||
*errP = nil
|
||||
}
|
||||
}
|
||||
|
||||
if t.status != nil {
|
||||
if err := t.status.Close(); err != nil {
|
||||
*errP = errors.Join(*errP, err)
|
||||
}
|
||||
if *errP != nil {
|
||||
*errP = errors.Join(*errP, os.Remove(t.statusPath.String()))
|
||||
}
|
||||
t.status = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying [context.Context].
|
||||
@@ -169,7 +240,7 @@ type FContext struct {
|
||||
}
|
||||
|
||||
// InvalidLookupError is the identifier of non-dependency [Artifact] looked up
|
||||
// via [FContext.Pathname] by a misbehaving [Artifact] implementation.
|
||||
// via [FContext.GetArtifact] by a misbehaving [Artifact] implementation.
|
||||
type InvalidLookupError ID
|
||||
|
||||
func (e InvalidLookupError) Error() string {
|
||||
@@ -375,6 +446,9 @@ const (
|
||||
// dirChecksum is the directory name appended to Cache.base for storing
|
||||
// artifacts named after their [Checksum].
|
||||
dirChecksum = "checksum"
|
||||
// dirStatus is the directory name appended to Cache.base for storing
|
||||
// artifact metadata and logs named after their [ID].
|
||||
dirStatus = "status"
|
||||
|
||||
// dirWork is the directory name appended to Cache.base for working
|
||||
// pathnames set up during [Cache.Cure].
|
||||
@@ -943,8 +1017,9 @@ func (c *Cache) openFile(f FileArtifact) (r io.ReadCloser, err error) {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return
|
||||
}
|
||||
id := c.Ident(f)
|
||||
if c.msg.IsVerbose() {
|
||||
rn := reportName(f, c.Ident(f))
|
||||
rn := reportName(f, id)
|
||||
c.msg.Verbosef("curing %s in memory...", rn)
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@@ -1527,6 +1602,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
t := TContext{
|
||||
c.base.Append(dirWork, ids),
|
||||
c.base.Append(dirTemp, ids),
|
||||
ids, nil, nil, nil,
|
||||
common{c},
|
||||
}
|
||||
switch ca := a.(type) {
|
||||
@@ -1714,6 +1790,7 @@ func open(
|
||||
for _, name := range []string{
|
||||
dirIdentifier,
|
||||
dirChecksum,
|
||||
dirStatus,
|
||||
dirWork,
|
||||
} {
|
||||
if err := os.MkdirAll(base.Append(name).String(), 0700); err != nil &&
|
||||
|
||||
Reference in New Issue
Block a user