Add deterministic analysis input identities
This commit is contained in:
@@ -44,21 +44,6 @@ type analyzeExecutionContext struct {
|
||||
Catalog *artifacts.ArtifactCatalog
|
||||
}
|
||||
|
||||
type analyzeInputResolutionState uint8
|
||||
|
||||
const (
|
||||
analyzeInputPresent analyzeInputResolutionState = iota
|
||||
analyzeInputAbsent
|
||||
analyzeInputError
|
||||
)
|
||||
|
||||
type analyzeInputResolution struct {
|
||||
State analyzeInputResolutionState
|
||||
Path string
|
||||
Artifact *artifacts.ResolvedSessionArtifact
|
||||
Err error
|
||||
}
|
||||
|
||||
func (analyzeStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*StageResult, error) {
|
||||
if env == nil || env.Config == nil {
|
||||
return nil, fmt.Errorf("analyze: stage environment config is required")
|
||||
@@ -335,34 +320,25 @@ func executeAnalyzeArtifact(
|
||||
artifactName := plan.Name
|
||||
artifactCfg := plan.Cfg
|
||||
|
||||
inputPaths := map[string]string{}
|
||||
resolvedInputs, err := resolveAnalyzeInputIdentities(artifactCfg.Inputs, execution)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("analyze: resolve inputs for artifact %q: %w", artifactName, err)
|
||||
}
|
||||
inputPaths := resolvedInputs.Paths()
|
||||
omittedOptionalInputs := []string{}
|
||||
reusedArtifacts := []map[string]any{}
|
||||
|
||||
inputNames := sortedScriptoriumInputNames(artifactCfg.Inputs)
|
||||
for _, inputName := range inputNames {
|
||||
inputCfg := artifactCfg.Inputs[inputName]
|
||||
resolution := resolveScriptoriumInput(inputCfg, execution)
|
||||
switch resolution.State {
|
||||
case analyzeInputError:
|
||||
return nil, fmt.Errorf("analyze: resolve input %q for artifact %q: %w", inputName, artifactName, resolution.Err)
|
||||
case analyzeInputAbsent:
|
||||
if inputCfg.Required {
|
||||
return nil, fmt.Errorf("analyze: required input %q for artifact %q could not be resolved", inputName, artifactName)
|
||||
}
|
||||
omittedOptionalInputs = append(omittedOptionalInputs, inputName)
|
||||
for _, identity := range resolvedInputs.Ordered {
|
||||
if !identity.Present {
|
||||
omittedOptionalInputs = append(omittedOptionalInputs, identity.Name)
|
||||
continue
|
||||
case analyzeInputPresent:
|
||||
inputPaths[inputName] = resolution.Path
|
||||
default:
|
||||
return nil, fmt.Errorf("analyze: resolve input %q for artifact %q: invalid resolution state", inputName, artifactName)
|
||||
}
|
||||
if resolution.Artifact != nil && resolution.Artifact.Provenance == artifacts.ArtifactProvenanceCurrentAnalyzeManifest {
|
||||
resolvedArtifact := resolvedInputs.Artifact(identity.Name)
|
||||
if resolvedArtifact != nil && resolvedArtifact.Provenance == artifacts.ArtifactProvenanceCurrentAnalyzeManifest {
|
||||
reusedArtifacts = append(reusedArtifacts, map[string]any{
|
||||
"name": configuredArtifactNameFromSourceID(resolution.Artifact.ID),
|
||||
"source_id": resolution.Artifact.ID,
|
||||
"path": resolution.Artifact.Path,
|
||||
"provenance": resolution.Artifact.Provenance,
|
||||
"name": configuredArtifactNameFromSourceID(resolvedArtifact.ID),
|
||||
"source_id": resolvedArtifact.ID,
|
||||
"path": resolvedArtifact.Path,
|
||||
"provenance": resolvedArtifact.Provenance,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -620,92 +596,6 @@ func discoverAnalyzeArtifactRef(m *manifest.Manifest, paths artifacts.SessionPat
|
||||
return resolved.Path, resolved.Provenance
|
||||
}
|
||||
|
||||
func resolveScriptoriumInput(inputCfg config.ScriptoriumInputConfig, execution analyzeExecutionContext) analyzeInputResolution {
|
||||
source := strings.TrimSpace(inputCfg.Source)
|
||||
descriptor, describeErr := artifactpolicy.DescribeScriptoriumInputSource(source)
|
||||
if describeErr != nil {
|
||||
return analyzeInputFailure(describeErr)
|
||||
}
|
||||
if descriptor.Source.Kind == artifactpolicy.SourceKindStableInput {
|
||||
identity, err := artifacts.ResolvePreparedInput(execution.Paths, execution.Manifest, descriptor.Source.ID)
|
||||
if err == nil {
|
||||
return analyzeInputFound(identity.Path, nil)
|
||||
}
|
||||
if errors.Is(err, artifacts.ErrPreparedInputAbsent) {
|
||||
if inputCfg.Required {
|
||||
return analyzeInputFailure(fmt.Errorf(
|
||||
"required prepared input source %q is unavailable; run narratio run-stage prepare %s --force",
|
||||
descriptor.Source.ID,
|
||||
execution.SessionID,
|
||||
))
|
||||
}
|
||||
return analyzeInputMissing()
|
||||
}
|
||||
return analyzeInputFailure(fmt.Errorf(
|
||||
"prepared input source %q is invalid; run narratio run-stage prepare %s --force: %w",
|
||||
descriptor.Source.ID,
|
||||
execution.SessionID,
|
||||
err,
|
||||
))
|
||||
}
|
||||
if descriptor.Source.Kind == artifactpolicy.SourceKindPreviousArtifact {
|
||||
resolved, err := artifacts.ResolvePreviousSessionArtifactWithCatalog(execution.Paths, execution.Manifest, source, execution.Catalog)
|
||||
if err == nil {
|
||||
copy := resolved
|
||||
return analyzeInputFound(resolved.Path, ©)
|
||||
}
|
||||
if errors.Is(err, artifacts.ErrSessionArtifactNotFound) {
|
||||
if inputCfg.Required {
|
||||
return analyzeInputFailure(fmt.Errorf(
|
||||
"required previous-session input source %q is unavailable; run narratio run-stage prepare %s --force",
|
||||
source,
|
||||
execution.SessionID,
|
||||
))
|
||||
}
|
||||
return analyzeInputMissing()
|
||||
}
|
||||
return analyzeInputFailure(err)
|
||||
}
|
||||
|
||||
resolved, err := artifacts.ResolveSessionArtifactWithCatalog(execution.Paths, execution.Manifest, source, execution.Catalog)
|
||||
if err == nil {
|
||||
copy := resolved
|
||||
return analyzeInputFound(resolved.Path, ©)
|
||||
}
|
||||
if !errors.Is(err, artifacts.ErrSessionArtifactNotFound) {
|
||||
return analyzeInputFailure(err)
|
||||
}
|
||||
if !inputCfg.Required {
|
||||
return analyzeInputMissing()
|
||||
}
|
||||
|
||||
switch descriptor.Source.Kind {
|
||||
case artifactpolicy.SourceKindExtraction:
|
||||
return analyzeInputFailure(fmt.Errorf(
|
||||
"required extraction source %q is unavailable; enable and configure pipeline.notarius output %q, then run narratio run-stage extract %s --force",
|
||||
source,
|
||||
descriptor.Source.ConfiguredKey,
|
||||
execution.SessionID,
|
||||
))
|
||||
case artifactpolicy.SourceKindConfiguredArtifact:
|
||||
return analyzeInputFailure(fmt.Errorf("configured artifact source %q is unavailable", source))
|
||||
default:
|
||||
return analyzeInputFailure(requiredBuiltInInputError(descriptor.Source.ID, execution))
|
||||
}
|
||||
}
|
||||
|
||||
func analyzeInputFound(path string, artifact *artifacts.ResolvedSessionArtifact) analyzeInputResolution {
|
||||
return analyzeInputResolution{State: analyzeInputPresent, Path: path, Artifact: artifact}
|
||||
}
|
||||
|
||||
func analyzeInputMissing() analyzeInputResolution {
|
||||
return analyzeInputResolution{State: analyzeInputAbsent}
|
||||
}
|
||||
|
||||
func analyzeInputFailure(err error) analyzeInputResolution {
|
||||
return analyzeInputResolution{State: analyzeInputError, Err: err}
|
||||
}
|
||||
|
||||
func requiredBuiltInInputError(source string, execution analyzeExecutionContext) error {
|
||||
entry, ok := execution.Catalog.Lookup(source)
|
||||
if !ok || strings.TrimSpace(entry.ProducerStage) == "" {
|
||||
|
||||
Reference in New Issue
Block a user