Skip to content

Commit

Permalink
Merge pull request #59 from DepshubHQ/24-add-pip-support
Browse files Browse the repository at this point in the history
Add pip support
  • Loading branch information
semanser authored Dec 17, 2024
2 parents 74427ed + 36a3e13 commit 7ba2907
Show file tree
Hide file tree
Showing 23 changed files with 420 additions and 16 deletions.
2 changes: 1 addition & 1 deletion internal/linter/rules/allowed_licenses.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewRuleAllowedLicenses() *RuleAllowedLicenses {
return &RuleAllowedLicenses{
name: "allowed-licenses",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultAllowedLicenses,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/lockfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleLockfile() *RuleLockfile {
return &RuleLockfile{
name: "lockfile",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/max_libyear.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func NewRuleMaxLibyear() *RuleMaxLibyear {
return &RuleMaxLibyear{
name: "max-libyear",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultMaxLibyear,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/max_major_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewRuleMaxMajorUpdates() *RuleMaxMajorUpdates {
return &RuleMaxMajorUpdates{
name: "max-major-updates",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultMaxMajorUpdatesPercent,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/max_minor_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewRuleMaxMinorUpdates() *RuleMaxMinorUpdates {
return &RuleMaxMinorUpdates{
name: "max-minor-updates",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultMaxMinorUpdatesPercent,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/max_package_age.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func NewRuleMaxPackageAge() *RuleMaxPackageAge {
return &RuleMaxPackageAge{
name: "max-package-age",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultMaxPackageAge,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/max_patch_updates.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewRuleMaxPatchUpdates() *RuleMaxPatchUpdates {
return &RuleMaxPatchUpdates{
name: "max-patch-updates",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
value: DefaultMaxPatchUpdatesPercent,
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_any_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleNoAnyTag() *RuleNoAnyTag {
return &RuleNoAnyTag{
name: "no-any-tag",
level: LevelWarning,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_deprecated.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleNoDeprecated() *RuleNoDeprecated {
return &RuleNoDeprecated{
name: "no-deprecated",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_duplicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleNoDuplicates() *RuleNoDuplicates {
return &RuleNoDuplicates{
name: "no-duplicates",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_multiple_versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleNoMultipleVersions() *RuleNoMultipleVersions {
return &RuleNoMultipleVersions{
name: "no-multiple-versions",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_pre_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func NewRuleNoPreRelease() *RuleNoPreRelease {
return &RuleNoPreRelease{
name: "no-pre-release",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/no_unstable.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func NewRuleNoUnstable() *RuleNoUnstable {
return &RuleNoUnstable{
name: "no-unstable",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Cargo, types.Pip},
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/linter/rules/sorted.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewRuleSorted() *RuleSorted {
return &RuleSorted{
name: "sorted",
level: LevelError,
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Cargo},
supported: []types.ManagerType{types.Npm, types.Go, types.Cargo, types.Cargo, types.Pip},
}
}

Expand Down
109 changes: 109 additions & 0 deletions pkg/manager/pip/pip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package pip

import (
"bufio"
"fmt"
"github.com/depshubhq/depshub/pkg/types"
"os"
"path/filepath"
"regexp"
"strings"
)

type Pip struct{}

func (Pip) GetType() types.ManagerType {
return types.Pip
}

func (Pip) Managed(path string) bool {
return filepath.Base(path) == "requirements.txt"
}

func (Pip) Dependencies(path string) ([]types.Dependency, error) {
var dependencies []types.Dependency

file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()

scanner := bufio.NewScanner(file)
lineNum := 0

// Regex patterns for parsing requirements.txt entries
commentPattern := regexp.MustCompile(`^\s*#`)
versionPattern := regexp.MustCompile(`^([^=<>!~]+)(==|>=|<=|!=|~=|>|<)(.+)`)

for scanner.Scan() {
lineNum++
line := scanner.Text()

// Skip empty lines and comments
if line == "" || commentPattern.MatchString(line) {
continue
}

// Handle -r requirements inclusion
if strings.HasPrefix(line, "-r ") {
continue // Skip requirements file inclusions for now
}

// Extract package name and version
var name, version string
if matches := versionPattern.FindStringSubmatch(line); matches != nil {
name = strings.TrimSpace(matches[1])
version = cleanVersion(matches[3])
} else {
// Package with no version specified
name = strings.TrimSpace(line)
version = ""
}

dependencies = append(dependencies, types.Dependency{
Manager: types.Pip,
Name: name,
Version: version,
Dev: false,
Definition: types.Definition{
Path: path,
RawLine: line,
Line: lineNum,
},
})
}

if err := scanner.Err(); err != nil {
return nil, err
}

return dependencies, nil
}

// Returns the version without any prefix or suffix
func cleanVersion(version string) string {
version = strings.TrimSpace(version)

// Remove any comments that might be at the end
if idx := strings.Index(version, "#"); idx != -1 {
version = version[:idx]
}

version = strings.Trim(version, "^~*><= ")

return version
}

func (Pip) LockfilePath(path string) (string, error) {
// Check for requirements.txt in the same directory
lockfilePath := filepath.Join(filepath.Dir(path), "requirements.lock")
if _, err := os.Stat(lockfilePath); os.IsNotExist(err) {
// Some projects use pip-lock instead
lockfilePath = filepath.Join(filepath.Dir(path), "pip.lock")
if _, err := os.Stat(lockfilePath); os.IsNotExist(err) {
return "", fmt.Errorf("lockfile not found")
}
}
return lockfilePath, nil
}
Loading

0 comments on commit 7ba2907

Please sign in to comment.