Centralize artifact source and publish destination policy

This commit is contained in:
2026-05-23 13:28:25 +00:00
parent 572a112c31
commit 7d584ee6cd
9 changed files with 319 additions and 115 deletions

View File

@@ -6,10 +6,10 @@ import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"gitea.maximumdirect.net/eric/narratio/internal/artifactmodel"
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
)
@@ -40,8 +40,6 @@ const (
// ErrSessionArtifactNotFound is returned when no readable artifact exists for a known ID.
var ErrSessionArtifactNotFound = errors.New("session artifact not found")
var configuredArtifactSourceRE = regexp.MustCompile(`^narratio\.artifact\.([a-z][a-z0-9_]*)$`)
var previousSessionArtifactSourceRE = regexp.MustCompile(`^narratio\.previous_session\.artifact\.([a-z][a-z0-9_]*)$`)
type artifactContentKind string
@@ -107,43 +105,39 @@ func (e *SessionArtifactNotFoundError) Unwrap() error {
// NormalizeSessionArtifactSource validates canonical artifact IDs.
func NormalizeSessionArtifactSource(source string) (string, error) {
normalized := strings.TrimSpace(source)
if normalized == "" {
return "", fmt.Errorf("artifact source is required")
}
if _, ok := artifactRegistry[normalized]; !ok {
classified, err := artifactpolicy.ClassifySource(source)
if err != nil {
return "", fmt.Errorf("unsupported artifact source %q", source)
}
return normalized, nil
if classified.Kind != artifactpolicy.SourceKindBuiltIn {
return "", fmt.Errorf("unsupported artifact source %q", source)
}
if _, ok := artifactRegistry[classified.ID]; !ok {
return "", fmt.Errorf("unsupported artifact source %q", source)
}
return classified.ID, nil
}
// IsConfiguredArtifactSource returns true when source is narratio.artifact.<name>.
func IsConfiguredArtifactSource(source string) bool {
return configuredArtifactSourceRE.MatchString(strings.TrimSpace(source))
_, ok := artifactpolicy.ParseConfiguredSource(source)
return ok
}
// ConfiguredArtifactName extracts <name> from narratio.artifact.<name>.
func ConfiguredArtifactName(source string) (string, bool) {
matches := configuredArtifactSourceRE.FindStringSubmatch(strings.TrimSpace(source))
if len(matches) != 2 {
return "", false
}
return matches[1], true
return artifactpolicy.ParseConfiguredSource(source)
}
// IsPreviousSessionArtifactSource returns true when source is narratio.previous_session.artifact.<name>.
func IsPreviousSessionArtifactSource(source string) bool {
_, ok := PreviousSessionArtifactName(source)
_, ok := artifactpolicy.ParsePreviousSessionSource(source)
return ok
}
// PreviousSessionArtifactName extracts <name> from narratio.previous_session.artifact.<name>.
func PreviousSessionArtifactName(source string) (string, bool) {
matches := previousSessionArtifactSourceRE.FindStringSubmatch(strings.TrimSpace(source))
if len(matches) != 2 {
return "", false
}
return matches[1], true
return artifactpolicy.ParsePreviousSessionSource(source)
}
// ResolveSessionArtifact resolves a symbolic source to a readable local session artifact path.

View File

@@ -4,6 +4,8 @@ import (
"fmt"
"sort"
"strings"
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
)
const (
@@ -47,7 +49,7 @@ func NewArtifactCatalog() *ArtifactCatalog {
// ConfiguredArtifactSourceID converts a configured artifact key into canonical source ID.
func ConfiguredArtifactSourceID(key string) string {
return "narratio.artifact." + strings.TrimSpace(key)
return artifactpolicy.ConfiguredSourceID(key)
}
// RegisterBuiltIns registers built-in source definitions used by runtime artifact resolution.