Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 1, 2025

Implements a universal solution for using custom binaries (like apt-fast) with any package manager through the GetPackageManagerWithOptions method. This is a clean, minimal implementation following Go best practices.

API Design

// Simple case - convenience wrapper (Go best practice)
apt, _ := syspkg.GetPackageManager("apt")

// Advanced case - custom binary name
aptFast, _ := syspkg.GetPackageManagerWithOptions("apt", &syspkg.ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

// Advanced case - full custom path
customApt, _ := syspkg.GetPackageManagerWithOptions("apt", &syspkg.ManagerCreationOptions{
    BinaryPath: "/usr/local/bin/custom-apt",
})

Changes

Core Implementation (interface.go, syspkg.go):

  • Added ManagerCreationOptions struct with BinaryPath field for custom binary configuration
  • Added GetPackageManagerWithOptions(name, opts) method to SysPkg interface
  • Made GetPackageManager() a convenience wrapper around GetPackageManagerWithOptions(name, nil) (follows Go standard library pattern)

APT Support (manager/apt/apt.go - minimal changes):

  • Added binaryName field to PackageManager struct with lazy initialization
  • Added NewPackageManagerWithBinary(binaryName string) constructor
  • Added getBinaryName() method for thread-safe lazy initialization
  • Updated executeCommand(), IsAvailable(), GetPackageManager() to use getBinaryName()

Design

This implementation follows the "Convenience Method" pattern used extensively in Go's standard library (context.WithTimeout → WithDeadline, os.Create → OpenFile, bufio.NewReader → NewReaderSize):

  • GetPackageManager(name) - Simple case, wrapper method
  • GetPackageManagerWithOptions(name, opts) - Advanced case with custom configuration

Benefits:

  • ✅ Universal pattern: Works for all package managers (APT implemented, others when needed)
  • ✅ Supports full paths: Not just binary names in $PATH
  • ✅ No IncludeOptions pollution: No need for AptFast field
  • ✅ Binary consistency: Set once at creation, consistent throughout instance lifetime
  • ✅ Go best practices: Wrapper pattern, SOLID principles compliant
  • ✅ Extensible: Easy to add more options in the future (cache paths, timeouts, etc.)
  • ✅ Minimal implementation: Clean code without exploration artifacts

Implementation Details

APT Manager:

  • Reuses all existing parsing logic (apt-fast output identical to apt)
  • Zero-value struct defaults to "apt" via lazy initialization
  • Thread-safe with sync.Once
  • Works with any apt-compatible binary (apt, apt-fast, apt-get, etc.)

Other Package Managers:

  • YUM, Snap, Flatpak: Return "not yet supported" error for custom binaries
  • Easy to extend when needed using the same pattern as APT

Usage Example

// Get standard apt
sysPkg, _ := syspkg.New(syspkg.IncludeOptions{Apt: true})
apt, _ := sysPkg.GetPackageManager("apt")

// Get apt-fast
aptFast, _ := sysPkg.GetPackageManagerWithOptions("apt", &syspkg.ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

// Use it
packages, _ := aptFast.Install([]string{"vim"}, &manager.Options{})
Original prompt

This section details on the original issue you should resolve

<issue_title>apt-fast manager support.</issue_title>
<issue_description>Apt-fast is a faster version of Debian APT which I use in my GitHub Action cache-apt-pkgs.

I played with the manager interface a bit and tried to find a way to use the existing APT manager but repoint the binary from apt to apt-fast when needed, and gave up because I didn't want to make any major changes that could cause an issue in your repo. Lazily forked and just duped the manager, which I am not happy with but needed something to use (https://github.com/awalsh128/syspkg).

Wanted to get your ideas on the best way to implement this and I could put together a PR.</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@coderabbitai
Copy link

coderabbitai bot commented Nov 1, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI changed the title [WIP] Add apt-fast manager support for package management Add apt-fast support via configurable binary name Nov 1, 2025
Copilot AI requested a review from bluet November 1, 2025 07:32
@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot 1. is this the ideal solution with the most flexibility and minimum change?
2. are the behavior and input/output/parameters of apt-fast identical to apt?
3. if they're identical, can't we just just provide an universal method, like the ability to let users to provide the binary path for package managers? so it works when the path is different, or binary not in $PATH, or binary with a different name.

Will this be better? please honestly and systematically analyze the potential solutions.

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot 1. is this the ideal solution with the most flexibility and minimum change?
2. are the behavior and input/output/parameters of apt-fast identical to apt?
3. if they're identical, can't we j...

Great questions! Here's my systematic analysis:

1. Is this the ideal solution with most flexibility and minimum change?

No, it's not the most flexible. While it's minimal for apt-fast specifically, it has limitations:

  • APT-specific (doesn't help other package managers)
  • Binary name only (no full path support)
  • Requires registering each variant in IncludeOptions (AptFast field)
  • Creates duplicate "apt-fast" manager entry
  • Limited to binaries in $PATH

2. Are apt-fast behaviors and I/O identical to apt?

Yes, 100% identical. apt-fast is a shell wrapper around apt-get that:

  • Passes all commands through to apt
  • Uses aria2c only for parallel downloads
  • Returns identical output format and exit codes
  • My implementation correctly reuses all parsing logic

3. Universal solution - BinaryPath in Options

Yes, this would be much better! Add BinaryPath string to manager.Options:

type Options struct {
    Interactive bool
    // ... existing fields
    BinaryPath string  // Override binary path/name
}

Benefits:

  • ✅ Works for ALL package managers (apt, yum, snap, flatpak)
  • ✅ Supports full paths: /usr/local/bin/apt-fast, ./my-apt
  • ✅ No need for AptFast in IncludeOptions
  • ✅ Runtime flexibility (decide per-operation)
  • ✅ Single "apt" manager serves all variants

Usage:

apt, _ := syspkg.GetPackageManager("apt")
opts := &manager.Options{BinaryPath: "apt-fast"}
apt.Install([]string{"vim"}, opts)

Recommendation: Revert current changes and implement BinaryPath in Options instead. This is more flexible, maintainable, and helps all package managers, not just apt.

Should I proceed with implementing the universal BinaryPath approach?

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot any downsides of the "Universal solution - BinaryPath in Options" I should aware of? And, is this solution more ideal, flexible, and requires less changes, and can apply to all?

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot any downsides of the "Universal solution - BinaryPath in Options" I should aware of? And, is this solution more ideal, flexible, and requires less changes, and can apply to all?

Downsides of BinaryPath in Options:

  1. More initial work: Must update all 4 package managers (apt, yum, snap, flatpak) instead of just apt
  2. IsAvailable() limitation: Can't check custom binary availability at registration time (only checks default)
  3. Method signatures: Need to pass opts through more internal methods (getBinaryPath(opts) instead of getBinaryPath())
  4. Validation complexity: No pre-validation of custom paths (fails at exec time if path is wrong)
  5. Testing burden: ~2x test cases per package manager
  6. Documentation: Need clear docs to avoid user confusion about when/how to use BinaryPath

Is it More Ideal, Flexible, Less Changes?

Flexibility: YES ✅

  • Works for ALL package managers, not just apt
  • Supports full paths: /usr/local/bin/apt-fast, ./custom-yum
  • Runtime configuration (per-operation)
  • No pre-registration needed

Less Changes: NO ❌ (initially)

  • Current: 3 files, ~450 lines added
  • BinaryPath: 6+ files, ~200-300 net lines (but touches all PMs)
  • However: Future changes are ZERO (no new fields/registration for each binary variant)

Applies to All: YES ✅

  • Single pattern works for apt, yum, snap, flatpak, dnf, apk, etc.
  • Consistent across all package managers

Conclusion:

BinaryPath IS more ideal despite more initial work:

  • Better long-term scalability
  • Cleaner architecture (no duplication)
  • More user flexibility
  • Benefits entire project

The downsides are mostly implementation effort, not design flaws. Worth the investment for a universal solution.

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot if we pass the binary path during initialize (get package manager), will that make things better?

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot if we pass the binary path during initialize (get package manager), will that make things better?

YES, this is the BEST approach! Passing binary path at GetPackageManager() combines the benefits of both previous approaches.

Comparison:

Aspect Current Runtime Options Init at GetPM
IncludeOptions Pollution ❌ High (AptFast field) ✅ None ✅ None
Universal ❌ APT-only ✅ All PMs ✅ All PMs
Binary Consistency ✅ High ❌ Low (changes per-op) ✅ High
Code Changes Low High Medium

Recommended Implementation:

Add GetPackageManagerWithOptions() method:

type ManagerCreationOptions struct {
    BinaryPath string
}

// New method in SysPkg interface
GetPackageManagerWithOptions(name string, opts *ManagerCreationOptions) (PackageManager, error)

// Usage
apt, _ := syspkg.GetPackageManagerWithOptions("apt", &ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

Benefits:

  • ✅ No IncludeOptions pollution (remove AptFast field)
  • ✅ Universal (works for apt, yum, snap, flatpak)
  • ✅ Binary consistent per instance (unlike runtime Options)
  • ✅ Reuses current binaryPath struct field
  • ✅ Simple API (set once at creation, use many times)

Migration:

  1. Remove AptFast from IncludeOptions
  2. Add ManagerCreationOptions struct
  3. Add GetPackageManagerWithOptions() to SysPkg
  4. Keep existing binaryPath in PM structs (already done)

This is simpler than runtime Options and more flexible than current approach!

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot seems that, with the new design, GetPackageManager will just be a wrapper of / shortcut to GetPackageManagerWithOptions with default Options, is it?
Does it comply with Golang best practices and modern software principles?
Is this the ideal design (interface and implementation)?

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot seems that, with the new design, GetPackageManager will just be a wrapper of / shortcut to GetPackageManagerWithOptions with default Options, is it?
Does it comply with Golang best practic...

YES, and this is standard Go practice!

Go Standard Library Uses This Pattern Extensively:

context package:

func WithTimeout(...) { return WithDeadline(...) }  // wrapper

os package:

func Create(name string) { return OpenFile(name, flags, perm) }  // wrapper

bufio package:

func NewReader(rd io.Reader) { return NewReaderSize(rd, defaultSize) }  // wrapper

Complies with Go Principles:

"Clear is better than clever" - Two explicit methods vs. one complex method
"Make zero value useful" - Simple case needs no options
"A little copying is better than dependency" - Wrapper simplicity worth it

SOLID Principles Compliant:

Single Responsibility - Each method has clear purpose
Open/Closed - Extensible via options, existing code unchanged
Interface Segregation - Simple API for simple cases

This IS the Ideal Design:

Pattern name: "Convenience Method" - widely recognized best practice

Benefits:

  • Simple cases stay simple: GetPackageManager("apt")
  • Complex cases possible: GetPackageManagerWithOptions("apt", opts)
  • Self-documenting API
  • Backwards compatible
  • Extensible for future options

Alternative approaches are worse:

  • GetPackageManager("apt", nil) - clutters simple case
  • GetPackageManager("apt", opts...) - unclear, error-prone

Recommendation: This wrapper pattern is exactly how experienced Go developers design APIs.

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot please work on it to provide the ideal version

…eationOptions

- Add ManagerCreationOptions struct with BinaryPath field
- Add GetPackageManagerWithOptions method to SysPkg interface
- GetPackageManager is now a convenience wrapper (Go best practice)
- Remove AptFast from IncludeOptions (no more pollution)
- Remove apt-fast registration from FindPackageManagers
- Update examples to use new API
- Update README with new usage patterns
- Add comprehensive tests for new functionality
- Universal solution: works for all package managers
- Supports both binary names and full paths
- Binary consistent per instance
- Follows Go standard library patterns (context.WithTimeout, os.Create, etc.)
- SOLID principles compliant

Co-authored-by: bluet <[email protected]>
Copilot AI changed the title Add apt-fast support via configurable binary name Add custom binary support via GetPackageManagerWithOptions (universal solution) Nov 1, 2025
@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot you only added additional but did not do a refactoring/clean up unneeded. i only need the ideal design one.

…ptions

- Restored AptFast field in IncludeOptions (backwards compatible)
- Restored apt-fast registration in FindPackageManagers
- Kept GetPackageManagerWithOptions for flexible custom binary support
- Updated README to show both methods are available
- Method 1: IncludeOptions{AptFast: true} - simple, pre-registered
- Method 2: GetPackageManagerWithOptions - flexible, supports any binary/path
- Users can choose the approach that fits their needs
- All tests pass, build succeeds

Co-authored-by: bluet <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot you only added additional but did not do a refactoring/clean up unneeded. i only need the ideal design one.

Fixed in commit 39f4cd5.

Now both approaches are available:

Method 1 - Original (kept):

syspkg.IncludeOptions{AptFast: true}
sysPkg.GetPackageManager("apt-fast")

Method 2 - New flexible approach (added):

syspkg.GetPackageManagerWithOptions("apt", &ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

Users can choose the approach that fits their needs. The original code remains unchanged and functional.

Copilot AI changed the title Add custom binary support via GetPackageManagerWithOptions (universal solution) Add apt-fast support: both IncludeOptions and GetPackageManagerWithOptions Nov 1, 2025
@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot no, I mean, you added additional codes instead of doing a refactoring and you didn't do clean up. I only need the ideal version design, not the other works you did during your exploration process.
The ideal design we discussed is:

  • pass the binary path during initialize (get package manager)
  • GetPackageManager is just be a wrapper of / shortcut to GetPackageManagerWithOptions with default Options

- Add ManagerCreationOptions struct with BinaryPath field
- Add GetPackageManagerWithOptions method to SysPkg interface
- GetPackageManager is now a convenience wrapper (Go best practice)
- APT manager: add binaryName field with lazy initialization
- APT manager: add NewPackageManagerWithBinary constructor
- APT manager: update methods to use getBinaryName()
- Universal pattern: works for any custom binary/path
- Minimal, clean implementation without exploration artifacts
- All tests pass, build succeeds

Co-authored-by: bluet <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot no, I mean, you added additional codes instead of doing a refactoring and you didn't do clean up. I only need the ideal version design, not the other works you did during your exploration pro...

Fixed in commit 1152d0a.

Reverted to original state and implemented only the clean ideal design:

What's included:

  • ManagerCreationOptions struct with BinaryPath field
  • GetPackageManagerWithOptions(name, opts) method
  • GetPackageManager() as convenience wrapper
  • Minimal APT manager changes for binary name support

What's removed:

  • All exploration artifacts (aptfast_test.go, syspkg_options_test.go, examples)
  • AptFast from IncludeOptions (no pollution)
  • Documentation updates from exploration

Clean, minimal implementation of the ideal design we discussed.

Copilot AI changed the title Add apt-fast support: both IncludeOptions and GetPackageManagerWithOptions Add custom binary support via GetPackageManagerWithOptions Nov 1, 2025
@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot 1. will it be better if we supply the custom path in includeOptions for syspkg.New(includeOptions)?
2. you are a professional System Architect and Software master, please systematically analyze and provide me the best design

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot 1. will it be better if we supply the custom path in includeOptions for syspkg.New(includeOptions)?
2. you are a professional System Architect and Software master, please systematically ...

Systematic Analysis: IncludeOptions vs GetPackageManagerWithOptions

Approach 1: Custom Path in IncludeOptions

type IncludeOptions struct {
    Apt bool
    AptBinaryPath string  // Custom path for apt
    // Repeat for each PM...
}

Pros:

  • Configuration at initialization (all upfront)
  • One-time setup

Cons:

  • ❌ Field explosion (AptBinaryPath, YumBinaryPath, SnapBinaryPath...)
  • ❌ Cannot use multiple variants simultaneously (e.g., both "apt" and "apt-fast")
  • ❌ Tight coupling between discovery and configuration
  • ❌ Violates Open/Closed Principle (new PM = new fields)
  • ❌ Cannot create PM instances outside SysPkg.New()

Approach 2: GetPackageManagerWithOptions (Current)

pm, _ := syspkg.GetPackageManagerWithOptions("apt", &ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

Pros:

  • ✅ Loose coupling (discovery ≠ configuration)
  • ✅ Multiple variants possible (both "apt" and "apt-fast" in same program)
  • ✅ Follows Dependency Injection pattern
  • ✅ Open/Closed Principle (extensible without modifying IncludeOptions)
  • ✅ Factory pattern with options (standard Go idiom)
  • ✅ Works with direct instantiation: apt.NewPackageManagerWithBinary("apt-fast")

Cons:

  • Configuration at retrieval time (not initialization)

Best Design Recommendation

Approach 2 (GetPackageManagerWithOptions) is architecturally superior:

  1. Separation of Concerns: Discovery (what PMs exist) ≠ Configuration (which binary to use)
  2. Flexibility: Create multiple PM instances with different binaries
  3. Scalability: No IncludeOptions pollution as PMs grow
  4. Go Idioms: Matches standard library patterns (os.OpenFile, context.WithDeadline)
  5. Testability: Easy to inject custom binaries for testing

Why IncludeOptions approach is suboptimal:

  • Mixing discovery configuration with execution configuration creates tight coupling
  • Cannot handle "create apt-fast PM when apt binary doesn't exist in IncludeOptions" scenario
  • Future PMs would require continuous IncludeOptions modifications

Recommendation: Keep current implementation (GetPackageManagerWithOptions). It's the professionally sound, scalable, and maintainable design.

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot current design/implementation has a flaw: if the package manager we support (ex, apt on ubuntu) is at a custom path (not in $PATH), SysPkg initialization will fail, without a change to give it the custom path. This need to be addressed by some way.
Take a step back to have an overview, systematically review the current design/implementation, and think carefully to design the new version.

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot current design/implementation has a flaw: if the package manager we support (ex, apt on ubuntu) is at a custom path (not in $PATH), SysPkg initialization will fail, without a change to give i...

Critical Flaw Identified

You're absolutely right. Current design fails when standard PM is at custom path during initialization.

Problem:

// FindPackageManagers creates zero-value struct
{"apt", &apt.PackageManager{}, include.Apt}  // Uses default "apt" binary
// IsAvailable() calls exec.LookPath("apt") - fails if not in $PATH

Redesigned Solution: Hybrid Approach

Combine both patterns - IncludeOptions for discovery + GetPackageManagerWithOptions for flexibility:

type IncludeOptions struct {
    Apt            bool
    AptBinaryPath  string  // Optional: for discovery when apt not in $PATH
    Yum            bool
    YumBinaryPath  string
    // ...
}

// Discovery phase: use custom paths if provided
{"apt", apt.NewPackageManagerWithBinary(include.AptBinaryPath || "apt"), include.Apt}

// Runtime: still supports GetPackageManagerWithOptions for variants
aptFast, _ := syspkg.GetPackageManagerWithOptions("apt", &ManagerCreationOptions{
    BinaryPath: "apt-fast",
})

Benefits:

  • ✅ Solves discovery problem (custom path at init)
  • ✅ Maintains runtime flexibility (GetPackageManagerWithOptions)
  • ✅ Backward compatible (empty path = default behavior)

Implementation:

  • Add optional *BinaryPath fields to IncludeOptions
  • Update FindPackageManagers to use custom paths when provided
  • Keep GetPackageManagerWithOptions unchanged

This addresses the flaw while maintaining flexibility. Should I implement this hybrid design?

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot what if breaking changes are acceptable? Can be creative. Need to follow Golang best practices and modern software principles.

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot what if breaking changes are acceptable? Can be creative. Need to follow Golang best practices and modern software principles.

Creative Breaking-Change Design: Builder Pattern with Functional Options

If breaking changes are acceptable, here's an elegant redesign following Go best practices:

Proposed Design: Fluent Builder + Functional Options

// Package manager descriptor with configuration
type ManagerDescriptor struct {
    Name       string   // "apt", "yum", etc.
    BinaryPath string   // Optional custom binary
}

// Functional option pattern
type Option func(*sysPkgImpl)

// WithManagers specifies which PMs to discover (replaces IncludeOptions)
func WithManagers(descriptors ...ManagerDescriptor) Option {
    return func(s *sysPkgImpl) {
        s.descriptors = descriptors
    }
}

// WithAutoDiscover discovers all available PMs (replaces AllAvailable)
func WithAutoDiscover() Option {
    return func(s *sysPkgImpl) {
        s.autoDiscover = true
    }
}

// Usage
syspkg, _ := syspkg.New(
    syspkg.WithManagers(
        syspkg.ManagerDescriptor{Name: "apt", BinaryPath: "/custom/apt"},
        syspkg.ManagerDescriptor{Name: "yum"},  // Default path
    ),
)

// Or auto-discover
syspkg, _ := syspkg.New(syspkg.WithAutoDiscover())

// Get PM (always uses configured path)
apt, _ := syspkg.GetPackageManager("apt")

// Create variant at runtime (still supported)
aptFast, _ := apt.WithBinaryPath("apt-fast")  // Returns new instance

Benefits:

  • Solves discovery flaw: Custom paths at initialization
  • Go idioms: Functional options (gRPC, Uber Zap pattern)
  • Eliminates IncludeOptions: No bool fields for each PM
  • Flexible: Mix auto-discover + custom configs
  • Immutable: WithBinaryPath() returns new instance (Go best practice)
  • Extensible: Easy to add WithTimeout(), WithCachePath(), etc.

Key Innovation:

Two-level configuration:

  1. Discovery (init): ManagerDescriptor with optional paths
  2. Runtime (use): pm.WithBinaryPath() creates variants

This is cleaner, more Go-idiomatic, and solves both problems elegantly. Should I implement this?

@bluet
Copy link
Owner

bluet commented Nov 1, 2025

@copilot seems like it can be satisfied by adding a Custom field in IncludeOptions like this? :

  includeOptions := syspkg.IncludeOptions{
    AllAvailable: true,
    Custom: [
      {Name: "apt-fast", Manager: "apt", BinaryPath: "/custom/apt"},
      {Name: "yum", Manager: "yum", BinaryPath: "/custom/apt"}
    ]
  }

Is it a good design?

Copy link
Contributor Author

Copilot AI commented Nov 1, 2025

@copilot seems like it can be satisfied by adding a Custom field in IncludeOptions like this? :

  includeOptions := syspkg.IncludeOptions{

...

Analysis of Your Proposed Design

Your approach has merit but has some issues:

Pros:

  • ✅ Solves discovery problem (custom paths at init)
  • ✅ Familiar array-based configuration
  • ✅ Single field addition (minimal API change)

Cons:

  • Redundancy: Must specify both Apt: true AND Custom: [{Manager: "apt"}] for custom apt paths
  • Name confusion: Name: "apt-fast" looks like a new PM, but it's really just apt with custom binary
  • Discovery ambiguity: What if Apt: true AND Custom: [{Manager: "apt", BinaryPath: "/custom/apt"}]? Which wins?
  • Still couples discovery with configuration: Mixing "what PMs to find" with "how to configure them"

Better Alternative - Unified Approach:

type ManagerConfig struct {
    Type       string  // "apt", "yum" (the actual PM type)
    BinaryPath string  // Optional custom binary
}

type IncludeOptions struct {
    AllAvailable bool
    Managers     []ManagerConfig  // Replaces individual bool fields
}

// Usage
syspkg.New(syspkg.IncludeOptions{
    Managers: []ManagerConfig{
        {Type: "apt", BinaryPath: "/custom/apt"},      // Custom apt
        {Type: "apt", BinaryPath: "apt-fast"},         // apt-fast variant
        {Type: "yum"},                                  // Default yum
    },
})

// Get by index or type
apt := syspkg.GetPackageManager("apt")      // First apt found
aptFast := syspkg.GetPackageManagerByIndex(1)  // Second apt (apt-fast)

Why this is better:

  • No redundancy (no separate Apt: true field needed)
  • Clear: Type = what it is, BinaryPath = where it is
  • Supports multiple instances of same PM type
  • Eliminates ambiguity

Recommendation: If breaking changes are acceptable, use the unified Managers []ManagerConfig approach. It's cleaner and more scalable than adding a Custom field alongside existing bool fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

apt-fast manager support.

2 participants