Rename archive config and stage contract to publish
This commit is contained in:
@@ -34,7 +34,7 @@ var archivePrerequisiteStages = []string{
|
||||
"analyze",
|
||||
}
|
||||
|
||||
func (archiveStage) Name() string { return "archive" }
|
||||
func (archiveStage) Name() string { return "publish" }
|
||||
|
||||
func (archiveStage) Declares() IODecl {
|
||||
return IODecl{
|
||||
@@ -46,13 +46,13 @@ func (archiveStage) Declares() IODecl {
|
||||
|
||||
func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*StageResult, error) {
|
||||
if env == nil || env.Config == nil || env.Config.Pipeline == nil || env.Config.Session == nil {
|
||||
return nil, fmt.Errorf("archive: resolved config must include pipeline and session")
|
||||
return nil, fmt.Errorf("publish: resolved config must include pipeline and session")
|
||||
}
|
||||
|
||||
if archiveDisabled(env) {
|
||||
return &StageResult{
|
||||
Metadata: map[string]any{
|
||||
"stage": "archive",
|
||||
"stage": "publish",
|
||||
"skipped": true,
|
||||
"archive_enabled": false,
|
||||
"audio_upload_skipped": true,
|
||||
@@ -63,7 +63,7 @@ func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
if archiveRunUploadDisabled(env) {
|
||||
return &StageResult{
|
||||
Metadata: map[string]any{
|
||||
"stage": "archive",
|
||||
"stage": "publish",
|
||||
"skipped": true,
|
||||
"upload_run_enabled": false,
|
||||
"audio_upload_skipped": true,
|
||||
@@ -73,76 +73,76 @@ func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
}
|
||||
|
||||
if err := validateArchivePrerequisites(m); err != nil {
|
||||
return nil, fmt.Errorf("archive: %w", err)
|
||||
return nil, fmt.Errorf("publish: %w", err)
|
||||
}
|
||||
if env.ObjectStore == nil {
|
||||
return nil, fmt.Errorf("archive: remote object store backend is required when archive run upload is enabled")
|
||||
return nil, fmt.Errorf("publish: remote object store backend is required when publish run upload is enabled")
|
||||
}
|
||||
|
||||
runRoot, err := resolveArchiveRunRoot(env, m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: resolve run root: %w", err)
|
||||
return nil, fmt.Errorf("publish: resolve run root: %w", err)
|
||||
}
|
||||
runRootInfo, err := os.Stat(runRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: run root %q: %w", runRoot, err)
|
||||
return nil, fmt.Errorf("publish: run root %q: %w", runRoot, err)
|
||||
}
|
||||
if !runRootInfo.IsDir() {
|
||||
return nil, fmt.Errorf("archive: run root %q is not a directory", runRoot)
|
||||
return nil, fmt.Errorf("publish: run root %q is not a directory", runRoot)
|
||||
}
|
||||
|
||||
runPrefix, err := artifacts.ResolveArchiveRunPrefix(env.Config, m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: resolve s3 run prefix: %w", err)
|
||||
return nil, fmt.Errorf("publish: resolve s3 run prefix: %w", err)
|
||||
}
|
||||
sessionPrefix, err := artifacts.ResolveArchiveSessionPrefix(env.Config, m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: resolve s3 session prefix: %w", err)
|
||||
return nil, fmt.Errorf("publish: resolve s3 session prefix: %w", err)
|
||||
}
|
||||
bucket := artifacts.ResolveArchiveBucket(env.Config, m)
|
||||
if bucket == "" {
|
||||
return nil, fmt.Errorf("archive: resolve s3 bucket: bucket is required")
|
||||
return nil, fmt.Errorf("publish: resolve s3 bucket: bucket is required")
|
||||
}
|
||||
runID := strings.TrimSpace(m.RunID)
|
||||
if runID == "" {
|
||||
return nil, fmt.Errorf("archive: run id is required")
|
||||
return nil, fmt.Errorf("publish: run id is required")
|
||||
}
|
||||
|
||||
manifestSource, err := resolveArchiveRunManifestSource(runRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: resolve run manifest source: %w", err)
|
||||
return nil, fmt.Errorf("publish: resolve run manifest source: %w", err)
|
||||
}
|
||||
|
||||
runFiles, err := collectArchiveRunFiles(runRoot, manifestSource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: collect run files: %w", err)
|
||||
return nil, fmt.Errorf("publish: collect run files: %w", err)
|
||||
}
|
||||
sessionPaths := archiveSessionPaths(env, m)
|
||||
previousFiles, err := collectArchivePreviousFiles(sessionPaths.PreviousDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: collect previous files: %w", err)
|
||||
return nil, fmt.Errorf("publish: collect previous files: %w", err)
|
||||
}
|
||||
runtimeCatalog, err := buildArchiveRuntimeArtifactCatalog(sessionPaths, env.Config.Pipeline.Scriptorium)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: build runtime artifact catalog: %w", err)
|
||||
return nil, fmt.Errorf("publish: build runtime artifact catalog: %w", err)
|
||||
}
|
||||
promotions, skippedOptional, skippedUnselected, lockedPromotions, err := resolveArchivePromotions(
|
||||
sessionPaths,
|
||||
m,
|
||||
runtimeCatalog,
|
||||
env.Config.Pipeline.Archive.PromoteArtifacts,
|
||||
env.Config.Pipeline.Archive.Locks,
|
||||
env.Config.Pipeline.Publish.PromoteArtifacts,
|
||||
env.Config.Pipeline.Publish.Locks,
|
||||
env.SelectedArtifactKeys,
|
||||
sessionPrefix,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: resolve promotion rules: %w", err)
|
||||
return nil, fmt.Errorf("publish: resolve promotion rules: %w", err)
|
||||
}
|
||||
runUploaded := make([]string, 0, len(runFiles))
|
||||
for _, file := range runFiles {
|
||||
key := artifacts.S3RunRelativeDestinationKey(runPrefix, file.RelativePath)
|
||||
if _, err := env.ObjectStore.Upload(ctx, file.LocalPath, key, storage.UploadOptions{}); err != nil {
|
||||
return nil, fmt.Errorf("archive: upload run file %q to %q: %w", file.RelativePath, key, err)
|
||||
return nil, fmt.Errorf("publish: upload run file %q to %q: %w", file.RelativePath, key, err)
|
||||
}
|
||||
runUploaded = append(runUploaded, file.RelativePath)
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
for _, promotion := range promotions {
|
||||
key := artifacts.S3PromotedArtifactKey(sessionPrefix, promotion.Dest)
|
||||
if _, err := env.ObjectStore.Upload(ctx, promotion.LocalPath, key, storage.UploadOptions{}); err != nil {
|
||||
return nil, fmt.Errorf("archive: upload promoted output source %q to %q: %w", promotion.Source, key, err)
|
||||
return nil, fmt.Errorf("publish: upload promoted output source %q to %q: %w", promotion.Source, key, err)
|
||||
}
|
||||
promotedUploaded = append(promotedUploaded, promotion.Dest)
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
for _, file := range previousFiles {
|
||||
key := artifacts.S3PromotedArtifactKey(sessionPrefix, file.RelativePath)
|
||||
if _, err := env.ObjectStore.Upload(ctx, file.LocalPath, key, storage.UploadOptions{}); err != nil {
|
||||
return nil, fmt.Errorf("archive: upload previous file %q to %q: %w", file.RelativePath, key, err)
|
||||
return nil, fmt.Errorf("publish: upload previous file %q to %q: %w", file.RelativePath, key, err)
|
||||
}
|
||||
previousUploaded = append(previousUploaded, file.RelativePath)
|
||||
}
|
||||
@@ -179,31 +179,31 @@ func (archiveStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*S
|
||||
currentManifestKey,
|
||||
))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: build current manifest snapshot: %w", err)
|
||||
return nil, fmt.Errorf("publish: build current manifest snapshot: %w", err)
|
||||
}
|
||||
defer func() { _ = os.Remove(manifestTempPath) }()
|
||||
|
||||
if _, err := env.ObjectStore.Upload(ctx, manifestTempPath, currentManifestKey, storage.UploadOptions{
|
||||
ContentType: "application/json",
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("archive: upload current manifest to %q: %w", currentManifestKey, err)
|
||||
return nil, fmt.Errorf("publish: upload current manifest to %q: %w", currentManifestKey, err)
|
||||
}
|
||||
|
||||
runIDTempPath, err := writeCurrentRunIDPointer(runID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archive: build current run id pointer: %w", err)
|
||||
return nil, fmt.Errorf("publish: build current run id pointer: %w", err)
|
||||
}
|
||||
defer func() { _ = os.Remove(runIDTempPath) }()
|
||||
|
||||
if _, err := env.ObjectStore.Upload(ctx, runIDTempPath, currentRunPointerKey, storage.UploadOptions{
|
||||
ContentType: "text/plain; charset=utf-8",
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("archive: upload current run pointer to %q: %w", currentRunPointerKey, err)
|
||||
return nil, fmt.Errorf("publish: upload current run pointer to %q: %w", currentRunPointerKey, err)
|
||||
}
|
||||
|
||||
return &StageResult{
|
||||
Metadata: map[string]any{
|
||||
"stage": "archive",
|
||||
"stage": "publish",
|
||||
"uploaded": true,
|
||||
"s3_bucket": bucket,
|
||||
"s3_run_prefix": runPrefix,
|
||||
@@ -250,7 +250,7 @@ type archiveSkippedUnselectedPromotion struct {
|
||||
}
|
||||
|
||||
func archiveDisabled(env *Env) bool {
|
||||
cfg := env.Config.Pipeline.Archive
|
||||
cfg := env.Config.Pipeline.Publish
|
||||
if cfg == nil {
|
||||
return true
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func archiveDisabled(env *Env) bool {
|
||||
}
|
||||
|
||||
func archiveRunUploadDisabled(env *Env) bool {
|
||||
cfg := env.Config.Pipeline.Archive
|
||||
cfg := env.Config.Pipeline.Publish
|
||||
if cfg == nil {
|
||||
return true
|
||||
}
|
||||
@@ -715,8 +715,8 @@ func writeCurrentManifestSnapshot(m *manifest.Manifest, archiveMetadata map[stri
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
clone.MarkStageSucceeded("archive", now, nil)
|
||||
if sr := clone.Stages["archive"]; sr != nil {
|
||||
clone.MarkStageSucceeded("publish", now, nil)
|
||||
if sr := clone.Stages["publish"]; sr != nil {
|
||||
sr.Metadata = archiveMetadata
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ func archiveMetadataPreview(
|
||||
currentManifestKey string,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"stage": "archive",
|
||||
"stage": "publish",
|
||||
"uploaded": true,
|
||||
"s3_bucket": bucket,
|
||||
"s3_run_prefix": runPrefix,
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
|
||||
func TestArchiveSkipsWhenDisabled(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.Enabled = boolPtr(false)
|
||||
env.Config.Pipeline.Publish.Enabled = boolPtr(false)
|
||||
|
||||
result, err := archiveStage{}.Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
@@ -35,7 +35,7 @@ func TestArchiveSkipsWhenDisabled(t *testing.T) {
|
||||
|
||||
func TestArchiveSkipsRunUploadWhenDisabled(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.UploadRun = boolPtr(false)
|
||||
env.Config.Pipeline.Publish.UploadRun = boolPtr(false)
|
||||
|
||||
result, err := archiveStage{}.Run(context.Background(), env, m)
|
||||
if err != nil {
|
||||
@@ -179,7 +179,7 @@ func TestArchiveToleratesMissingPreviousCache(t *testing.T) {
|
||||
|
||||
func TestArchiveUsesCustomPromotionRules(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
env.Config.Pipeline.Publish.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
{Source: "narratio.transcript.final_trimmed", Dest: "published/trimmed.json", Required: boolPtr(true)},
|
||||
{Source: "narratio.artifact.session_recap", Dest: "published/recap.md", Required: boolPtr(true)},
|
||||
}
|
||||
@@ -200,7 +200,7 @@ func TestArchiveUsesCustomPromotionRules(t *testing.T) {
|
||||
|
||||
func TestArchiveSkipsOptionalMissingPromotion(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
env.Config.Pipeline.Publish.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
{Source: "narratio.transcript.final_trimmed", Dest: "transcripts/final.trimmed.json", Required: boolPtr(true)},
|
||||
{Source: "narratio.transcript.base", Dest: "transcripts/base.json", Required: boolPtr(false)},
|
||||
}
|
||||
@@ -272,7 +272,7 @@ func TestArchiveSelectedConfiguredPromotionStillFailsWhenMissing(t *testing.T) {
|
||||
func TestArchiveLockedSelectedPromotionSkipsAsLocked(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.SelectedArtifactKeys = []string{"session_recap"}
|
||||
env.Config.Pipeline.Archive.Locks = []config.ArchiveLockRule{
|
||||
env.Config.Pipeline.Publish.Locks = []config.ArchiveLockRule{
|
||||
{Source: "narratio.artifact.session_recap", Reason: "reviewed"},
|
||||
}
|
||||
fake := env.ObjectStore.(*storage.FakeBackend)
|
||||
@@ -301,7 +301,7 @@ func TestArchiveLockedUnselectedConfiguredPromotionSkipsAsUnselected(t *testing.
|
||||
PromptID: "dnd.player_handout",
|
||||
OutputPath: "artifacts/player_handout.md",
|
||||
}
|
||||
env.Config.Pipeline.Archive.Locks = []config.ArchiveLockRule{
|
||||
env.Config.Pipeline.Publish.Locks = []config.ArchiveLockRule{
|
||||
{Source: "narratio.artifact.session_recap", Reason: "reviewed"},
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ func TestArchiveLockedUnselectedConfiguredPromotionSkipsAsUnselected(t *testing.
|
||||
|
||||
func TestArchiveSkipsLockedRequiredPromotionAndCommits(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.Locks = []config.ArchiveLockRule{
|
||||
env.Config.Pipeline.Publish.Locks = []config.ArchiveLockRule{
|
||||
{Source: "narratio.transcript.final_trimmed", Reason: "human reviewed"},
|
||||
}
|
||||
fake := env.ObjectStore.(*storage.FakeBackend)
|
||||
@@ -374,7 +374,7 @@ func TestArchiveSkipsLockedRequiredPromotionAndCommits(t *testing.T) {
|
||||
t.Fatalf("unmarshal current manifest: %v", err)
|
||||
}
|
||||
stages := current["stages"].(map[string]any)
|
||||
archive := stages["archive"].(map[string]any)
|
||||
archive := stages["publish"].(map[string]any)
|
||||
meta := archive["metadata"].(map[string]any)
|
||||
if meta["locked_promotion_count"] != float64(1) {
|
||||
t.Fatalf("current manifest locked_promotion_count = %#v, want 1", meta["locked_promotion_count"])
|
||||
@@ -387,10 +387,10 @@ func TestArchiveSkipsLockedRequiredPromotionAndCommits(t *testing.T) {
|
||||
|
||||
func TestArchiveLockedRequiredMissingPromotionSucceeds(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
env.Config.Pipeline.Publish.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
{Source: "narratio.transcript.base", Dest: "transcripts/base.json", Required: boolPtr(true)},
|
||||
}
|
||||
env.Config.Pipeline.Archive.Locks = []config.ArchiveLockRule{
|
||||
env.Config.Pipeline.Publish.Locks = []config.ArchiveLockRule{
|
||||
{Source: "narratio.transcript.base", Reason: "manual merge is locked"},
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ func TestArchiveLockedRequiredMissingPromotionSucceeds(t *testing.T) {
|
||||
|
||||
func TestArchiveLockDoesNotOverwriteExistingPromotion(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.Locks = []config.ArchiveLockRule{
|
||||
env.Config.Pipeline.Publish.Locks = []config.ArchiveLockRule{
|
||||
{Source: "narratio.transcript.final_trimmed", Reason: "already published"},
|
||||
}
|
||||
fake := env.ObjectStore.(*storage.FakeBackend)
|
||||
@@ -442,7 +442,7 @@ func TestArchiveLockDoesNotOverwriteExistingPromotion(t *testing.T) {
|
||||
|
||||
func TestArchiveFailsWhenRequiredPromotionMissing(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
env.Config.Pipeline.Archive.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
env.Config.Pipeline.Publish.PromoteArtifacts = []config.ArchivePromotionRule{
|
||||
{Source: "narratio.transcript.base", Dest: "transcripts/base.json", Required: boolPtr(true)},
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ func archiveFixture(t *testing.T) (*Env, *manifest.Manifest, string) {
|
||||
RootPrefix: "dnd",
|
||||
},
|
||||
},
|
||||
Archive: &config.ArchiveConfig{
|
||||
Publish: &config.ArchiveConfig{
|
||||
Enabled: boolPtr(true),
|
||||
UploadRun: boolPtr(true),
|
||||
PromoteArtifacts: []config.ArchivePromotionRule{
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestStagesReturnExpectedMetadata(t *testing.T) {
|
||||
RootPrefix: "dnd",
|
||||
},
|
||||
},
|
||||
Archive: &config.ArchiveConfig{
|
||||
Publish: &config.ArchiveConfig{
|
||||
Enabled: boolPtr(true),
|
||||
UploadRun: boolPtr(true),
|
||||
},
|
||||
@@ -195,9 +195,9 @@ func TestStagesReturnExpectedMetadata(t *testing.T) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if s.Name() == "archive" {
|
||||
if result.Metadata["stage"] != "archive" {
|
||||
t.Fatalf("archive metadata = %#v, want stage=archive", result.Metadata)
|
||||
if s.Name() == "publish" {
|
||||
if result.Metadata["stage"] != "publish" {
|
||||
t.Fatalf("archive metadata = %#v, want stage=publish", result.Metadata)
|
||||
}
|
||||
if result.Metadata["uploaded"] != true {
|
||||
t.Fatalf("archive metadata = %#v, want uploaded=true", result.Metadata)
|
||||
|
||||
Reference in New Issue
Block a user