Delete run manifest domain model
This commit is contained in:
@@ -21,12 +21,6 @@ type Store interface {
|
||||
Save(ctx context.Context, path string, m *Manifest) error
|
||||
}
|
||||
|
||||
// RunStore persists invocation-scoped run manifests.
|
||||
type RunStore interface {
|
||||
CreateRun(ctx context.Context, sessionID, campaign, runID string, force bool, requestedStages []string) (*RunManifest, error)
|
||||
SaveRun(ctx context.Context, path string, m *RunManifest) error
|
||||
}
|
||||
|
||||
// LocalStore stores manifests as JSON on the local filesystem.
|
||||
type LocalStore struct{}
|
||||
|
||||
@@ -136,117 +130,6 @@ func (s *LocalStore) Save(ctx context.Context, path string, m *Manifest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRun returns a new in-memory run manifest for one invocation.
|
||||
func (s *LocalStore) CreateRun(
|
||||
ctx context.Context,
|
||||
sessionID, campaign, runID string,
|
||||
force bool,
|
||||
requestedStages []string,
|
||||
) (*RunManifest, error) {
|
||||
if err := checkContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.TrimSpace(sessionID) == "" {
|
||||
return nil, fmt.Errorf("create run manifest: session_id is required")
|
||||
}
|
||||
if strings.TrimSpace(runID) == "" {
|
||||
return nil, fmt.Errorf("create run manifest: run_id is required")
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
return NewRun(sessionID, campaign, runID, force, requestedStages, now), nil
|
||||
}
|
||||
|
||||
// LoadRun reads and validates a local JSON run manifest from path.
|
||||
func (s *LocalStore) LoadRun(ctx context.Context, path string) (*RunManifest, error) {
|
||||
if err := checkContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return nil, fmt.Errorf("load run manifest: path is required")
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load run manifest %q: %w", path, err)
|
||||
}
|
||||
defer file.Close()
|
||||
return s.LoadRunReader(ctx, file)
|
||||
}
|
||||
|
||||
// LoadRunReader reads and validates a run manifest from a caller-owned reader.
|
||||
func (s *LocalStore) LoadRunReader(ctx context.Context, source io.Reader) (*RunManifest, error) {
|
||||
if err := checkContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if source == nil {
|
||||
return nil, fmt.Errorf("load run manifest: source is required")
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(source)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read run manifest: %w", err)
|
||||
}
|
||||
|
||||
var m RunManifest
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return nil, fmt.Errorf("decode run manifest: %w", err)
|
||||
}
|
||||
|
||||
if err := validateLoadedRunManifest(&m); err != nil {
|
||||
return nil, fmt.Errorf("run manifest invalid: %w", err)
|
||||
}
|
||||
normalizeRunManifest(&m)
|
||||
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
// SaveRun writes the run manifest to path atomically via temp file + rename.
|
||||
func (s *LocalStore) SaveRun(ctx context.Context, path string, m *RunManifest) error {
|
||||
if err := checkContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return fmt.Errorf("save run manifest: path is required")
|
||||
}
|
||||
if m == nil {
|
||||
return fmt.Errorf("save run manifest: manifest is nil")
|
||||
}
|
||||
if err := validateRunManifestSchemaVersion(m.SchemaVersion); err != nil {
|
||||
return fmt.Errorf("save run manifest: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(m.SessionID) == "" {
|
||||
return fmt.Errorf("save run manifest: session_id is required")
|
||||
}
|
||||
if strings.TrimSpace(m.RunID) == "" {
|
||||
return fmt.Errorf("save run manifest: run_id is required")
|
||||
}
|
||||
if m.CreatedAt.IsZero() {
|
||||
return fmt.Errorf("save run manifest: created_at is required")
|
||||
}
|
||||
if err := validateManifestIdentities(m.SessionID, m.Campaign, m.RunID); err != nil {
|
||||
return fmt.Errorf("save run manifest: %w", err)
|
||||
}
|
||||
if err := validateRunStages(m.Stages); err != nil {
|
||||
return fmt.Errorf("save run manifest: %w", err)
|
||||
}
|
||||
for name, stage := range m.Stages {
|
||||
normalizeAnalyzeArtifactCollection(stage.AnalyzeArtifacts)
|
||||
if err := validateAnalyzeStageState(name, stage.AnalyzeStateVersion, stage.AnalyzeArtifacts); err != nil {
|
||||
return fmt.Errorf("save run manifest: stages.%s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
m.UpdatedAt = time.Now().UTC()
|
||||
data, err := json.MarshalIndent(m, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("save run manifest: marshal: %w", err)
|
||||
}
|
||||
data = append(data, '\n')
|
||||
|
||||
return writeJSONAtomically(ctx, path, data)
|
||||
}
|
||||
|
||||
func validateLoadedManifest(m *Manifest) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("manifest is nil")
|
||||
@@ -313,45 +196,6 @@ func validatePostPublishCleanup(cleanup *PostPublishCleanup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLoadedRunManifest(m *RunManifest) error {
|
||||
if m == nil {
|
||||
return fmt.Errorf("manifest is nil")
|
||||
}
|
||||
if err := validateRunManifestSchemaVersion(m.SchemaVersion); err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.TrimSpace(m.SessionID) == "" {
|
||||
return fmt.Errorf("session_id is required")
|
||||
}
|
||||
if strings.TrimSpace(m.RunID) == "" {
|
||||
return fmt.Errorf("run_id is required")
|
||||
}
|
||||
if m.CreatedAt.IsZero() {
|
||||
return fmt.Errorf("created_at is required")
|
||||
}
|
||||
if m.UpdatedAt.IsZero() {
|
||||
return fmt.Errorf("updated_at is required")
|
||||
}
|
||||
if err := validateManifestIdentities(m.SessionID, m.Campaign, m.RunID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRunStages(m.Stages); err != nil {
|
||||
return err
|
||||
}
|
||||
for name, stage := range m.Stages {
|
||||
if err := validateAnalyzeStageState(name, stage.AnalyzeStateVersion, stage.AnalyzeArtifacts); err != nil {
|
||||
return fmt.Errorf("stages.%s: %w", name, err)
|
||||
}
|
||||
if stage.SemanticConfig != nil {
|
||||
if err := stage.SemanticConfig.Validate(); err != nil {
|
||||
return fmt.Errorf("stages.%s.semantic_config: %w", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateManifestIdentities(sessionID, campaign, runID string) error {
|
||||
identities := []struct {
|
||||
field string
|
||||
@@ -379,20 +223,10 @@ func validateManifestIdentities(sessionID, campaign, runID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeRunManifest(m *RunManifest) {
|
||||
for _, stage := range m.Stages {
|
||||
normalizeAnalyzeArtifactCollection(stage.AnalyzeArtifacts)
|
||||
}
|
||||
}
|
||||
|
||||
func validateSessionManifestSchemaVersion(version string) error {
|
||||
return validateManifestSchemaVersion("session manifest", version, SessionManifestSchemaVersion)
|
||||
}
|
||||
|
||||
func validateRunManifestSchemaVersion(version string) error {
|
||||
return validateManifestSchemaVersion("run manifest", version, RunManifestSchemaVersion)
|
||||
}
|
||||
|
||||
func validateManifestSchemaVersion(kind, version, supported string) error {
|
||||
if version == supported {
|
||||
return nil
|
||||
@@ -421,24 +255,6 @@ func validateSessionStages(stages map[string]*StageRecord) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateRunStages(stages map[string]*RunStageRecord) error {
|
||||
if stages == nil {
|
||||
return fmt.Errorf("stages is required")
|
||||
}
|
||||
for name, stage := range stages {
|
||||
if stage == nil {
|
||||
return fmt.Errorf("stages.%s is nil", name)
|
||||
}
|
||||
if stage.Name == "" {
|
||||
return fmt.Errorf("stages.%s.name is required", name)
|
||||
}
|
||||
if stage.Name != name {
|
||||
return fmt.Errorf("stages.%s.name %q does not match map key", name, stage.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeJSONAtomically(ctx context.Context, path string, data []byte) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := fileops.EnsureWorkspaceDirectory(dir); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user