diff --git a/CHANGELOG.md b/CHANGELOG.md index d95f1f489..74893c39a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - `pnpm` version 5 parser including metadata in package versions +- Platform-specific dependencies ignored by the `Gemfile.lock` parser ## 7.1.4 - 2024-11-07 diff --git a/lockfile/src/parsers/gem.rs b/lockfile/src/parsers/gem.rs index c755131e4..dd7305111 100644 --- a/lockfile/src/parsers/gem.rs +++ b/lockfile/src/parsers/gem.rs @@ -15,10 +15,10 @@ use crate::{Package, PackageVersion, ThirdPartyVersion}; const DEFAULT_REGISTRY: &str = "https://rubygems.org/"; /// Legal non-alphanumeric characters in loose version specifications. -const LOOSE_VERSION_CHARS: &[char] = &[' ', ',', '<', '>', '=', '~', '!', '.', '-', '+']; +const LOOSE_VERSION_CHARS: &[char] = &[' ', ',', '<', '>', '=', '~', '!', '.', '-', '+', '_']; /// Legal non-alphanumeric characters in strict version specifications. -const STRICT_VERSION_CHARS: &[char] = &['.', '-', '+']; +const STRICT_VERSION_CHARS: &[char] = &['.']; #[derive(Debug)] struct Section<'a> { @@ -224,7 +224,7 @@ fn package_name(input: &str) -> IResult<&str, &str> { } /// Parser allowing for loose `(>= 1.2.0, < 2.0, != 1.2.3)` and strict -/// `(1.2.3-alpha+build3)` versions. +/// `(1.2.3.alpha.1)` versions. fn loose_package_version(input: &str) -> IResult<&str, &str> { // Versions can be completely omitted for sub-dependencies. if input.is_empty() { @@ -241,7 +241,7 @@ fn loose_package_version(input: &str) -> IResult<&str, &str> { )(input) } -/// Parser allowing only strict `1.2.3-alpha+build3` versions. +/// Parser allowing only strict `1.2.3.alpha.1` versions. fn strict_package_version(input: &str) -> IResult<&str, &str> { let (input, _) = space0(input)?; recognize(many1(satisfy(|c: char| { diff --git a/lockfile/src/ruby.rs b/lockfile/src/ruby.rs index 2570d0a0b..e3942a4eb 100644 --- a/lockfile/src/ruby.rs +++ b/lockfile/src/ruby.rs @@ -17,11 +17,17 @@ pub struct GemLock; impl Parse for GemLock { /// Parses `Gemfile.lock` files into a vec of packages fn parse(&self, data: &str) -> anyhow::Result> { - let (_, entries) = gem::parse(data) + let (_, mut packages) = gem::parse(data) .finish() .map_err(|e| anyhow!(convert_error(data, e))) .context("Failed to parse gem lockfile")?; - Ok(entries) + + // Remove duplicate dependencies, which can occur when a dependency is included + // with multiple different platform suffixes. + packages.sort_unstable(); + packages.dedup(); + + Ok(packages) } fn is_path_lockfile(&self, path: &Path) -> bool { @@ -48,7 +54,7 @@ mod tests { #[test] fn lock_parse_gem() { let pkgs = GemLock.parse(include_str!("../../tests/fixtures/Gemfile.lock")).unwrap(); - assert_eq!(pkgs.len(), 11); + assert_eq!(pkgs.len(), 13); let expected_pkgs = [ Package { @@ -84,6 +90,16 @@ mod tests { }), package_type: PackageType::RubyGems, }, + Package { + name: "ffi".into(), + version: PackageVersion::FirstParty("1.17.0".into()), + package_type: PackageType::RubyGems, + }, + Package { + name: "fake".into(), + version: PackageVersion::FirstParty("1.2.3".into()), + package_type: PackageType::RubyGems, + }, ]; for expected_pkg in expected_pkgs { diff --git a/tests/fixtures/Gemfile.lock b/tests/fixtures/Gemfile.lock index 875eeef9d..1eb534946 100644 --- a/tests/fixtures/Gemfile.lock +++ b/tests/fixtures/Gemfile.lock @@ -31,7 +31,7 @@ GEM rspec-core (~> 3.11.0) rspec-expectations (~> 3.11.0) rspec-mocks (~> 3.11.0) - rspec-core (3.11.0-alpha+build3) + rspec-core (3.11.0) rspec-support (~> 3.11.0) rspec-expectations (3.11.1) diff-lcs (>= 1.2.0, < 2.0) @@ -48,6 +48,9 @@ GEM remote: https://rubygems.org/ specs: wirble (0.1.3) + ffi (1.17.0) + ffi (1.17.0-x86_64-linux-gnu) + fake (1.2.3-x86_64-linux-gnu) DEPENDENCIES benchmark!