mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
feat: add Linux system-wide setup
Install shared shims, managed configuration, and login-shell PATH integration so golden images and multi-user hosts can protect package installs for every user. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package shim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/pmg/internal/alias"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSystemBinDir = "/usr/local/lib/pmg/bin"
|
||||
defaultSystemProfilePath = "/etc/profile.d/pmg.sh"
|
||||
systemProfileMarker = "PMG system shims"
|
||||
)
|
||||
|
||||
// systemBinDirOverride and systemProfilePathOverride replace the OS-level
|
||||
// system install paths. They exist only for tests within this package. There
|
||||
// is intentionally no env var or flag for them.
|
||||
var (
|
||||
systemBinDirOverride string
|
||||
systemProfilePathOverride string
|
||||
)
|
||||
|
||||
// SystemBinDir returns the directory for system-wide PMG shims.
|
||||
func SystemBinDir() string {
|
||||
if systemBinDirOverride != "" {
|
||||
return systemBinDirOverride
|
||||
}
|
||||
return defaultSystemBinDir
|
||||
}
|
||||
|
||||
// SystemProfilePath returns the path of the system profile.d snippet.
|
||||
func SystemProfilePath() string {
|
||||
if systemProfilePathOverride != "" {
|
||||
return systemProfilePathOverride
|
||||
}
|
||||
return defaultSystemProfilePath
|
||||
}
|
||||
|
||||
// NewSystemShimManager creates a shim manager for system-wide install: shims
|
||||
// under SystemBinDir, no per-user rc edits, and /etc/profile.d management.
|
||||
func NewSystemShimManager() (*ShimManager, error) {
|
||||
aliasCfg := alias.DefaultConfig()
|
||||
pmgBin, err := currentExecutable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ShimManager{
|
||||
config: ShimConfig{
|
||||
BinDir: SystemBinDir(),
|
||||
PMGBin: pmgBin,
|
||||
PackageManagers: aliasCfg.PackageManagers,
|
||||
SkipShellRc: true,
|
||||
ManageProfile: true,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SystemShimsInstalled reports whether the system shim directory contains at
|
||||
// least one shim script.
|
||||
func SystemShimsInstalled() bool {
|
||||
return shimsPresent(SystemBinDir())
|
||||
}
|
||||
|
||||
func shimsPresent(dir string) bool {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, e := range entries {
|
||||
if !e.IsDir() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SystemProfileInstalled reports whether the system profile snippet exists and
|
||||
// contains the PMG marker.
|
||||
func SystemProfileInstalled() bool {
|
||||
data, err := os.ReadFile(SystemProfilePath())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(string(data), systemProfileMarker)
|
||||
}
|
||||
|
||||
func writeSystemProfile() error {
|
||||
binDir := SystemBinDir()
|
||||
path := SystemProfilePath()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return fmt.Errorf("failed to create profile.d directory: %w", err)
|
||||
}
|
||||
|
||||
if data, err := os.ReadFile(path); err == nil && strings.Contains(string(data), systemProfileMarker) {
|
||||
return nil
|
||||
}
|
||||
|
||||
content := fmt.Sprintf(`# %s - managed by pmg setup install --system
|
||||
# remove by running: pmg setup remove --system
|
||||
export PATH="%s:$PATH"
|
||||
`, systemProfileMarker, binDir)
|
||||
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
return fmt.Errorf("failed to write system profile %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeSystemProfile() error {
|
||||
path := SystemProfilePath()
|
||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to remove system profile %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user