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.