Skip to content

Commit

Permalink
Better lockfile generation withpip-compile (#1075)
Browse files Browse the repository at this point in the history
* Specify manifest path in pip-compile

* Recognize more python requirements filenames

* Note Python changes in CHANGELOG

* Allow `phylum parse -t pip pyproject.toml`

Keep `poetry` as the default generator for `pyproject.toml` files
because it is very common. But allow the user to use `-t pip` for other
backends (requires `pip-compile`)
  • Loading branch information
kylewillmon authored May 9, 2023
1 parent 04bfc4f commit 295d30a
Show file tree
Hide file tree
Showing 18 changed files with 55 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added
- Support more Python manifest files: `requirements.in`, `setup.py`, `setup.cfg`
- Recognize all `requirements*.txt` file names as Python lockfiles

## [5.1.0] - 2023-05-04

### Added
Expand Down
5 changes: 1 addition & 4 deletions cli/src/commands/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,7 @@ pub fn parse_lockfile(
eprintln!("Generating lockfile for manifest {path:?} using {format:?}…");

// Generate a new lockfile.
let canonicalized = fs::canonicalize(&path)?;
let project_path =
canonicalized.parent().ok_or_else(|| anyhow!("invalid manifest path: {path:?}"))?;
let generated_lockfile = generator.generate_lockfile(project_path)?;
let generated_lockfile = generator.generate_lockfile(&path)?;

// Parse the generated lockfile.
let packages = parse_lockfile_content(&generated_lockfile, parser)?;
Expand Down
2 changes: 1 addition & 1 deletion docs/command_line_tool/phylum_analyze.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Usage: phylum analyze [OPTIONS] [LOCKFILE]...

-t, --lockfile-type <type>
&emsp; Lock file type used for all lock files (default: auto)
&emsp; Accepted values: `npm`, `yarn`, `gem`, `pip`, `pipenv`, `poetry`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`
&emsp; Accepted values: `npm`, `yarn`, `gem`, `poetry`, `pip`, `pipenv`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`

-v, --verbose...
&emsp; Increase the level of verbosity (the maximum is -vvv)
Expand Down
2 changes: 1 addition & 1 deletion docs/command_line_tool/phylum_init.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Usage: phylum init [OPTIONS] [PROJECT_NAME]

-t, --lockfile-type <type>
&emsp; Lock file type used for all lock files (default: auto)
&emsp; Accepted values: `npm`, `yarn`, `gem`, `pip`, `pipenv`, `poetry`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`
&emsp; Accepted values: `npm`, `yarn`, `gem`, `poetry`, `pip`, `pipenv`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`

-f, --force
&emsp; Overwrite existing configurations without confirmation
Expand Down
2 changes: 1 addition & 1 deletion docs/command_line_tool/phylum_parse.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Usage: phylum parse [OPTIONS] [LOCKFILE]...

-t, --lockfile-type <type>
&emsp; Lock file type used for all lock files (default: auto)
&emsp; Accepted values: `npm`, `yarn`, `gem`, `pip`, `pipenv`, `poetry`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`
&emsp; Accepted values: `npm`, `yarn`, `gem`, `poetry`, `pip`, `pipenv`, `mvn`, `gradle`, `nuget`, `go`, `cargo`, `spdx`, `auto`

-v, --verbose...
&emsp; Increase the level of verbosity (the maximum is -vvv)
Expand Down
6 changes: 3 additions & 3 deletions lockfile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ impl Iterator for LockfileFormatIter {
0 => LockfileFormat::Npm,
1 => LockfileFormat::Yarn,
2 => LockfileFormat::Gem,
3 => LockfileFormat::Pip,
4 => LockfileFormat::Pipenv,
5 => LockfileFormat::Poetry,
3 => LockfileFormat::Poetry,
4 => LockfileFormat::Pip,
5 => LockfileFormat::Pipenv,
6 => LockfileFormat::Maven,
7 => LockfileFormat::Gradle,
8 => LockfileFormat::Msbuild,
Expand Down
15 changes: 13 additions & 2 deletions lockfile/src/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ pub struct PyRequirements;
pub struct PipFile;
pub struct Poetry;

/// Check if filename is `requirements*.txt`
fn is_requirements_file(path: &Path) -> bool {
path.file_name().and_then(|f| f.to_str()).map_or(false, |file_name| {
file_name.starts_with("requirements") && file_name.ends_with(".txt")
})
}

impl Parse for PyRequirements {
/// Parses `requirements.txt` files into a vec of packages
fn parse(&self, data: &str) -> anyhow::Result<Vec<Package>> {
Expand All @@ -34,11 +41,15 @@ impl Parse for PyRequirements {
}

fn is_path_lockfile(&self, path: &Path) -> bool {
path.file_name() == Some(OsStr::new("requirements.txt"))
is_requirements_file(path)
}

fn is_path_manifest(&self, path: &Path) -> bool {
path.file_name() == Some(OsStr::new("requirements.txt"))
path.file_name() == Some(OsStr::new("requirements.in"))
|| path.file_name() == Some(OsStr::new("pyproject.toml"))
|| path.file_name() == Some(OsStr::new("setup.cfg"))
|| path.file_name() == Some(OsStr::new("setup.py"))
|| is_requirements_file(path)
}

#[cfg(feature = "generator")]
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/bundler.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Ruby bundler ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Bundler {
"Gemfile.lock"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("bundle");
command.args(["lock"]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Rust cargo ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Cargo {
"Cargo.lock"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("cargo");
command.args(["generate-lockfile"]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/go.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Go ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Go {
"go.sum"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("go");
command.args(["get", "-d"]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/gradle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Java gradle ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Gradle {
"gradle.lockfile"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("gradle");
command.args(["dependencies", "--write-locks"]);
command
Expand Down
13 changes: 10 additions & 3 deletions lockfile_generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub trait Generator {
fn lockfile_name(&self) -> &'static str;

/// Command for generating the lockfile.
fn command(&self) -> Command;
fn command(&self, manifest_path: &Path) -> Command;

/// List of files conflicting with lockfile generation.
///
Expand All @@ -34,7 +34,12 @@ pub trait Generator {
///
/// This will ignore all existing lockfiles and create a new lockfile based
/// on the current project configuration.
fn generate_lockfile(&self, project_path: &Path) -> Result<String> {
fn generate_lockfile(&self, manifest_path: &Path) -> Result<String> {
let canonicalized = fs::canonicalize(manifest_path)?;
let project_path = canonicalized
.parent()
.ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?;

// Move files which interfere with lockfile generation.
let _relocators = self
.conflicting_files()
Expand All @@ -43,7 +48,7 @@ pub trait Generator {
.collect::<Result<Vec<_>>>()?;

// Generate lockfile at the target location.
let mut command = self.command();
let mut command = self.command(&canonicalized);
command.current_dir(project_path);
command.stdin(Stdio::null());
command.stdout(Stdio::null());
Expand Down Expand Up @@ -117,6 +122,8 @@ pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("invalid manifest path: {0:?}")]
InvalidManifest(PathBuf),
#[error("failed to spawn command {0}: {1}")]
ProcessCreation(String, io::Error),
#[error("package manager exited with non-zero status")]
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/maven.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Java maven ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Maven {
"effective-pom.xml"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("mvn");
command.args(["help:effective-pom", &format!("-Doutput={}", self.lockfile_name())]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/npm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! JavaScript npm ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -15,7 +16,7 @@ impl Generator for Npm {
vec![self.lockfile_name(), "npm-shrinkwrap.json", "yarn.lock"]
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("npm");
command.args(["install", "--package-lock-only", "--ignore-scripts"]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/pip.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Python pipenv ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Pip {
"Pipfile.lock"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("pipenv");
command.args(["lock"]);
command
Expand Down
3 changes: 2 additions & 1 deletion lockfile_generator/src/poetry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Python poetry ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Poetry {
"poetry.lock"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("poetry");
command.args(["lock"]);
command
Expand Down
5 changes: 3 additions & 2 deletions lockfile_generator/src/python_requirements.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Python pip ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,9 +12,9 @@ impl Generator for PythonRequirements {
"requirements-locked.txt"
}

fn command(&self) -> Command {
fn command(&self, manifest_path: &Path) -> Command {
let mut command = Command::new("pip-compile");
command.args(["-o", self.lockfile_name()]);
command.arg("-o").arg(self.lockfile_name()).arg(manifest_path);
command
}
}
3 changes: 2 additions & 1 deletion lockfile_generator/src/yarn.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! JavaScript yarn ecosystem.
use std::path::Path;
use std::process::Command;

use crate::Generator;
Expand All @@ -11,7 +12,7 @@ impl Generator for Yarn {
"yarn.lock"
}

fn command(&self) -> Command {
fn command(&self, _manifest_path: &Path) -> Command {
let mut command = Command::new("yarn");
command.args(["install", "--mode=skip-build", "--mode=update-lockfile"]);
command
Expand Down

0 comments on commit 295d30a

Please sign in to comment.