package app import ( "context" "flag" "fmt" "io" "strings" "gitea.maximumdirect.net/eric/narratio/internal/adapters/storage" "gitea.maximumdirect.net/eric/narratio/internal/artifacts" "gitea.maximumdirect.net/eric/narratio/internal/config" "gitea.maximumdirect.net/eric/narratio/internal/manifest" ) type commonConfigFlags struct { pipelinePath string campaignPath string campaignFilePath string sessionPath string sessionID string previousSessionID string } func addCommonConfigFlags(fs *flag.FlagSet, flags *commonConfigFlags) { fs.StringVar(&flags.pipelinePath, "config", "", "path to pipeline.yml (optional; defaults searched)") fs.StringVar(&flags.campaignPath, "campaign", "", "campaign ID") fs.StringVar(&flags.campaignFilePath, "campaign-file", "", "path to campaign.yml") fs.StringVar(&flags.sessionPath, "session", "", "path to session.yml") fs.StringVar(&flags.sessionID, "session-id", "", "session identifier") fs.StringVar(&flags.previousSessionID, "previous-session-id", "", "expected previous session identifier") } func (f commonConfigFlags) sessionOptions() config.SessionLoadOptions { return config.SessionLoadOptions{ SessionID: f.sessionID, PreviousSessionID: f.previousSessionID, } } // Session dispatches session helper subcommands. func Session(ctx context.Context, args []string, out io.Writer) error { if len(args) == 0 { return fmt.Errorf("session: expected subcommand: init|validate|status|plan|restore|artifacts|locks") } switch args[0] { case "init": return SessionInit(ctx, args[1:], out) case "validate": return SessionValidate(ctx, args[1:], out) case "status": return Status(ctx, args[1:], out) case "plan": return Plan(ctx, args[1:], out) case "restore": return Restore(ctx, args[1:], out) case "artifacts": return ArtifactsList(ctx, args[1:], out) case "locks": return SessionLocks(ctx, args[1:], out) default: return fmt.Errorf("session: unknown subcommand %q", args[0]) } } // SessionLocks dispatches session-oriented publish lock list and mutation // helpers while preserving the existing lock implementations. func SessionLocks(ctx context.Context, args []string, out io.Writer) error { if len(args) > 0 && !isCLIFlagToken(args[0]) { switch args[0] { case "add": return LocksAdd(ctx, args[1:], out) case "remove": return LocksRemove(ctx, args[1:], out) } } return LocksList(ctx, args, out) } // Artifacts dispatches artifact helper subcommands. func Artifacts(ctx context.Context, args []string, out io.Writer) error { if len(args) == 0 { return fmt.Errorf("artifacts: expected subcommand: list") } switch args[0] { case "list": return ArtifactsList(ctx, args[1:], out) default: return fmt.Errorf("artifacts: unknown subcommand %q", args[0]) } } func loadHelperContext(ctx context.Context, flags commonConfigFlags, needStore bool) (*config.Config, storage.ObjectStore, *effectiveLocks, *manifest.Manifest, error) { cfg, err := loadCommandConfig(ctx, flags.pipelinePath, flags.campaignPath, flags.campaignFilePath, flags.sessionPath, flags.sessionOptions()) if err != nil { return nil, nil, nil, nil, err } if err := config.Validate(cfg); err != nil { return nil, nil, nil, nil, err } var store storage.ObjectStore if needStore { store, err = newCommandObjectStore(ctx, cfg, nil) if err != nil { return nil, nil, nil, nil, err } } else { store, _ = objectStoreIfConfigured(ctx, cfg) } locks, err := loadEffectiveLocks(ctx, cfg, store) if err != nil { return nil, nil, nil, nil, err } paths := artifacts.NewLocalStore(cfg.Pipeline.Workspace.Root).SessionPathsFor(cfg.Session.Campaign, cfg.Session.SessionID) m, err := loadLocalManifest(ctx, paths.ManifestPath) if err != nil { return nil, nil, nil, nil, err } return cfg, store, locks, m, nil } func objectStoreIfConfigured(ctx context.Context, cfg *config.Config) (storage.ObjectStore, error) { if cfg == nil || cfg.Pipeline == nil || cfg.Pipeline.Storage.S3 == nil || strings.TrimSpace(cfg.Pipeline.Storage.S3.Bucket) == "" { return nil, nil } store, err := newCommandObjectStore(ctx, cfg, nil) if err != nil { return nil, err } return store, nil }