178 lines
6.3 KiB
Go
178 lines
6.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
)
|
|
|
|
func buildHelperArtifactCatalog(cfg *config.Config, m *manifest.Manifest) (*artifacts.ArtifactCatalog, error) {
|
|
catalog := artifacts.NewArtifactCatalog()
|
|
if err := catalog.RegisterBuiltIns(); err != nil {
|
|
return nil, err
|
|
}
|
|
configured := map[string]artifacts.ConfiguredArtifactDefinition{}
|
|
if cfg.Pipeline.Scriptorium != nil {
|
|
for key, item := range cfg.Pipeline.Scriptorium.Artifacts {
|
|
configured[key] = artifacts.ConfiguredArtifactDefinition{Enabled: item.Enabled, OutputPath: item.OutputPath}
|
|
}
|
|
}
|
|
if err := catalog.RegisterConfiguredArtifacts(configured, nil); err != nil {
|
|
return nil, err
|
|
}
|
|
extractionDefinitions := artifacts.ExtractionDefinitionsFromConfig(cfg.Pipeline.Notarius)
|
|
if err := catalog.RegisterExtractionArtifacts(extractionDefinitions); err != nil {
|
|
return nil, err
|
|
}
|
|
paths := artifacts.NewLocalStore(cfg.Pipeline.Workspace.Root).SessionPathsFor(cfg.Session.Campaign, cfg.Session.SessionID)
|
|
if cfg.Pipeline.Notarius != nil && cfg.Pipeline.Notarius.Enabled {
|
|
catalog.HydrateExtractionArtifacts(paths, m, extractionDefinitions)
|
|
}
|
|
return catalog, nil
|
|
}
|
|
|
|
func writeArtifactList(out io.Writer, cfg *config.Config, catalog *artifacts.ArtifactCatalog, locks *effectiveLocks, publishedRemoteState map[string]string) {
|
|
lockSet := lockSourceSet(locks.All)
|
|
fmt.Fprintln(out, "Built-in:")
|
|
for _, transcript := range artifacts.RuntimeTranscriptArtifacts() {
|
|
writeArtifactLine(out, transcript.SourceID, lockSet)
|
|
}
|
|
writeArtifactLine(out, artifacts.ArtifactBoundsSession, lockSet)
|
|
fmt.Fprintln(out, "Configured:")
|
|
for _, entry := range catalog.ListConfigured() {
|
|
writeArtifactLine(out, entry.SourceID, lockSet)
|
|
}
|
|
fmt.Fprintln(out, "Extraction:")
|
|
for _, entry := range catalog.ListExtraction() {
|
|
state := "unavailable"
|
|
if entry.Available {
|
|
state = "available"
|
|
}
|
|
writeExtractionArtifactLine(out, entry.SourceID, state, entry.Provenance, lockSet)
|
|
}
|
|
fmt.Fprintln(out, "Previous-session:")
|
|
for _, req := range artifacts.CollectPreviousArtifactRequirements(configuredScriptoriumArtifacts(cfg)) {
|
|
fmt.Fprintf(out, "- %s required=%t\n", artifactpolicy.PreviousSessionSourceID(req.Name), req.Required)
|
|
}
|
|
fmt.Fprintln(out, "Published:")
|
|
for _, rule := range cfg.Pipeline.Publish.Outputs {
|
|
writePublishedOutputLine(out, rule, catalog, lockSet, publishedRemoteState)
|
|
}
|
|
}
|
|
|
|
func writeExtractionArtifactLine(out io.Writer, source, state, provenance string, lockSet map[string]config.PublishLockRule) {
|
|
parts := []string{source, "planned", state}
|
|
if strings.TrimSpace(provenance) != "" {
|
|
parts = append(parts, "provenance="+strings.TrimSpace(provenance))
|
|
}
|
|
if _, ok := lockSet[source]; ok {
|
|
parts = append(parts, "locked")
|
|
}
|
|
fmt.Fprintf(out, "- %s\n", strings.Join(parts, " "))
|
|
}
|
|
|
|
func writeArtifactLine(out io.Writer, source string, lockSet map[string]config.PublishLockRule) {
|
|
parts := []string{source}
|
|
if _, ok := lockSet[source]; ok {
|
|
parts = append(parts, "locked")
|
|
}
|
|
fmt.Fprintf(out, "- %s\n", strings.Join(parts, " "))
|
|
}
|
|
|
|
func writePublishedOutputLine(out io.Writer, rule config.PublishOutputRule, catalog *artifacts.ArtifactCatalog, lockSet map[string]config.PublishLockRule, remoteState map[string]string) {
|
|
source := strings.TrimSpace(rule.Source)
|
|
parts := []string{source}
|
|
if _, ok := lockSet[source]; ok {
|
|
parts = append(parts, "locked")
|
|
}
|
|
dest, showDest, err := helperPublishedOutputDest(rule, catalog)
|
|
if err != nil {
|
|
parts = append(parts, "remote=error")
|
|
fmt.Fprintf(out, "- %s\n", strings.Join(parts, " "))
|
|
return
|
|
}
|
|
if showDest {
|
|
parts = append(parts, "dest="+dest)
|
|
}
|
|
if state := remoteState[publishedOutputRemoteStateKey(source, dest)]; state != "" {
|
|
parts = append(parts, state)
|
|
}
|
|
fmt.Fprintf(out, "- %s\n", strings.Join(parts, " "))
|
|
}
|
|
|
|
func remotePublishedOutputAvailability(ctx context.Context, cfg *config.Config, store storage.ObjectStore, catalog *artifacts.ArtifactCatalog) map[string]string {
|
|
out := map[string]string{}
|
|
sessionPrefix := artifacts.S3SessionPrefix(cfg.Pipeline.Storage.S3.RootPrefix, cfg.Session.Campaign, cfg.Session.SessionID)
|
|
for _, rule := range cfg.Pipeline.Publish.Outputs {
|
|
source := strings.TrimSpace(rule.Source)
|
|
dest, _, err := helperPublishedOutputDest(rule, catalog)
|
|
if err != nil {
|
|
out[publishedOutputRemoteStateKey(source, "")] = "remote=error"
|
|
continue
|
|
}
|
|
key := artifacts.S3PublishedOutputKey(sessionPrefix, dest)
|
|
if exists, err := store.Exists(ctx, key); err == nil && exists {
|
|
out[publishedOutputRemoteStateKey(source, dest)] = "remote=published"
|
|
} else if err != nil {
|
|
out[publishedOutputRemoteStateKey(source, dest)] = "remote=error"
|
|
} else {
|
|
out[publishedOutputRemoteStateKey(source, dest)] = "remote=missing"
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func helperPublishedOutputDest(rule config.PublishOutputRule, catalog *artifacts.ArtifactCatalog) (string, bool, error) {
|
|
source := strings.TrimSpace(rule.Source)
|
|
normalized, err := artifactpolicy.ResolvePublishedDestinationWithExtractions(
|
|
source,
|
|
rule.Dest,
|
|
helperConfiguredOutputPathMap(catalog),
|
|
helperExtractionOutputSet(catalog),
|
|
)
|
|
if err != nil {
|
|
return "", false, err
|
|
}
|
|
entry, ok := catalog.Lookup(source)
|
|
showDest := !ok || strings.TrimSpace(entry.CanonicalRelPath) != normalized
|
|
return normalized, showDest, nil
|
|
}
|
|
|
|
func helperExtractionOutputSet(catalog *artifacts.ArtifactCatalog) map[string]struct{} {
|
|
out := map[string]struct{}{}
|
|
if catalog == nil {
|
|
return out
|
|
}
|
|
for _, entry := range catalog.ListExtraction() {
|
|
if strings.TrimSpace(entry.ExtractionKey) != "" {
|
|
out[entry.ExtractionKey] = struct{}{}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func helperConfiguredOutputPathMap(catalog *artifacts.ArtifactCatalog) map[string]string {
|
|
out := map[string]string{}
|
|
if catalog == nil {
|
|
return out
|
|
}
|
|
for _, entry := range catalog.ListConfigured() {
|
|
if strings.TrimSpace(entry.ConfiguredKey) == "" {
|
|
continue
|
|
}
|
|
out[entry.ConfiguredKey] = strings.TrimSpace(entry.CanonicalRelPath)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func publishedOutputRemoteStateKey(source, dest string) string {
|
|
return strings.TrimSpace(source) + "\x00" + strings.TrimSpace(dest)
|
|
}
|