internal/pkg: pass cure context as single value
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m30s
Test / ShareFS (push) Successful in 3m45s
Test / Hpkg (push) Successful in 4m32s
Test / Sandbox (race detector) (push) Successful in 5m3s
Test / Hakurei (race detector) (push) Successful in 5m55s
Test / Hakurei (push) Successful in 2m40s
Test / Flake checks (push) Successful in 1m52s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m30s
Test / ShareFS (push) Successful in 3m45s
Test / Hpkg (push) Successful in 4m32s
Test / Sandbox (race detector) (push) Successful in 5m3s
Test / Hakurei (race detector) (push) Successful in 5m55s
Test / Hakurei (push) Successful in 2m40s
Test / Flake checks (push) Successful in 1m52s
This cleans up the function signature and makes backwards compatible API changes possible. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -7,8 +7,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/container/check"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// An httpArtifact is an [Artifact] backed by a [http] url string. The method is
|
// An httpArtifact is an [Artifact] backed by a [http] url string. The method is
|
||||||
@@ -101,7 +99,7 @@ func (a *httpArtifact) do() (data []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cure returns syscall.ENOTSUP. Callers should use Data instead.
|
// Cure returns syscall.ENOTSUP. Callers should use Data instead.
|
||||||
func (a *httpArtifact) Cure(*check.Absolute, *check.Absolute, CacheDataFunc) error {
|
func (a *httpArtifact) Cure(*CureContext) error {
|
||||||
return syscall.ENOTSUP
|
return syscall.ENOTSUP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,36 @@ func MustDecode(s string) Checksum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CacheDataFunc tries to load [File] from [Cache], and if that fails, obtains
|
// CureContext is passed to [Artifact.Cure] and contains information and methods
|
||||||
// it via [File.Data] instead.
|
// useful for curing the [Artifact], like requesting the data of [File], or that
|
||||||
type CacheDataFunc func(f File) (data []byte, err error)
|
// other artifacts be cured.
|
||||||
|
//
|
||||||
|
// Methods of CureContext are safe for concurrent use. CureContext is valid
|
||||||
|
// until [Artifact.Cure] returns.
|
||||||
|
type CureContext struct {
|
||||||
|
// Address of underlying [Cache], should be zeroed after [Artifact.Cure]
|
||||||
|
// returns and must not be exposed directly.
|
||||||
|
cache *Cache
|
||||||
|
|
||||||
|
// Populated during [Cache.Cure].
|
||||||
|
work, temp *check.Absolute
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkDir returns a pathname to a directory which [Artifact] is expected to
|
||||||
|
// write its output to. This is not the final resting place of the [Artifact]
|
||||||
|
// and this pathname should not be directly referred to in the final contents.
|
||||||
|
func (c *CureContext) GetWorkDir() *check.Absolute { return c.work }
|
||||||
|
|
||||||
|
// GetTempDir returns a pathname which implementations may use as scratch space.
|
||||||
|
// A directory is not created automatically, implementations are expected to
|
||||||
|
// create it if they wish to use it, using [os.MkdirAll].
|
||||||
|
func (c *CureContext) GetTempDir() *check.Absolute { return c.temp }
|
||||||
|
|
||||||
|
// LoadData tries to load [File] from [Cache], and if that fails, obtains it via
|
||||||
|
// [File.Data] instead. Notably, it does not cure [File].
|
||||||
|
func (c *CureContext) LoadData(f File) (data []byte, err error) {
|
||||||
|
return c.cache.loadData(f)
|
||||||
|
}
|
||||||
|
|
||||||
// An Artifact is a read-only reference to a piece of data that may be created
|
// An Artifact is a read-only reference to a piece of data that may be created
|
||||||
// deterministically but might not currently be available in memory or on the
|
// deterministically but might not currently be available in memory or on the
|
||||||
@@ -83,18 +110,15 @@ type Artifact interface {
|
|||||||
// Result must remain identical across multiple invocations.
|
// Result must remain identical across multiple invocations.
|
||||||
Dependencies() []Artifact
|
Dependencies() []Artifact
|
||||||
|
|
||||||
// Cure cures the current [Artifact] to the caller-specified temporary
|
// Cure cures the current [Artifact] to the working directory obtained via
|
||||||
// pathname. This is not the final resting place of the [Artifact] and this
|
// [CureContext.GetWorkDir].
|
||||||
// pathname should not be directly referred to in the final contents.
|
|
||||||
//
|
//
|
||||||
// If the implementation produces a single file, it must implement [File]
|
// If the implementation produces a single file, it must implement [File]
|
||||||
// as well. In that case, Cure must produce a single regular file with
|
// as well. In that case, Cure must produce a single regular file with
|
||||||
// contents identical to that returned by [File.Data].
|
// contents identical to that returned by [File.Data].
|
||||||
//
|
//
|
||||||
// Implementations may use temp as scratch space. The caller is not required
|
// Implementations must not retain c.
|
||||||
// to create a directory here, implementations are expected to create it if
|
Cure(c *CureContext) (err error)
|
||||||
// they wish to use it, using [os.MkdirAll].
|
|
||||||
Cure(work, temp *check.Absolute, loadData CacheDataFunc) (err error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KnownIdent is optionally implemented by [Artifact] and is used instead of
|
// KnownIdent is optionally implemented by [Artifact] and is used instead of
|
||||||
@@ -515,7 +539,7 @@ func (c *Cache) finaliseIdent(
|
|||||||
close(done)
|
close(done)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadData provides [CacheDataFunc] for [Artifact.Cure].
|
// loadData provides [CureContext.LoadData] for [Artifact.Cure].
|
||||||
func (c *Cache) loadData(f File) (data []byte, err error) {
|
func (c *Cache) loadData(f File) (data []byte, err error) {
|
||||||
var r *os.File
|
var r *os.File
|
||||||
if kc, ok := f.(KnownChecksum); ok {
|
if kc, ok := f.(KnownChecksum); ok {
|
||||||
@@ -790,10 +814,14 @@ func (c *Cache) Cure(a Artifact) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
workPathname := c.base.Append(dirWork, ids)
|
cc := CureContext{
|
||||||
|
cache: c,
|
||||||
|
work: c.base.Append(dirWork, ids),
|
||||||
|
temp: c.base.Append(dirTemp, ids),
|
||||||
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
chmodErr, removeErr := removeAll(workPathname)
|
chmodErr, removeErr := removeAll(cc.work)
|
||||||
if chmodErr != nil || removeErr != nil {
|
if chmodErr != nil || removeErr != nil {
|
||||||
err = errors.Join(err, chmodErr, removeErr)
|
err = errors.Join(err, chmodErr, removeErr)
|
||||||
} else if errors.Is(err, os.ErrExist) {
|
} else if errors.Is(err, os.ErrExist) {
|
||||||
@@ -803,17 +831,17 @@ func (c *Cache) Cure(a Artifact) (
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
tempPathname := c.base.Append(dirTemp, ids)
|
if err = a.Cure(&cc); err != nil {
|
||||||
if err = a.Cure(workPathname, tempPathname, c.loadData); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if chmodErr, removeErr := removeAll(tempPathname); chmodErr != nil || removeErr != nil {
|
cc.cache = nil
|
||||||
|
if chmodErr, removeErr := removeAll(cc.temp); chmodErr != nil || removeErr != nil {
|
||||||
err = errors.Join(err, chmodErr, removeErr)
|
err = errors.Join(err, chmodErr, removeErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi os.FileInfo
|
var fi os.FileInfo
|
||||||
if fi, err = os.Lstat(workPathname.String()); err != nil {
|
if fi, err = os.Lstat(cc.work.String()); err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
err = NoOutputError{}
|
err = NoOutputError{}
|
||||||
}
|
}
|
||||||
@@ -831,7 +859,7 @@ func (c *Cache) Cure(a Artifact) (
|
|||||||
|
|
||||||
var gotChecksum Checksum
|
var gotChecksum Checksum
|
||||||
if gotChecksum, err = HashFS(
|
if gotChecksum, err = HashFS(
|
||||||
dotOverrideFS{os.DirFS(workPathname.String()).(dirFS)},
|
dotOverrideFS{os.DirFS(cc.work.String()).(dirFS)},
|
||||||
".",
|
".",
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return
|
return
|
||||||
@@ -854,12 +882,12 @@ func (c *Cache) Cure(a Artifact) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.Chmod(workPathname.String(), 0700); err != nil {
|
if err = os.Chmod(cc.work.String(), 0700); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.checksumMu.Lock()
|
c.checksumMu.Lock()
|
||||||
if err = os.Rename(
|
if err = os.Rename(
|
||||||
workPathname.String(),
|
cc.work.String(),
|
||||||
checksumPathname.String(),
|
checksumPathname.String(),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
if !errors.Is(err, os.ErrExist) {
|
if !errors.Is(err, os.ErrExist) {
|
||||||
|
|||||||
@@ -73,19 +73,13 @@ type stubArtifact struct {
|
|||||||
params []byte
|
params []byte
|
||||||
deps []pkg.Artifact
|
deps []pkg.Artifact
|
||||||
|
|
||||||
cure func(work, temp *check.Absolute, loadData pkg.CacheDataFunc) error
|
cure func(c *pkg.CureContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a stubArtifact) Kind() pkg.Kind { return a.kind }
|
func (a stubArtifact) Kind() pkg.Kind { return a.kind }
|
||||||
func (a stubArtifact) Params() []byte { return a.params }
|
func (a stubArtifact) Params() []byte { return a.params }
|
||||||
func (a stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
|
func (a stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
|
||||||
|
func (a stubArtifact) Cure(c *pkg.CureContext) error { return a.cure(c) }
|
||||||
func (a stubArtifact) Cure(
|
|
||||||
work, temp *check.Absolute,
|
|
||||||
loadData pkg.CacheDataFunc,
|
|
||||||
) error {
|
|
||||||
return a.cure(work, temp, loadData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A stubFile implements [File] with hardcoded behaviour.
|
// A stubFile implements [File] with hardcoded behaviour.
|
||||||
type stubFile struct {
|
type stubFile struct {
|
||||||
@@ -109,7 +103,7 @@ func newStubFile(
|
|||||||
kind,
|
kind,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
func(*check.Absolute, *check.Absolute, pkg.CacheDataFunc) error {
|
func(*pkg.CureContext) error {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
},
|
},
|
||||||
}}}
|
}}}
|
||||||
@@ -410,7 +404,8 @@ func TestCache(t *testing.T) {
|
|||||||
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
||||||
overrideIdent{testdataChecksum, stubArtifact{}},
|
overrideIdent{testdataChecksum, stubArtifact{}},
|
||||||
)
|
)
|
||||||
makeSample := func(work, _ *check.Absolute, _ pkg.CacheDataFunc) error {
|
makeSample := func(c *pkg.CureContext) error {
|
||||||
|
work := c.GetWorkDir()
|
||||||
if err := os.Mkdir(work.String(), 0700); err != nil {
|
if err := os.Mkdir(work.String(), 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -511,15 +506,15 @@ func TestCache(t *testing.T) {
|
|||||||
|
|
||||||
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, stubArtifact{
|
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, _ pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return makeGarbage(work, stub.UniqueError(0xcafe))
|
return makeGarbage(c.GetWorkDir(), stub.UniqueError(0xcafe))
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
}}, nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||||
|
|
||||||
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, stubArtifact{
|
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, _ pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return makeGarbage(work, nil)
|
return makeGarbage(c.GetWorkDir(), nil)
|
||||||
},
|
},
|
||||||
}}}, nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
}}}, nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
||||||
Got: pkg.MustDecode(
|
Got: pkg.MustDecode(
|
||||||
@@ -538,8 +533,8 @@ func TestCache(t *testing.T) {
|
|||||||
|
|
||||||
{"loadData directory", overrideIdent{pkg.ID{0xff, 3}, stubArtifact{
|
{"loadData directory", overrideIdent{pkg.ID{0xff, 3}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
_, err := loadData(overrideChecksumFile{checksum: wantChecksum})
|
_, err := c.LoadData(overrideChecksumFile{checksum: wantChecksum})
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, &os.PathError{
|
}}, nil, pkg.Checksum{}, &os.PathError{
|
||||||
@@ -553,22 +548,25 @@ func TestCache(t *testing.T) {
|
|||||||
|
|
||||||
{"no output", overrideIdent{pkg.ID{0xff, 4}, stubArtifact{
|
{"no output", overrideIdent{pkg.ID{0xff, 4}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, pkg.NoOutputError{}},
|
}}, nil, pkg.Checksum{}, pkg.NoOutputError{}},
|
||||||
|
|
||||||
{"file output", overrideIdent{pkg.ID{0xff, 5}, stubArtifact{
|
{"file output", overrideIdent{pkg.ID{0xff, 5}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return os.WriteFile(work.String(), []byte{0}, 0400)
|
return os.WriteFile(c.GetWorkDir().String(), []byte{0}, 0400)
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
||||||
|
|
||||||
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, stubArtifact{
|
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return os.Symlink(work.String(), work.String())
|
return os.Symlink(
|
||||||
|
c.GetWorkDir().String(),
|
||||||
|
c.GetWorkDir().String(),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||||
fs.ModeSymlink | 0777,
|
fs.ModeSymlink | 0777,
|
||||||
@@ -584,7 +582,7 @@ func TestCache(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
close(ready)
|
close(ready)
|
||||||
<-n
|
<-n
|
||||||
return wantErr
|
return wantErr
|
||||||
@@ -616,10 +614,16 @@ func TestCache(t *testing.T) {
|
|||||||
|
|
||||||
{"file output", overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
{"file output", overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
||||||
kind: pkg.KindTar,
|
kind: pkg.KindTar,
|
||||||
cure: func(work, _ *check.Absolute, loadData pkg.CacheDataFunc) error {
|
cure: func(c *pkg.CureContext) error {
|
||||||
return os.WriteFile(work.String(), []byte{0}, 0400)
|
return os.WriteFile(
|
||||||
|
c.GetWorkDir().String(),
|
||||||
|
[]byte{0},
|
||||||
|
0400,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
}}, nil, pkg.Checksum{}, errors.New(
|
||||||
|
"non-file artifact produced regular file",
|
||||||
|
)},
|
||||||
})
|
})
|
||||||
|
|
||||||
wantErrScrub := &pkg.ScrubError{
|
wantErrScrub := &pkg.ScrubError{
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ func (e DisallowedTypeflagError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cure cures the [Artifact], producing a directory located at work.
|
// Cure cures the [Artifact], producing a directory located at work.
|
||||||
func (a *tarArtifact) Cure(work, temp *check.Absolute, loadData CacheDataFunc) (err error) {
|
func (a *tarArtifact) Cure(c *CureContext) (err error) {
|
||||||
|
temp := c.GetTempDir()
|
||||||
var tr io.ReadCloser
|
var tr io.ReadCloser
|
||||||
|
|
||||||
{
|
{
|
||||||
var data []byte
|
var data []byte
|
||||||
data, err = loadData(a.f)
|
data, err = c.LoadData(a.f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -209,9 +210,9 @@ func (a *tarArtifact) Cure(work, temp *check.Absolute, loadData CacheDataFunc) (
|
|||||||
if err = os.Chmod(p.String(), 0700); err != nil {
|
if err = os.Chmod(p.String(), 0700); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = os.Rename(p.String(), work.String())
|
err = os.Rename(p.String(), c.GetWorkDir().String())
|
||||||
} else {
|
} else {
|
||||||
err = os.Rename(temp.String(), work.String())
|
err = os.Rename(temp.String(), c.GetWorkDir().String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user