113 lines
3.9 KiB
Go
113 lines
3.9 KiB
Go
package config
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
pipelineDefaultOwnershipSource = "default"
|
|
effectivePipelineDigestVersion = "narratio-effective-pipeline-v3\n"
|
|
)
|
|
|
|
// PipelineOverlayProvenance identifies the overlay selected while resolving a
|
|
// pipeline. It contains only non-secret selection metadata.
|
|
type PipelineOverlayProvenance struct {
|
|
Name string
|
|
Source string
|
|
OverlayPath string
|
|
}
|
|
|
|
// SelectedPipelineOverlay reports the overlay used to resolve cfg, if any.
|
|
func SelectedPipelineOverlay(cfg *PipelineConfig) (*PipelineOverlayProvenance, bool) {
|
|
if cfg == nil || cfg.resolution == nil || cfg.resolution.selectedOverlay == nil {
|
|
return nil, false
|
|
}
|
|
selection := cfg.resolution.selectedOverlay
|
|
return &PipelineOverlayProvenance{Name: selection.name, Source: selection.source, OverlayPath: selection.overlayPath}, true
|
|
}
|
|
|
|
// EffectivePipelineDigest reports the deterministic secret-free digest for a
|
|
// resolved pipeline.
|
|
func EffectivePipelineDigest(cfg *PipelineConfig) string {
|
|
if cfg == nil || cfg.resolution == nil {
|
|
return ""
|
|
}
|
|
return cfg.resolution.effectiveDigest
|
|
}
|
|
|
|
func finalizePipelineResolution(cfg *PipelineConfig) error {
|
|
if cfg == nil || cfg.resolution == nil {
|
|
return fmt.Errorf("pipeline resolution metadata is required")
|
|
}
|
|
declared := make(map[string][]string, len(cfg.resolution.ownership))
|
|
for _, ownership := range cfg.resolution.ownership {
|
|
declared[ownership.path] = append([]string(nil), ownership.sources...)
|
|
}
|
|
records, err := projectionValueRecords(projectEffectivePipeline(cfg, true))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ownership := make([]pipelineFieldOwnership, 0, len(records))
|
|
for _, record := range records {
|
|
sources := declared[record.Path]
|
|
if len(sources) == 0 {
|
|
sources = []string{pipelineDefaultOwnershipSource}
|
|
}
|
|
ownership = append(ownership, pipelineFieldOwnership{
|
|
path: record.Path, sources: append([]string(nil), sources...),
|
|
})
|
|
}
|
|
for path, sources := range declared {
|
|
if strings.HasPrefix(path, "scriptorium.artifact_families.") {
|
|
ownership = append(ownership, pipelineFieldOwnership{path: path, sources: append([]string(nil), sources...)})
|
|
}
|
|
}
|
|
sort.Slice(ownership, func(i, j int) bool { return ownership[i].path < ownership[j].path })
|
|
cfg.resolution.ownership = ownership
|
|
return recomputePipelineEffectiveDigest(cfg)
|
|
}
|
|
|
|
// recomputePipelineEffectiveDigest is the single package-owned hook for
|
|
// refreshing provenance after later resolution expands concrete pipeline
|
|
// values. Loader directives and runtime provenance are not serialized.
|
|
func recomputePipelineEffectiveDigest(cfg *PipelineConfig) error {
|
|
if cfg == nil || cfg.resolution == nil {
|
|
return fmt.Errorf("pipeline resolution metadata is required")
|
|
}
|
|
canonical, err := projectionJSON(projectEffectivePipeline(cfg, true))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
digestInput := append([]byte(effectivePipelineDigestVersion), canonical...)
|
|
digest := sha256.Sum256(digestInput)
|
|
cfg.resolution.effectiveDigest = hex.EncodeToString(digest[:])
|
|
return nil
|
|
}
|
|
|
|
// captureLogicalNotariusPaths retains normalized user-facing path semantics
|
|
// before runtime resolution makes relative paths depend on the checkout or
|
|
// installation directory. Runtime paths remain absolute; provenance does not.
|
|
func captureLogicalNotariusPaths(cfg *PipelineConfig) {
|
|
if cfg == nil || cfg.resolution == nil || cfg.Notarius == nil {
|
|
return
|
|
}
|
|
configPath := normalizeLogicalFilesystemPath(cfg.Notarius.ConfigPath)
|
|
workingDirectory := normalizeLogicalFilesystemPath(cfg.Notarius.WorkingDirectory)
|
|
cfg.resolution.logicalNotariusConfig = configPath
|
|
cfg.resolution.logicalNotariusWorking = workingDirectory
|
|
cfg.resolution.logicalNotariusCaptured = true
|
|
}
|
|
|
|
func normalizeLogicalFilesystemPath(value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
return filepath.ToSlash(filepath.Clean(value))
|
|
}
|