ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m19s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 23m57s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: failure
GitHub / darwin arm64 native GitHub-hosted native CI: failure
GitHub / darwin amd64 native GitHub-hosted native CI: failure
GitHub / linux amd64 native GitHub-hosted native CI: success
101 lines
3.9 KiB
Go
101 lines
3.9 KiB
Go
package pipeline
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
|
|
"aego/core/errs"
|
|
"aego/core/log"
|
|
"aego/project"
|
|
)
|
|
|
|
// resolveMetadata runs under the import lock; importers cannot write metadata or mutate the record.
|
|
// Stable references are persisted before publishing artifacts that use them.
|
|
func (p *Pipeline) resolveMetadata(r *record, ctx *ImportContext) error {
|
|
resolver, ok := r.importer.(MetadataResolver)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
copyMeta := *r.meta
|
|
copyMeta.Settings = r.meta.Settings.Clone()
|
|
copyMeta.Subs = append([]SubAsset(nil), r.meta.Subs...)
|
|
copyCtx := *ctx
|
|
copyCtx.Meta = ©Meta
|
|
settings, err := resolver.ResolveMetadata(©Ctx)
|
|
if err != nil || settings == nil {
|
|
return err
|
|
}
|
|
if bytes.Equal(canonicalSettings(settings), canonicalSettings(r.meta.Settings)) {
|
|
return nil
|
|
}
|
|
if p.sourceTransactionReadOnlyMetas[r.path+MetaSuffix] {
|
|
return errs.New(errs.AssetSourceUnstable, "metadata needs changes but is a read-only source guard", log.F("path", r.path))
|
|
}
|
|
if p.shared || p.mode == MetaReadOnly {
|
|
return errs.New(errs.AssetBadMeta, "automatic asset references need a writable metadata owner", log.F("path", r.path))
|
|
}
|
|
transactionPath := r.path + MetaSuffix
|
|
if p.sourceTransactionMetas != nil {
|
|
if _, guarded := p.sourceTransactionMetas[transactionPath]; !guarded {
|
|
// Dependencies and cache-salted sources are also reimported during
|
|
// ApplySources. Their sidecars are not automatically part of the
|
|
// caller's history or rollback: never silently normalize one here.
|
|
return errs.New(errs.AssetBadMeta, "dependent metadata needs an explicit source transaction guard before references can be resolved", log.F("path", transactionPath))
|
|
}
|
|
}
|
|
path := p.abs(r.path) + MetaSuffix
|
|
before, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return errs.Wrap(err, errs.AssetBadMeta, "cannot read metadata before resolving references")
|
|
}
|
|
if expected, guarded := p.sourceTransactionMetas[transactionPath]; guarded && !sameSource(before, expected) {
|
|
return errs.New(errs.AssetSourceUnstable, "metadata changed during source transaction; resolve before retrying", log.F("path", r.path))
|
|
}
|
|
disk, err := ParseMeta(before, r.path+MetaSuffix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if disk.Importer == "" {
|
|
disk.Importer = r.importer.ID()
|
|
}
|
|
for k, v := range r.importer.Defaults() {
|
|
if _, exists := disk.Settings[k]; !exists {
|
|
disk.Settings[k] = v
|
|
}
|
|
}
|
|
if !bytes.Equal(WriteMeta(disk), WriteMeta(r.meta)) {
|
|
return errs.New(errs.AssetSourceUnstable, "metadata changed during reference resolution; rescan before retrying", log.F("path", r.path))
|
|
}
|
|
copyMeta.Settings = settings.Clone()
|
|
after, err := os.ReadFile(path)
|
|
if err != nil || !bytes.Equal(before, after) {
|
|
return errs.New(errs.AssetSourceUnstable, "metadata is still being written", log.F("path", r.path))
|
|
}
|
|
published := WriteMeta(©Meta)
|
|
if p.sourceJournal != nil {
|
|
checked, err := p.sourceBytes(p.sourceJournalRoot, transactionPath)
|
|
if err != nil || !sameSource(checked, before) {
|
|
return errs.New(errs.AssetSourceUnstable, "metadata changed before reference publication", log.F("path", r.path))
|
|
}
|
|
if err := p.sourceJournal.authorize(transactionPath, published); err != nil {
|
|
return errs.Wrap(err, errs.VFSIO, "cannot protect resolved asset references")
|
|
}
|
|
if err := replaceSource(p.sourceJournalRoot, transactionPath, published); err != nil {
|
|
return errs.Wrap(err, errs.VFSIO, "cannot persist resolved asset references")
|
|
}
|
|
} else if err := project.WriteAtomic(path, published); err != nil {
|
|
return errs.Wrap(err, errs.VFSIO, "cannot persist resolved asset references")
|
|
}
|
|
if _, guarded := p.sourceTransactionMetas[transactionPath]; guarded {
|
|
p.sourceTransactionMetas[transactionPath] = published
|
|
}
|
|
if p.sourceJournal != nil && p.sourceTransactionCheckpoint != nil {
|
|
p.sourceTransactionCheckpoint("metadata", transactionPath)
|
|
}
|
|
r.meta = ©Meta
|
|
ctx.Meta = r.meta
|
|
p.metas[r.path+MetaSuffix] = true
|
|
p.rev++
|
|
return nil
|
|
}
|