mirror of
https://github.com/safedep/pmg.git
synced 2026-08-03 07:24:09 +02:00
Add Pip3 support (#75)
* add pip3 support * add pip3 e2e * chore: rename var
This commit is contained in:
@@ -187,6 +187,29 @@ jobs:
|
|||||||
deactivate
|
deactivate
|
||||||
cd .. && rm -rf pip-test
|
cd .. && rm -rf pip-test
|
||||||
|
|
||||||
|
- name: Test Pip3 - Single Package & Manifest
|
||||||
|
run: |
|
||||||
|
echo "Testing Pip3 single package installation..."
|
||||||
|
mkdir pip3-test && cd pip3-test
|
||||||
|
python -m venv venv && source venv/bin/activate
|
||||||
|
pmg pip3 install requests
|
||||||
|
pmg pip3 install numpy==1.24.0
|
||||||
|
pmg pip3 freeze > requirements.txt
|
||||||
|
|
||||||
|
# Verification: requirements.txt contains expected packages
|
||||||
|
test -s requirements.txt
|
||||||
|
grep -E '^requests==' requirements.txt
|
||||||
|
grep -E '^numpy==' requirements.txt
|
||||||
|
|
||||||
|
echo "Testing Pip3 manifest installation..."
|
||||||
|
pmg pip3 uninstall -y requests numpy
|
||||||
|
pmg pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
# Verification: imported packages are available in the environment
|
||||||
|
python -c "import requests, numpy; print(requests.__version__); print(numpy.__version__)"
|
||||||
|
deactivate
|
||||||
|
cd .. && rm -rf pip3-test
|
||||||
|
|
||||||
- name: Test UV - Single Package & Manifest
|
- name: Test UV - Single Package & Manifest
|
||||||
run: |
|
run: |
|
||||||
echo "Testing UV single package installation..."
|
echo "Testing UV single package installation..."
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package pypi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/safedep/pmg/config"
|
||||||
|
"github.com/safedep/pmg/internal/analytics"
|
||||||
|
"github.com/safedep/pmg/internal/flows"
|
||||||
|
"github.com/safedep/pmg/internal/ui"
|
||||||
|
"github.com/safedep/pmg/packagemanager"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPip3Command() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "pip3 [action] [package]",
|
||||||
|
Short: "Guard pip3 package manager",
|
||||||
|
DisableFlagParsing: true,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
err := executePip3Flow(cmd.Context(), args)
|
||||||
|
if err != nil {
|
||||||
|
ui.ErrorExit(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executePip3Flow(ctx context.Context, args []string) error {
|
||||||
|
analytics.TrackCommandPip3()
|
||||||
|
packageManager, err := packagemanager.NewPypiPackageManager(packagemanager.DefaultPip3PackageManagerConfig())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create pip3 package manager proxy: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := config.FromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedCommand, err := packageManager.ParseCommand(args)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the args right here
|
||||||
|
packageResolverConfig := packagemanager.NewDefaultPypiDependencyResolverConfig()
|
||||||
|
packageResolverConfig.IncludeTransitiveDependencies = config.Transitive
|
||||||
|
packageResolverConfig.TransitiveDepth = config.TransitiveDepth
|
||||||
|
packageResolverConfig.IncludeDevDependencies = config.IncludeDevDependencies
|
||||||
|
packageResolverConfig.PackageInstallTargets = parsedCommand.InstallTargets
|
||||||
|
|
||||||
|
packageResolver, err := packagemanager.NewPypiDependencyResolver(packageResolverConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create dependency resolver: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return flows.Common(packageManager, packageResolver, config).Run(ctx, args, parsedCommand)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ const (
|
|||||||
Npm PackageManagerName = "npm"
|
Npm PackageManagerName = "npm"
|
||||||
Pnpm PackageManagerName = "pnpm"
|
Pnpm PackageManagerName = "pnpm"
|
||||||
Pip PackageManagerName = "pip"
|
Pip PackageManagerName = "pip"
|
||||||
|
Pip3 PackageManagerName = "pip3"
|
||||||
Bun PackageManagerName = "bun"
|
Bun PackageManagerName = "bun"
|
||||||
Yarn PackageManagerName = "yarn"
|
Yarn PackageManagerName = "yarn"
|
||||||
Uv PackageManagerName = "uv"
|
Uv PackageManagerName = "uv"
|
||||||
@@ -45,6 +46,7 @@ func NewExtractorManager() *ExtractorManager {
|
|||||||
Yarn: &YarnExtractor{},
|
Yarn: &YarnExtractor{},
|
||||||
Uv: &UvExtractor{},
|
Uv: &UvExtractor{},
|
||||||
Poetry: &PoetryExtractor{},
|
Poetry: &PoetryExtractor{},
|
||||||
|
Pip3: &Pip3Extractor{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-2
@@ -19,8 +19,8 @@ func (p *PipExtractor) GetPackageManager() PackageManagerName {
|
|||||||
return Pip
|
return Pip
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *PipExtractor) Extract(lockfilePath, scanDir string) ([]*packagev1.PackageVersion, error) {
|
func (p *PipExtractor) Extract(lockfilePath, scanDir string) ([]*packagev1.PackageVersion, error) {
|
||||||
return parseLockfile(lockfilePath, scanDir, n.GetEcosystem())
|
return parseLockfile(lockfilePath, scanDir, p.GetEcosystem())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UvExtractor handles uv.lock files
|
// UvExtractor handles uv.lock files
|
||||||
@@ -60,3 +60,22 @@ func (p *PoetryExtractor) GetPackageManager() PackageManagerName {
|
|||||||
func (p *PoetryExtractor) Extract(lockfilePath, scanDir string) ([]*packagev1.PackageVersion, error) {
|
func (p *PoetryExtractor) Extract(lockfilePath, scanDir string) ([]*packagev1.PackageVersion, error) {
|
||||||
return parseLockfile(lockfilePath, scanDir, p.GetEcosystem())
|
return parseLockfile(lockfilePath, scanDir, p.GetEcosystem())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pip3Extractor handles requirements.txt files
|
||||||
|
type Pip3Extractor struct{}
|
||||||
|
|
||||||
|
func (p *Pip3Extractor) GetSupportedFiles() []string {
|
||||||
|
return []string{"requirements.txt"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pip3Extractor) GetEcosystem() packagev1.Ecosystem {
|
||||||
|
return packagev1.Ecosystem_ECOSYSTEM_PYPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pip3Extractor) GetPackageManager() PackageManagerName {
|
||||||
|
return Pip3
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pip3Extractor) Extract(lockfilePath, scanDir string) ([]*packagev1.PackageVersion, error) {
|
||||||
|
return parseLockfile(lockfilePath, scanDir, p.GetEcosystem())
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const (
|
|||||||
eventCommandPnpm = "pmg_command_pnpm"
|
eventCommandPnpm = "pmg_command_pnpm"
|
||||||
eventCommandYarn = "pmg_command_yarn"
|
eventCommandYarn = "pmg_command_yarn"
|
||||||
eventCommandPip = "pmg_command_pip"
|
eventCommandPip = "pmg_command_pip"
|
||||||
|
eventCommandPip3 = "pmg_command_pip3"
|
||||||
eventCommandUv = "pmg_command_uv"
|
eventCommandUv = "pmg_command_uv"
|
||||||
eventCommandPoetry = "pmg_command_poetry"
|
eventCommandPoetry = "pmg_command_poetry"
|
||||||
|
|
||||||
@@ -39,6 +40,10 @@ func TrackCommandPip() {
|
|||||||
TrackEvent(eventCommandPip)
|
TrackEvent(eventCommandPip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TrackCommandPip3() {
|
||||||
|
TrackEvent(eventCommandPip3)
|
||||||
|
}
|
||||||
|
|
||||||
func TrackCommandUv() {
|
func TrackCommandUv() {
|
||||||
TrackEvent(eventCommandUv)
|
TrackEvent(eventCommandUv)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ func main() {
|
|||||||
cmd.AddCommand(npm.NewBunCommand())
|
cmd.AddCommand(npm.NewBunCommand())
|
||||||
cmd.AddCommand(npm.NewYarnCommand())
|
cmd.AddCommand(npm.NewYarnCommand())
|
||||||
cmd.AddCommand(pypi.NewPipCommand())
|
cmd.AddCommand(pypi.NewPipCommand())
|
||||||
|
cmd.AddCommand(pypi.NewPip3Command())
|
||||||
cmd.AddCommand(pypi.NewUvCommand())
|
cmd.AddCommand(pypi.NewUvCommand())
|
||||||
cmd.AddCommand(pypi.NewPoetryCommand())
|
cmd.AddCommand(pypi.NewPoetryCommand())
|
||||||
cmd.AddCommand(version.NewVersionCommand())
|
cmd.AddCommand(version.NewVersionCommand())
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ func DefaultPipPackageManagerConfig() PypiPackageManagerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DefaultPip3PackageManagerConfig() PypiPackageManagerConfig {
|
||||||
|
return PypiPackageManagerConfig{
|
||||||
|
InstallCommands: []string{"install"},
|
||||||
|
CommandName: "pip3",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func DefaultUvPackageManagerConfig() PypiPackageManagerConfig {
|
func DefaultUvPackageManagerConfig() PypiPackageManagerConfig {
|
||||||
return PypiPackageManagerConfig{
|
return PypiPackageManagerConfig{
|
||||||
InstallCommands: []string{"add", "install"},
|
InstallCommands: []string{"add", "install"},
|
||||||
@@ -58,6 +65,8 @@ func NewPypiPackageManager(config PypiPackageManagerConfig) (*pypiPackageManager
|
|||||||
parser = NewUVCommandParser(config)
|
parser = NewUVCommandParser(config)
|
||||||
case "poetry":
|
case "poetry":
|
||||||
parser = NewPoetryCommandParser(config)
|
parser = NewPoetryCommandParser(config)
|
||||||
|
case "pip3":
|
||||||
|
parser = NewPipCommandParser(config)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported package manager: %s", config.CommandName)
|
return nil, fmt.Errorf("unsupported package manager: %s", config.CommandName)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user