package app import ( "context" "fmt" "gitea.maximumdirect.net/eric/distributor/internal/bundle" "gitea.maximumdirect.net/eric/distributor/internal/config" "gitea.maximumdirect.net/eric/distributor/internal/storage" ) type sourceCommandOptions struct { CommandName string Path string ConfigPath string PipelineID string BundlePath string } type sourceSelection struct { Bundles []bundle.Bundle PipelineID string SourceBackend string ConfigMode bool Warnings []OutputWarning } func selectSourceBundles(ctx context.Context, options sourceCommandOptions, provider backendFactoryProvider) (sourceSelection, error) { if err := ctx.Err(); err != nil { return sourceSelection{}, err } if options.ConfigPath != "" { cfg, err := config.LoadFile(options.ConfigPath) if err != nil { return sourceSelection{}, err } return selectSourceBundlesFromConfig(ctx, cfg, options, provider) } if options.PipelineID != "" { return sourceSelection{}, fmt.Errorf("configured source mode requires --config") } if options.BundlePath != "" { return sourceSelection{}, fmt.Errorf("configured source mode requires --config") } if options.Path == "" { return sourceSelection{}, fmt.Errorf("%s command requires a path", options.CommandName) } backend, err := newBackendFactory().openLocalPath(ctx, options.Path) if err != nil { return sourceSelection{}, err } defer closeBackend(backend) bundles, err := bundle.Discover(ctx, backend, "") if err != nil { return sourceSelection{}, err } return sourceSelection{Bundles: bundles}, nil } func selectSourceBundlesFromConfig(ctx context.Context, cfg config.Config, options sourceCommandOptions, provider backendFactoryProvider) (sourceSelection, error) { if options.Path != "" { return sourceSelection{}, fmt.Errorf("configured source mode does not accept a local path") } if options.PipelineID == "" { return sourceSelection{}, fmt.Errorf("configured source mode requires --pipeline") } secretLoad, err := config.LoadSecretEnvironment(cfg.Secrets.Directory, nil) if err != nil { return sourceSelection{}, err } pipeline, ok := findPipeline(cfg, options.PipelineID) if !ok { return sourceSelection{}, fmt.Errorf("pipeline %q not found", options.PipelineID) } backends := provider(secretLoad.Environment) sourceBackend, err := backends.openSource(ctx, pipeline.Source) if err != nil { return sourceSelection{}, fmt.Errorf("pipeline %s source backend %s: %w", pipeline.ID, pipeline.Source.Backend, err) } defer closeBackend(sourceBackend) var bundles []bundle.Bundle if options.BundlePath != "" { sourceBundle, err := bundle.Validate(ctx, sourceBackend, options.BundlePath) if err != nil { return sourceSelection{}, fmt.Errorf("pipeline %s source backend %s bundle %s: %w", pipeline.ID, pipeline.Source.Backend, storage.DisplayPath(options.BundlePath), err) } bundles = []bundle.Bundle{sourceBundle} } else { bundles, err = bundle.Discover(ctx, sourceBackend, "") if err != nil { return sourceSelection{}, fmt.Errorf("pipeline %s source backend %s discover source bundles: %w", pipeline.ID, pipeline.Source.Backend, err) } } return sourceSelection{ Bundles: bundles, PipelineID: pipeline.ID, SourceBackend: pipeline.Source.Backend, ConfigMode: true, Warnings: append(secretConflictWarnings(secretLoad.Conflicts), sourceSSHWarnings(pipeline)...), }, nil } func findPipeline(cfg config.Config, id string) (config.Pipeline, bool) { for _, pipeline := range cfg.Pipelines { if pipeline.ID == id { return pipeline, true } } return config.Pipeline{}, false } func sourceSSHWarnings(pipeline config.Pipeline) []OutputWarning { if pipeline.Source.Backend != config.BackendSSH || pipeline.Source.SSH.HostKeyPolicy != config.HostKeyPolicyOff { return nil } return []OutputWarning{{ Message: fmt.Sprintf("pipeline=%s source host_key_policy=off disables SSH host key checking", pipeline.ID), }} }