refactor: Inject CommandExecutor into guard to fix dependency direction

guard depended on internal/runner, which inverted the intended layer
hierarchy. Now guard defines a CommandExecutor function type and accepts
it as a constructor argument. internal/flows (the composition root)
creates the executor closure wrapping runner.Execute and injects it,
keeping guard free of internal/ dependencies.
This commit is contained in:
Sahilb315
2026-04-14 20:34:33 +05:30
parent da098a51a8
commit 1089d4b3cd
3 changed files with 27 additions and 12 deletions
+9 -6
View File
@@ -15,11 +15,15 @@ import (
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/extractor"
"github.com/safedep/pmg/internal/audit"
"github.com/safedep/pmg/internal/runner"
"github.com/safedep/pmg/internal/ui"
"github.com/safedep/pmg/packagemanager"
)
// CommandExecutor executes a parsed package manager command directly.
// It is injected into the guard so that callers control execution behavior
// (e.g., dry-run, sandbox application) without guard depending on internal packages.
type CommandExecutor func(ctx context.Context, pc *packagemanager.ParsedCommand) error
type PackageManagerGuardInteraction struct {
// SetStatus is called to set the status of the guard in the UI
SetStatus func(status string)
@@ -96,6 +100,7 @@ type packageManagerGuard struct {
analyzers []analyzer.PackageVersionAnalyzer
packageManager packagemanager.PackageManager
packageResolver packagemanager.PackageResolver
executor CommandExecutor
}
func NewPackageManagerGuard(config PackageManagerGuardConfig,
@@ -103,6 +108,7 @@ func NewPackageManagerGuard(config PackageManagerGuardConfig,
packageResolver packagemanager.PackageResolver,
analyzers []analyzer.PackageVersionAnalyzer,
interaction PackageManagerGuardInteraction,
executor CommandExecutor,
) (*packageManagerGuard, error) {
return &packageManagerGuard{
interaction: interaction,
@@ -110,6 +116,7 @@ func NewPackageManagerGuard(config PackageManagerGuardConfig,
packageManager: packageManager,
packageResolver: packageResolver,
config: config,
executor: executor,
}, nil
}
@@ -252,11 +259,7 @@ func (g *packageManagerGuard) Run(ctx context.Context, args []string, parsedComm
}
func (g *packageManagerGuard) continueExecution(ctx context.Context, pc *packagemanager.ParsedCommand) error {
pmName := ""
if g.packageManager != nil {
pmName = g.packageManager.Name()
}
return runner.Execute(ctx, pc, pmName, g.config.DryRun)
return g.executor(ctx, pc)
}
func (g *packageManagerGuard) concurrentAnalyzePackages(ctx context.Context,
+11 -5
View File
@@ -11,6 +11,12 @@ import (
"github.com/stretchr/testify/assert"
)
// noopExecutor is a no-op executor for use in tests that set DryRun=true
// or otherwise don't reach actual command execution.
var noopExecutor CommandExecutor = func(_ context.Context, _ *packagemanager.ParsedCommand) error {
return nil
}
func TestGuardConcurrentlyAnalyzePackagesMalwareQueryService(t *testing.T) {
mq, err := analyzer.NewMalysisQueryAnalyzer(analyzer.MalysisQueryAnalyzerConfig{})
if err != nil {
@@ -20,7 +26,7 @@ func TestGuardConcurrentlyAnalyzePackagesMalwareQueryService(t *testing.T) {
pg, err := NewPackageManagerGuard(DefaultPackageManagerGuardConfig(), nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, PackageManagerGuardInteraction{
ShowWarning: func(message string) {},
})
}, noopExecutor)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
@@ -80,7 +86,7 @@ func TestGuardInsecureInstallation(t *testing.T) {
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
[]analyzer.PackageVersionAnalyzer{mq}, interaction, noopExecutor)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
@@ -129,7 +135,7 @@ func TestGuardInsecureInstallation(t *testing.T) {
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
[]analyzer.PackageVersionAnalyzer{mq}, interaction, noopExecutor)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
@@ -184,7 +190,7 @@ func TestGuardInsecureInstallation(t *testing.T) {
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
[]analyzer.PackageVersionAnalyzer{mq}, interaction, noopExecutor)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
@@ -225,7 +231,7 @@ func TestGuardInsecureInstallation(t *testing.T) {
}
pg, err := NewPackageManagerGuard(config, nil, nil,
[]analyzer.PackageVersionAnalyzer{mq}, interaction)
[]analyzer.PackageVersionAnalyzer{mq}, interaction, noopExecutor)
if err != nil {
t.Fatalf("failed to create pg: %v", err)
}
+7 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/safedep/pmg/config"
"github.com/safedep/pmg/guard"
"github.com/safedep/pmg/internal/audit"
"github.com/safedep/pmg/internal/runner"
"github.com/safedep/pmg/internal/ui"
"github.com/safedep/pmg/packagemanager"
)
@@ -77,7 +78,12 @@ func (f *commonFlow) Run(ctx context.Context, args []string, parsedCmd *packagem
guardConfig.DryRun = cfg.DryRun
guardConfig.InsecureInstallation = cfg.InsecureInstallation
guardManager, err := guard.NewPackageManagerGuard(guardConfig, f.pm, f.packageResolver, analyzers, interaction)
pmName := f.pm.Name()
executor := func(ctx context.Context, pc *packagemanager.ParsedCommand) error {
return runner.Execute(ctx, pc, pmName, cfg.DryRun)
}
guardManager, err := guard.NewPackageManagerGuard(guardConfig, f.pm, f.packageResolver, analyzers, interaction, executor)
if err != nil {
return fmt.Errorf("failed to create package manager guard: %s", err)
}