141 lines
4.8 KiB
Markdown
141 lines
4.8 KiB
Markdown
# go-package-manager
|
|
|
|
[](https://pkg.go.dev/github.com/grmrgecko/go-package-manager)
|
|
|
|
`pkgmgr` is a Go library that abstracts system package managers behind a single
|
|
[`Manager`](managers.go) interface. One code path can install, upgrade, search,
|
|
and manage repositories on Debian, Fedora, openSUSE, Arch, Alpine, macOS, and
|
|
more, without each caller hard-coding `apt`/`dnf`/`pacman` invocations.
|
|
|
|
API documentation is available on [pkg.go.dev](https://pkg.go.dev/github.com/grmrgecko/go-package-manager).
|
|
|
|
## Supported managers
|
|
|
|
| Manager | Type | Format | Notes |
|
|
| --------- | --------- | ------- | ------------------------------------------------ |
|
|
| zypper | root | rpm | openSUSE |
|
|
| dnf | root | rpm | Fedora / RHEL |
|
|
| yum | root | rpm | Legacy RHEL |
|
|
| apt | root | deb | Debian / Ubuntu |
|
|
| apt-get | root | deb | Debian / Ubuntu |
|
|
| pacman | root | pacman | Arch |
|
|
| apk | root | apk | Alpine |
|
|
| brew | unpriv. | bottle | Homebrew; refuses to run as root |
|
|
| yay | unpriv. | pacman | AUR helper; pacman drop-in |
|
|
| paru | unpriv. | pacman | AUR helper; pacman drop-in |
|
|
|
|
Root-type managers escalate privileged commands through a configurable wrapper
|
|
(`sudo` by default). Homebrew and the AUR helpers must not run as root, so they
|
|
bypass the wrapper and instead drop to an unprivileged user via `sudo -u` when
|
|
the process is running as root.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
go get github.com/grmrgecko/go-package-manager
|
|
```
|
|
|
|
## Usage
|
|
|
|
Resolve the active system manager, then drive it through the interface. The
|
|
example below installs a package, escalating with `sudo` when not already root.
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
pkgmgr "github.com/grmrgecko/go-package-manager"
|
|
)
|
|
|
|
func main() {
|
|
m := pkgmgr.GetSystemManager()
|
|
if m == nil {
|
|
log.Fatal("no supported package manager found")
|
|
}
|
|
|
|
// Wrap privileged commands with sudo when not running as root.
|
|
m.UseSudoWhenNeeded()
|
|
|
|
ctx := context.Background()
|
|
if err := m.Sync(ctx, nil); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := m.Install(ctx, nil, "htop"); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
results, err := m.Search(ctx, nil, "editor")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for _, r := range results {
|
|
log.Printf("%s %s - %s", r.Name, r.Version, r.Summary)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Selecting a manager explicitly
|
|
|
|
`GetSystemManager` auto-detects in priority order. To target a specific manager,
|
|
construct it directly:
|
|
|
|
```go
|
|
m := &pkgmgr.Apt{}
|
|
m.UseSudoWhenNeeded()
|
|
```
|
|
|
|
AUR helpers must be constructed through their constructors so the helper binary
|
|
name is set:
|
|
|
|
```go
|
|
yay := pkgmgr.NewYay()
|
|
paru := pkgmgr.NewParu()
|
|
```
|
|
|
|
## Interface
|
|
|
|
The `Manager` interface covers the full package lifecycle. Methods that perform
|
|
I/O take a `context.Context` for cancellation and timeouts.
|
|
|
|
- **Identity:** `Name`, `Format`, `Path`
|
|
- **Privilege:** `SetCmdWrapper`, `UseSudoWhenNeeded`
|
|
- **I/O:** `SetIO` (override stdin/stdout/stderr of spawned commands)
|
|
- **Repos:** `AddRepo`, `AddRepoURL`, `RemoveRepo`, `GetRepo`, `ListRepos`
|
|
- **Keys:** `AddRepoKey`, `AddRepoKeyFile`, `AddRepoKeyURL`
|
|
- **Packages:** `Sync`, `Install`, `Remove`, `Upgrade`, `InstallFile`,
|
|
`UpgradeAll`, `Clean`
|
|
- **Queries:** `Search`, `Info`, `ListInstalled`, `ListUpgradable`
|
|
|
|
`Search` returns structured `SearchResult` values; `Info` returns the underlying
|
|
tool's native text output; `ListInstalled` and `ListUpgradable` return
|
|
`name -> version` maps. `ListRepos` returns every configured repo as a
|
|
`name -> configuration` map, where each value matches what `GetRepo` returns for
|
|
that name.
|
|
|
|
## Privilege handling
|
|
|
|
Two mechanisms cover opposite requirements:
|
|
|
|
- **Root managers** (`apt`, `dnf`, `pacman`, ...) need root for mutating
|
|
operations. `UseSudoWhenNeeded` sets the wrapper to `sudo` when the process is
|
|
not already root.
|
|
- **Non-root managers** (`brew`, `yay`, `paru`) refuse to run as root. They use
|
|
an embedded `dropPrivilege` to run their command as an unprivileged user,
|
|
dropping via `sudo -u` only when the process is root. The drop user defaults to
|
|
`SUDO_USER` and can be set explicitly with `SetDropUser`.
|
|
|
|
A custom command wrapper (for example `[]string{"sudo", "-n"}`) can be supplied
|
|
through `SetCmdWrapper`.
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
go test ./...
|
|
```
|
|
|
|
The suite verifies that every manager satisfies the `Manager` interface and
|
|
exercises the command wrapper, I/O overrides, privilege drop logic, and the
|
|
output parsers.
|