From b69b11511e518e915c0fc107ae0dae55c053939e Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 31 Oct 2022 00:15:19 +0000 Subject: [PATCH 01/20] feat: add support for PNPM Adds support for checking licenses for PNPM workspaces --- lib/license_finder/cli/base.rb | 1 + lib/license_finder/cli/main.rb | 1 + lib/license_finder/configuration.rb | 4 + lib/license_finder/core.rb | 1 + lib/license_finder/package.rb | 1 + lib/license_finder/package_manager.rb | 1 + lib/license_finder/package_managers/pnpm.rb | 128 ++++++++++++++++++++ lib/license_finder/packages/pnpm_package.rb | 13 ++ lib/license_finder/scanner.rb | 2 +- 9 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 lib/license_finder/package_managers/pnpm.rb create mode 100644 lib/license_finder/packages/pnpm_package.rb diff --git a/lib/license_finder/cli/base.rb b/lib/license_finder/cli/base.rb index 06e30280..915fd342 100644 --- a/lib/license_finder/cli/base.rb +++ b/lib/license_finder/cli/base.rb @@ -47,6 +47,7 @@ def license_finder_config :maven_options, :npm_options, :yarn_options, + :pnpm_options, :pip_requirements_path, :python_version, :rebar_command, diff --git a/lib/license_finder/cli/main.rb b/lib/license_finder/cli/main.rb index d440b4d6..00a6834b 100644 --- a/lib/license_finder/cli/main.rb +++ b/lib/license_finder/cli/main.rb @@ -33,6 +33,7 @@ class Main < Base class_option :maven_options, desc: 'Maven options to append to command. Defaults to empty.' class_option :npm_options, desc: 'npm options to append to command. Defaults to empty.' class_option :yarn_options, desc: 'yarn options to append to command. Defaults to empty.' + class_option :pnpm_options, desc: 'pnpm options to append to command. Defaults to empty.' class_option :pip_requirements_path, desc: 'Path to python requirements file. Defaults to requirements.txt.' class_option :python_version, desc: 'Python version to invoke pip with. Valid versions: 2 or 3. Default: 2' class_option :rebar_command, desc: "Command to use when fetching rebar packages. Only meaningful if used with a Erlang/rebar project. Defaults to 'rebar'." diff --git a/lib/license_finder/configuration.rb b/lib/license_finder/configuration.rb index f0160552..eccde110 100644 --- a/lib/license_finder/configuration.rb +++ b/lib/license_finder/configuration.rb @@ -101,6 +101,10 @@ def yarn_options get(:yarn_options) end + def pnpm_options + get(:pnpm_options) + end + def pip_requirements_path get(:pip_requirements_path) end diff --git a/lib/license_finder/core.rb b/lib/license_finder/core.rb index c7e1a916..38defd3a 100644 --- a/lib/license_finder/core.rb +++ b/lib/license_finder/core.rb @@ -102,6 +102,7 @@ def options # rubocop:disable Metrics/AbcSize maven_options: config.maven_options, npm_options: config.npm_options, yarn_options: config.yarn_options, + pnpm_options: config.pnpm_options, pip_requirements_path: config.pip_requirements_path, python_version: config.python_version, rebar_command: config.rebar_command, diff --git a/lib/license_finder/package.rb b/lib/license_finder/package.rb index 04a8370a..c10096b5 100644 --- a/lib/license_finder/package.rb +++ b/lib/license_finder/package.rb @@ -187,6 +187,7 @@ def log_activation(activation) require 'license_finder/packages/nuget_package' require 'license_finder/packages/conan_package' require 'license_finder/packages/yarn_package' +require 'license_finder/packages/pnpm_package' require 'license_finder/packages/sbt_package' require 'license_finder/packages/cargo_package' require 'license_finder/packages/composer_package' diff --git a/lib/license_finder/package_manager.rb b/lib/license_finder/package_manager.rb index 95c9efcd..1ab041e6 100644 --- a/lib/license_finder/package_manager.rb +++ b/lib/license_finder/package_manager.rb @@ -158,6 +158,7 @@ def log_to_file(prep_cmd, contents) require 'license_finder/package_managers/trash' require 'license_finder/package_managers/bundler' require 'license_finder/package_managers/npm' +require 'license_finder/package_managers/pnpm' require 'license_finder/package_managers/yarn' require 'license_finder/package_managers/pip' require 'license_finder/package_managers/pipenv' diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb new file mode 100644 index 00000000..1ba86e7c --- /dev/null +++ b/lib/license_finder/package_managers/pnpm.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require 'json' +require 'tempfile' + +module LicenseFinder + class PNPM < PackageManager + def initialize(options = {}) + super + @pnpm_options = options[:pnpm_options] + end + + SHELL_COMMAND = 'pnpm licenses --json --long' + + def possible_package_paths + [project_path.join('pnpm-lock.yaml')] + end + + def self.takes_priority_over + NPM + end + + def current_packages + # check if the minimum version of PNPM is met + raise "The minimum PNPM version is not met, requires 7.14.1 or later" unless is_supported_pnpm + + # check if the project directory has workspace file + cmd = "#{PNPM::SHELL_COMMAND}" + cmd += ' --no-color' + cmd += " --recursive" unless project_has_workspaces == false + cmd += " --dir #{project_path}" unless project_path.nil? + cmd += " #{@pnpm_options}" unless @pnpm_options.nil? + + stdout, stderr, status = Cmd.run(cmd) + raise "Command '#{cmd}' failed to execute: #{stderr}" unless status.success? + + json_objects = JSON.parse(stdout) + get_pnpm_packages(json_objects) + end + + def get_pnpm_packages(json_objects) + packages = [] + incompatible_packages = [] + + json_objects.map do |k,value| + value.each do |pkg| + name = pkg['name'] + version = pkg['version'] + license = pkg['license'] + homepage = pkg['vendorUrl'] + author = pkg['vendorName'] + modulePath = pkg['path'] + + package = PNPMPackage.new( + name, + version, + spec_licenses: [license], + homepage: homepage, + authors: author, + install_path: modulePath + ) + packages << package + end + end + + packages + incompatible_packages.uniq + end + + def package_management_command + 'pnpm' + end + + def prepare_command + 'pnpm install --no-lockfile --ignore-scripts' + end + + def possible_package_paths + [project_path.join('package.json')] + end + + def prepare + prep_cmd = "#{prepare_command}#{production_flag}" + _stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(prep_cmd) } + + return if status.success? + + log_errors stderr + raise "Prepare command '#{prep_cmd}' failed" unless @prepare_no_fail + end + + private + + def project_has_workspaces + Dir.chdir(project_path) do + return File.file?('pnpm-workspace.yaml') + end + end + + # PNPM introduced the licenses command in 7.14.1 + def is_supported_pnpm + Dir.chdir(project_path) do + version_string, stderr_str, status = Cmd.run('pnpm -v') + raise "Command 'pnpm -v' failed to execute: #{stderr_str}" unless status.success? + + version = version_string.split('.').map(&:to_i) + major = version[0] + minor = version[1] + patch = version[1] + if (major > 7) + return true + elsif (major == 7 && minor > 14) + return true + elsif (major == 7 && minor == 14 && patch >= 1) + return true + else + return false + end + end + end + + + def production_flag + return '' if @ignored_groups.nil? + + @ignored_groups.include?('devDependencies') ? ' --prod' : '' + end + end +end diff --git a/lib/license_finder/packages/pnpm_package.rb b/lib/license_finder/packages/pnpm_package.rb new file mode 100644 index 00000000..6264f26c --- /dev/null +++ b/lib/license_finder/packages/pnpm_package.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module LicenseFinder + class PNPMPackage < Package + def package_manager + 'PNPM' + end + + def package_url + "https://www.npmjs.com/package/#{CGI.escape(name)}/v/#{CGI.escape(version)}" + end + end +end diff --git a/lib/license_finder/scanner.rb b/lib/license_finder/scanner.rb index a0fbc196..0d5f78b6 100644 --- a/lib/license_finder/scanner.rb +++ b/lib/license_finder/scanner.rb @@ -3,7 +3,7 @@ module LicenseFinder class Scanner PACKAGE_MANAGERS = [ - GoModules, GoDep, GoWorkspace, Go15VendorExperiment, Glide, Gvt, Govendor, Trash, Dep, Bundler, NPM, Pip, + GoModules, GoDep, GoWorkspace, Go15VendorExperiment, Glide, Gvt, Govendor, Trash, Dep, Bundler, NPM, PNPM, Pip, Yarn, Bower, Maven, Gradle, CocoaPods, Rebar, Erlangmk, Nuget, Carthage, Mix, Conan, Sbt, Cargo, Dotnet, Composer, Pipenv, Conda, Spm, Pub ].freeze From 4a8978e0023c048fe6d4d8caafda068fc52db7aa Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Mon, 31 Oct 2022 01:29:33 +0000 Subject: [PATCH 02/20] test: add tests for PNPM package manager --- .../package_managers/pnpm_spec.rb | 169 ++++++++++++++++++ .../packages/pnpm_package_spec.rb | 11 ++ 2 files changed, 180 insertions(+) create mode 100644 spec/lib/license_finder/package_managers/pnpm_spec.rb create mode 100644 spec/lib/license_finder/packages/pnpm_package_spec.rb diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb new file mode 100644 index 00000000..3bf526e7 --- /dev/null +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'fakefs/spec_helpers' +require 'json' + +module LicenseFinder + describe PNPM do + let(:root) { '/fake-node-project' } + it_behaves_like 'a PackageManager' + + let(:pnpm_shell_command_output) do + { + "MIT": [ + { + "name": "yn", + "version": "2.0.0", + "path": Pathname(root).join('node_modules', 'yn'), + "license": "MIT", + "vendorUrl": "sindresorhus.com", + "vendorName": "Sindre Sorhus" + } + ] + }.to_json + end + + let(:pnpm_incompatible_packages_shell_output) do + { + "MIT": [ + { + "name": "fsevents", + "version": "1.1.1", + "path": Pathname(root).join('node_modules', 'fsevents'), + "license": "Unknown", + "vendorUrl": "github.com/fsevents/fsevents", + } + ] + }.to_json + end + + describe '.prepare' do + subject { PNPM.new(project_path: Pathname(root), logger: double(:logger, active: nil)) } + + include FakeFS::SpecHelpers + before do + FileUtils.mkdir_p(Dir.tmpdir) + FileUtils.mkdir_p(root) + end + + context 'when using PNPM 6 throws error' do + before do + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['6.1.4', '', cmd_success]) + end + + context 'when the shell command fails' do + it 'an error is raised' do + allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " #{Pathname(root)}").and_return([nil, 'error', cmd_failure]) + + expect { subject.current_packages }.to raise_error(/The minimum PNPM version is not met, requires 7.14.1 or later/) + end + end + end + + context 'when using PNPM projects' do + before do + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['2.0.1', '', cmd_success]) + end + + it 'should call pnpm install with no cli parameters' do + expect(SharedHelpers::Cmd).to receive(:run).with('pnpm install --no-lockfile --ignore-scripts').and_return([pnpm_shell_command_output, '', cmd_success]) + subject.prepare + end + + context 'ignored_groups contains devDependencies' do + subject { PNPM.new(project_path: Pathname(root), ignored_groups: 'devDependencies') } + + it 'should include a production flag' do + expect(SharedHelpers::Cmd).to receive(:run).with('pnpm install --no-lockfile --ignore-scripts --prod').and_return([pnpm_shell_command_output, '', cmd_success]) + subject.prepare + end + end + end + end + + describe '.prepare_command' do + include FakeFS::SpecHelpers + before do + FileUtils.mkdir_p(Dir.tmpdir) + FileUtils.mkdir_p(root) + end + + context 'when in a PNPM project' do + before do + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + end + + subject { PNPM.new(project_path: Pathname(root), logger: double(:logger, active: nil)) } + it 'returns the correct prepare method' do + expect(subject.prepare_command).to eq('pnpm install --no-lockfile --ignore-scripts') + end + end + end + + describe '#current_packages' do + subject { PNPM.new(project_path: Pathname(root), logger: double(:logger, active: nil)) } + + include FakeFS::SpecHelpers + before do + FileUtils.mkdir_p(Dir.tmpdir) + FileUtils.mkdir_p(root) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + end + + context 'when using PNPM 7.14.1+' do + before do + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + end + end + + it 'displays packages as returned from "pmpm list"' do + allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " --no-color --dir #{Pathname(root)}") do + [pnpm_shell_command_output, '', cmd_success] + end + + expect(subject.current_packages.length).to eq 1 + expect(subject.current_packages.first.name).to eq 'yn' + expect(subject.current_packages.first.version).to eq '2.0.0' + expect(subject.current_packages.first.license_names_from_spec).to eq ['MIT'] + expect(subject.current_packages.first.homepage).to eq 'sindresorhus.com' + expect(subject.current_packages.first.authors).to eq 'Sindre Sorhus' + expect(subject.current_packages.first.install_path).to eq Pathname(root).join('node_modules', 'yn').to_s + end + + it 'displays incompatible packages with license type unknown' do + allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " --no-color --dir #{Pathname(root)}") do + [pnpm_incompatible_packages_shell_output, '', cmd_success] + end + + expect(subject.current_packages.length).to eq 1 + expect(subject.current_packages.last.name).to eq 'fsevents' + expect(subject.current_packages.last.version).to eq '1.1.1' + expect(subject.current_packages.last.license_names_from_spec).to eq ['Unknown'] + end + + context 'ignored_groups contains devDependencies' do + subject { PNPM.new(project_path: Pathname(root), ignored_groups: 'devDependencies') } + it 'should include a production flag' do + expect(SharedHelpers::Cmd).to receive(:run).with("#{PNPM::SHELL_COMMAND} --no-color --dir #{Pathname(root)}") + .and_return([pnpm_shell_command_output, '', cmd_success]) + subject.current_packages + end + end + + context 'when the shell command fails' do + it 'an error is raised' do + allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " --no-color --dir #{Pathname(root)}").and_return([nil, 'error', cmd_failure]) + + expect { subject.current_packages }.to raise_error(/Command 'pnpm licenses --json --long --no-color --dir #{Pathname(root)}' failed to execute: error/) + end + end + end + + describe '.package_management_command' do + it 'returns the correct package management command' do + expect(subject.package_management_command).to eq('pnpm') + end + end + end +end diff --git a/spec/lib/license_finder/packages/pnpm_package_spec.rb b/spec/lib/license_finder/packages/pnpm_package_spec.rb new file mode 100644 index 00000000..45d101df --- /dev/null +++ b/spec/lib/license_finder/packages/pnpm_package_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +module LicenseFinder + describe PNPMPackage do + subject { described_class.new('a package', '1.1.1') } + + its(:package_url) { should == 'https://www.npmjs.com/package/a+package/v/1.1.1' } + end +end From 1c2e18a3dd599b107fae6c0187457b7e4dd7e0e5 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Tue, 1 Nov 2022 20:30:06 +0000 Subject: [PATCH 03/20] fix: add `pnpm` to the Dockerfile --- Dockerfile | 75 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index 49cffff9..c91072a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,15 +15,15 @@ ENV COMPOSER_ALLOW_SUPERUSER 1 # programs needed for building RUN apt-get update && apt-get install -y \ - build-essential \ - curl \ - sudo \ - unzip \ - wget \ - gnupg2 \ - apt-utils \ - software-properties-common \ - bzr + build-essential \ + curl \ + sudo \ + unzip \ + wget \ + gnupg2 \ + apt-utils \ + software-properties-common \ + bzr RUN add-apt-repository ppa:git-core/ppa && apt-get update && apt-get install -y git @@ -33,14 +33,17 @@ RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - && \ # install yarn RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list && \ - apt-get update && \ - apt-get install yarn + echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list && \ + apt-get update && \ + apt-get install yarn # install bower RUN npm install -g bower && \ echo '{ "allow_root": true }' > /root/.bowerrc +# install pnpm +RUN curl -fsSL "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" -o /bin/pnpm; chmod +x /usr/local/bin/pnpm; + # install jdk 12 RUN curl -L -o openjdk12.tar.gz https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz && \ tar xvf openjdk12.tar.gz && \ @@ -97,10 +100,10 @@ ENV GOPATH=/gopath ENV PATH=$PATH:$GOPATH/bin RUN mkdir /gopath && \ - go install github.com/tools/godep@latest && \ - go install github.com/FiloSottile/gvt@latest && \ - go install github.com/kardianos/govendor@latest && \ - go clean -cache + go install github.com/tools/godep@latest && \ + go install github.com/FiloSottile/gvt@latest && \ + go install github.com/kardianos/govendor@latest && \ + go clean -cache #install rvm and glide and godep RUN apt-add-repository -y ppa:rael-gc/rvm && \ @@ -145,10 +148,10 @@ WORKDIR / # install conan RUN apt-get install -y python-dev && \ - pip install --no-cache-dir --ignore-installed six --ignore-installed colorama \ - --ignore-installed requests --ignore-installed chardet \ - --ignore-installed urllib3 \ - --upgrade setuptools && \ + pip install --no-cache-dir --ignore-installed six --ignore-installed colorama \ + --ignore-installed requests --ignore-installed chardet \ + --ignore-installed urllib3 \ + --upgrade setuptools && \ pip install --no-cache-dir -Iv conan==1.43.0 && \ conan config install https://github.com/conan-io/conanclientcert.git @@ -156,18 +159,18 @@ RUN apt-get install -y python-dev && \ # install NuGet (w. mono) # https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools#macoslinux RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF &&\ - echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list &&\ - apt-get update &&\ - apt-get install -y mono-complete &&\ - curl -o "/usr/local/bin/nuget.exe" "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" &&\ - curl -o "/usr/local/bin/nugetv3.5.0.exe" "https://dist.nuget.org/win-x86-commandline/v3.5.0/nuget.exe" + echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list &&\ + apt-get update &&\ + apt-get install -y mono-complete &&\ + curl -o "/usr/local/bin/nuget.exe" "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" &&\ + curl -o "/usr/local/bin/nugetv3.5.0.exe" "https://dist.nuget.org/win-x86-commandline/v3.5.0/nuget.exe" # install dotnet core RUN wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb &&\ - sudo dpkg -i packages-microsoft-prod.deb &&\ - rm packages-microsoft-prod.deb &&\ - sudo apt-get update &&\ - sudo apt-get install -y dotnet-runtime-2.1 dotnet-sdk-2.1 dotnet-sdk-2.2 dotnet-sdk-3.0 dotnet-sdk-3.1 + sudo dpkg -i packages-microsoft-prod.deb &&\ + rm packages-microsoft-prod.deb &&\ + sudo apt-get update &&\ + sudo apt-get install -y dotnet-runtime-2.1 dotnet-sdk-2.1 dotnet-sdk-2.2 dotnet-sdk-3.0 dotnet-sdk-3.1 # install Composer # The ARG and ENV are for installing tzdata which is part of this installaion. @@ -190,12 +193,12 @@ RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 4F4EA0AAE5 # See https://docs.conda.io/en/latest/miniconda_hashes.html # for latest versions and SHAs. RUN \ - conda_installer=Miniconda3-py38_4.9.2-Linux-x86_64.sh &&\ - ref='1314b90489f154602fd794accfc90446111514a5a72fe1f71ab83e07de9504a7' &&\ - wget -q https://repo.anaconda.com/miniconda/${conda_installer} &&\ - sha=`openssl sha256 "${conda_installer}" | cut -d' ' -f2` &&\ - ([ "$sha" = "${ref}" ] || (echo "Verification failed: ${sha} != ${ref}"; false)) &&\ - (echo; echo "yes") | sh "${conda_installer}" + conda_installer=Miniconda3-py38_4.9.2-Linux-x86_64.sh &&\ + ref='1314b90489f154602fd794accfc90446111514a5a72fe1f71ab83e07de9504a7' &&\ + wget -q https://repo.anaconda.com/miniconda/${conda_installer} &&\ + sha=`openssl sha256 "${conda_installer}" | cut -d' ' -f2` &&\ + ([ "$sha" = "${ref}" ] || (echo "Verification failed: ${sha} != ${ref}"; false)) &&\ + (echo; echo "yes") | sh "${conda_installer}" # install Swift Package Manager # Based on https://github.com/apple/swift-docker/blob/main/5.3/ubuntu/18.04/Dockerfile @@ -225,7 +228,7 @@ RUN curl -o flutter_linux_2.8.1-stable.tar.xz https://storage.googleapis.com/flu && tar xf flutter_linux_2.8.1-stable.tar.xz \ && mv flutter ${FLUTTER_HOME} \ && rm flutter_linux_2.8.1-stable.tar.xz - + ENV PATH=$PATH:${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin RUN flutter doctor -v \ && flutter update-packages \ From 6b373fbd0a87fc8c5e7eee2aa1e04f000dacb6a2 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 00:49:21 +0000 Subject: [PATCH 04/20] style: fixing code style issues based on rubocop --- lib/license_finder/package_managers/pnpm.rb | 3 +- .../package_managers/pnpm_spec.rb | 30 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index 1ba86e7c..178c65e0 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -42,7 +42,7 @@ def get_pnpm_packages(json_objects) packages = [] incompatible_packages = [] - json_objects.map do |k,value| + json_objects.map do |k, value| value.each do |pkg| name = pkg['name'] version = pkg['version'] @@ -118,7 +118,6 @@ def is_supported_pnpm end end - def production_flag return '' if @ignored_groups.nil? diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index 3bf526e7..e9bb56b0 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -12,14 +12,14 @@ module LicenseFinder let(:pnpm_shell_command_output) do { "MIT": [ - { - "name": "yn", - "version": "2.0.0", - "path": Pathname(root).join('node_modules', 'yn'), - "license": "MIT", - "vendorUrl": "sindresorhus.com", - "vendorName": "Sindre Sorhus" - } + { + "name": "yn", + "version": "2.0.0", + "path": Pathname(root).join('node_modules', 'yn'), + "license": "MIT", + "vendorUrl": "sindresorhus.com", + "vendorName": "Sindre Sorhus" + } ] }.to_json end @@ -27,13 +27,13 @@ module LicenseFinder let(:pnpm_incompatible_packages_shell_output) do { "MIT": [ - { - "name": "fsevents", - "version": "1.1.1", - "path": Pathname(root).join('node_modules', 'fsevents'), - "license": "Unknown", - "vendorUrl": "github.com/fsevents/fsevents", - } + { + "name": "fsevents", + "version": "1.1.1", + "path": Pathname(root).join('node_modules', 'fsevents'), + "license": "Unknown", + "vendorUrl": "github.com/fsevents/fsevents", + } ] }.to_json end From 6f70af054ec87ae76f3316ce466e9c38c92a1be3 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 00:54:23 +0000 Subject: [PATCH 05/20] fix: attempt to install pnpm via npm package manager --- Dockerfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index ead9702c..e2188432 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,8 @@ RUN npm install -g bower && \ echo '{ "allow_root": true }' > /root/.bowerrc # install pnpm -RUN curl -fsSL "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" -o /bin/pnpm; chmod +x /usr/local/bin/pnpm; +RUN npm install -g pnpm && \ + pnpm version # install jdk 12 RUN curl -L -o openjdk12.tar.gz https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz && \ @@ -148,10 +149,10 @@ WORKDIR / # install conan RUN apt-get install -y python-dev && \ - pip install --no-cache-dir --ignore-installed six --ignore-installed colorama \ - --ignore-installed requests --ignore-installed chardet \ - --ignore-installed urllib3 \ - --upgrade setuptools && \ + pip install --no-cache-dir --ignore-installed six --ignore-installed colorama \ + --ignore-installed requests --ignore-installed chardet \ + --ignore-installed urllib3 \ + --upgrade setuptools && \ pip3 install --no-cache-dir -Iv conan==1.51.3 && \ conan config install https://github.com/conan-io/conanclientcert.git From 5e7d3281c7a7a7bd264c76244225783c53932d65 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 01:01:06 +0000 Subject: [PATCH 06/20] style: manually fixed styling offences that rubocop did not autocorrect --- lib/license_finder/package_managers/pnpm.rb | 28 ++++++++----------- .../package_managers/pnpm_spec.rb | 26 ++++++++--------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index 178c65e0..ab49187c 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -22,12 +22,12 @@ def self.takes_priority_over def current_packages # check if the minimum version of PNPM is met - raise "The minimum PNPM version is not met, requires 7.14.1 or later" unless is_supported_pnpm + raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless is_supported_pnpm # check if the project directory has workspace file - cmd = "#{PNPM::SHELL_COMMAND}" + cmd = PNPM::SHELL_COMMAND.to_s cmd += ' --no-color' - cmd += " --recursive" unless project_has_workspaces == false + cmd += ' --recursive' unless project_has_workspaces == false cmd += " --dir #{project_path}" unless project_path.nil? cmd += " #{@pnpm_options}" unless @pnpm_options.nil? @@ -42,14 +42,14 @@ def get_pnpm_packages(json_objects) packages = [] incompatible_packages = [] - json_objects.map do |k, value| + json_objects.map do |_, value| value.each do |pkg| name = pkg['name'] version = pkg['version'] license = pkg['license'] homepage = pkg['vendorUrl'] author = pkg['vendorName'] - modulePath = pkg['path'] + module_path = pkg['path'] package = PNPMPackage.new( name, @@ -57,7 +57,7 @@ def get_pnpm_packages(json_objects) spec_licenses: [license], homepage: homepage, authors: author, - install_path: modulePath + install_path: module_path ) packages << package end @@ -97,7 +97,7 @@ def project_has_workspaces end # PNPM introduced the licenses command in 7.14.1 - def is_supported_pnpm + def supported_pnpm? Dir.chdir(project_path) do version_string, stderr_str, status = Cmd.run('pnpm -v') raise "Command 'pnpm -v' failed to execute: #{stderr_str}" unless status.success? @@ -106,15 +106,11 @@ def is_supported_pnpm major = version[0] minor = version[1] patch = version[1] - if (major > 7) - return true - elsif (major == 7 && minor > 14) - return true - elsif (major == 7 && minor == 14 && patch >= 1) - return true - else - return false - end + + return true if major > 7 + return true if major == 7 && minor > 14 + return true if major == 7 && minor == 14 && patch >= 1 + return false end end diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index e9bb56b0..8f64e41c 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -11,14 +11,14 @@ module LicenseFinder let(:pnpm_shell_command_output) do { - "MIT": [ + 'MIT': [ { - "name": "yn", - "version": "2.0.0", - "path": Pathname(root).join('node_modules', 'yn'), - "license": "MIT", - "vendorUrl": "sindresorhus.com", - "vendorName": "Sindre Sorhus" + 'name': 'yn', + 'version': '2.0.0', + 'path': Pathname(root).join('node_modules', 'yn'), + 'license': 'MIT', + 'vendorUrl': 'sindresorhus.com', + 'vendorName': 'Sindre Sorhus' } ] }.to_json @@ -26,13 +26,13 @@ module LicenseFinder let(:pnpm_incompatible_packages_shell_output) do { - "MIT": [ + 'MIT': [ { - "name": "fsevents", - "version": "1.1.1", - "path": Pathname(root).join('node_modules', 'fsevents'), - "license": "Unknown", - "vendorUrl": "github.com/fsevents/fsevents", + 'name': 'fsevents', + 'version': '1.1.1', + 'path': Pathname(root).join('node_modules', 'fsevents'), + 'license': 'Unknown', + 'vendorUrl': 'github.com/fsevents/fsevents', } ] }.to_json From 441cbbe3089acdd1c4d66d955600168bb32b7af3 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 01:07:04 +0000 Subject: [PATCH 07/20] style: manually fixed styling offences that rubocop did not autocorrect --- lib/license_finder/package_managers/pnpm.rb | 5 +---- spec/lib/license_finder/package_managers/pnpm_spec.rb | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index ab49187c..cbeeb69a 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -74,10 +74,6 @@ def prepare_command 'pnpm install --no-lockfile --ignore-scripts' end - def possible_package_paths - [project_path.join('package.json')] - end - def prepare prep_cmd = "#{prepare_command}#{production_flag}" _stdout, stderr, status = Dir.chdir(project_path) { Cmd.run(prep_cmd) } @@ -110,6 +106,7 @@ def supported_pnpm? return true if major > 7 return true if major == 7 && minor > 14 return true if major == 7 && minor == 14 && patch >= 1 + return false end end diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index 8f64e41c..23955370 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -32,7 +32,7 @@ module LicenseFinder 'version': '1.1.1', 'path': Pathname(root).join('node_modules', 'fsevents'), 'license': 'Unknown', - 'vendorUrl': 'github.com/fsevents/fsevents', + 'vendorUrl': 'github.com/fsevents/fsevents' } ] }.to_json From e347017f68b5ac2f8f44345054f48eea5964d03e Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 16:47:21 +0000 Subject: [PATCH 08/20] test: add missing pnpm-lock.yaml file for all_pms --- spec/fixtures/all_pms/pnpm-lock.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 spec/fixtures/all_pms/pnpm-lock.yaml diff --git a/spec/fixtures/all_pms/pnpm-lock.yaml b/spec/fixtures/all_pms/pnpm-lock.yaml new file mode 100644 index 00000000..e69de29b From cc0960ce8848a59d4fd741f4bcf0dbad4b31db12 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 16:48:40 +0000 Subject: [PATCH 09/20] fix: resolve naming issue for is_supported_pnpm? --- lib/license_finder/package_managers/pnpm.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index cbeeb69a..c52433ac 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -22,7 +22,7 @@ def self.takes_priority_over def current_packages # check if the minimum version of PNPM is met - raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless is_supported_pnpm + raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless is_supported_pnpm? # check if the project directory has workspace file cmd = PNPM::SHELL_COMMAND.to_s From 3c48a59180ba988826bcc1c2ccef11756765d3ca Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Thu, 3 Nov 2022 18:50:01 +0000 Subject: [PATCH 10/20] fix: update pnpm.rb to refer to supported_pnpm? --- lib/license_finder/package_managers/pnpm.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index c52433ac..555057ce 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -22,7 +22,7 @@ def self.takes_priority_over def current_packages # check if the minimum version of PNPM is met - raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless is_supported_pnpm? + raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless supported_pnpm? # check if the project directory has workspace file cmd = PNPM::SHELL_COMMAND.to_s From 6f39b643b26e60d908d15d6c4b02bf3a3b90ac05 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Fri, 4 Nov 2022 00:33:33 +0000 Subject: [PATCH 11/20] test: add pnpm spec test --- features/features/package_managers/pnpm_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 features/features/package_managers/pnpm_spec.rb diff --git a/features/features/package_managers/pnpm_spec.rb b/features/features/package_managers/pnpm_spec.rb new file mode 100644 index 00000000..f52636d1 --- /dev/null +++ b/features/features/package_managers/pnpm_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require_relative '../../support/feature_helper' + +describe 'PNPM Dependencies' do + # As a Node developer + # I want to be able to manage PNPM dependencies + + let(:node_developer) { LicenseFinder::TestingDSL::User.new } + + specify 'are shown in reports' do + LicenseFinder::TestingDSL::PNPMProject.create + node_developer.run_license_finder + expect(node_developer).to be_seeing_line 'http-server, 0.11.1, MIT' + end +end From d6fa2f45c46b9bee5e161eeac9b6acdca457240b Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Fri, 4 Nov 2022 00:35:38 +0000 Subject: [PATCH 12/20] test: add missing `PNPMProject` to `testing_dsl.rb`-file --- features/support/testing_dsl.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/features/support/testing_dsl.rb b/features/support/testing_dsl.rb index 50c78595..70afa978 100644 --- a/features/support/testing_dsl.rb +++ b/features/support/testing_dsl.rb @@ -223,6 +223,13 @@ def add_dep end end + class PNPMProject < Project + def add_dep + add_to_file('pnpm-lock.yaml', '') + add_to_file('package.json', '{"dependencies" : {"http-server": "0.11.1"}}') + end + end + class MavenProject < Project def add_dep install_fixture('pom.xml') From e9eff9e845a5a02bbcd78d74426efba7dfb35bb1 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Tue, 22 Nov 2022 11:11:09 +0000 Subject: [PATCH 13/20] fix: pin the minimum version of PNPM to 7.17.0 --- lib/license_finder/package_managers/pnpm.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index 555057ce..8900d716 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -22,7 +22,7 @@ def self.takes_priority_over def current_packages # check if the minimum version of PNPM is met - raise 'The minimum PNPM version is not met, requires 7.14.1 or later' unless supported_pnpm? + raise 'The minimum PNPM version is not met, requires 7.17.0 or later' unless supported_pnpm? # check if the project directory has workspace file cmd = PNPM::SHELL_COMMAND.to_s @@ -92,7 +92,7 @@ def project_has_workspaces end end - # PNPM introduced the licenses command in 7.14.1 + # PNPM introduced the licenses command in 7.17.0 def supported_pnpm? Dir.chdir(project_path) do version_string, stderr_str, status = Cmd.run('pnpm -v') @@ -104,8 +104,8 @@ def supported_pnpm? patch = version[1] return true if major > 7 - return true if major == 7 && minor > 14 - return true if major == 7 && minor == 14 && patch >= 1 + return true if major == 7 && minor > 17 + return true if major == 7 && minor == 17 && patch >= 0 return false end From 806ecf5e438119f8e3ef38886fc7b03cc3e8b9a9 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Tue, 22 Nov 2022 18:30:26 +0000 Subject: [PATCH 14/20] test: update error message for PNPM 7.17 --- bin/check | 15 +++++++++++++++ .../license_finder/package_managers/pnpm_spec.rb | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100755 bin/check diff --git a/bin/check b/bin/check new file mode 100755 index 00000000..7e6c05b9 --- /dev/null +++ b/bin/check @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'license_finder' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. +LicenseFinder::CLI::Main.start + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +# require 'irb' +# IRB.start(__FILE__) diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index 23955370..8d0f90a2 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -56,7 +56,7 @@ module LicenseFinder it 'an error is raised' do allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " #{Pathname(root)}").and_return([nil, 'error', cmd_failure]) - expect { subject.current_packages }.to raise_error(/The minimum PNPM version is not met, requires 7.14.1 or later/) + expect { subject.current_packages }.to raise_error(/The minimum PNPM version is not met, requires 7.17.0 or later/) end end end From 5e5b41e7e186eda65a0dd38bee66746cebd83dc1 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Tue, 22 Nov 2022 18:32:45 +0000 Subject: [PATCH 15/20] fix: update the command for PNPM licenses --- bin/check | 15 --------------- lib/license_finder/package_managers/pnpm.rb | 2 +- .../license_finder/package_managers/pnpm_spec.rb | 2 +- 3 files changed, 2 insertions(+), 17 deletions(-) delete mode 100755 bin/check diff --git a/bin/check b/bin/check deleted file mode 100755 index 7e6c05b9..00000000 --- a/bin/check +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby - -require 'bundler/setup' -require 'license_finder' - -# You can add fixtures and/or initialization code here to make experimenting -# with your gem easier. You can also use a different console, if you like. -LicenseFinder::CLI::Main.start - -# (If you use this, don't forget to add pry to your Gemfile!) -# require "pry" -# Pry.start - -# require 'irb' -# IRB.start(__FILE__) diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index 8900d716..db5c0f3b 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -10,7 +10,7 @@ def initialize(options = {}) @pnpm_options = options[:pnpm_options] end - SHELL_COMMAND = 'pnpm licenses --json --long' + SHELL_COMMAND = 'pnpm licenses list --json --long' def possible_package_paths [project_path.join('pnpm-lock.yaml')] diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index 8d0f90a2..d0fac67f 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -155,7 +155,7 @@ module LicenseFinder it 'an error is raised' do allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " --no-color --dir #{Pathname(root)}").and_return([nil, 'error', cmd_failure]) - expect { subject.current_packages }.to raise_error(/Command 'pnpm licenses --json --long --no-color --dir #{Pathname(root)}' failed to execute: error/) + expect { subject.current_packages }.to raise_error(/Command 'pnpm licenses list --json --long --no-color --dir #{Pathname(root)}' failed to execute: error/) end end end From d3b7ab8021e9c1a2203c99cbd6550ab466b46b5f Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Thu, 20 Oct 2022 18:09:43 -0400 Subject: [PATCH 16/20] Add ability to specify package version when setting a manual license Currently, adding a license for a package applies it to all versions of that package. However, packages sometimes change their license between versions. This change allows the --version option to be specified when invoking `license_finder licenses [add | remove]`. Similar to how approvals work, once licenses for specific versions of a given dependency are added, any licenses added for that dependency without the --version option are ignored. --- .../configure/assign_licenses_spec.rb | 26 +++++++ lib/license_finder/cli/licenses.rb | 14 +++- lib/license_finder/decision_applier.rb | 2 +- lib/license_finder/decisions.rb | 43 +++++++++-- .../license_finder/decision_applier_spec.rb | 10 +++ spec/lib/license_finder/decisions_spec.rb | 71 +++++++++++++++++++ 6 files changed, 158 insertions(+), 8 deletions(-) diff --git a/features/features/configure/assign_licenses_spec.rb b/features/features/configure/assign_licenses_spec.rb index 3607ffea..535debe0 100644 --- a/features/features/configure/assign_licenses_spec.rb +++ b/features/features/configure/assign_licenses_spec.rb @@ -34,4 +34,30 @@ developer.run_license_finder expect(developer).to be_seeing_something_like /mislicensed_dep.*Default/ end + + specify 'can be assigned and removed by package version' do + project = developer.create_ruby_app + dep_a_gem = developer.create_gem 'dep_a', version: '2.0.0', license: 'Unknown' + dep_b_gem = developer.create_gem 'dep_b', version: '3.0.0', license: 'Unknown' + project.depend_on dep_a_gem + project.depend_on dep_b_gem + + developer.execute_command 'license_finder licenses add dep_a GPL --version=1.0.0' + developer.execute_command 'license_finder licenses add dep_a MIT --version=2.0.0' + developer.execute_command 'license_finder licenses add dep_a GPL --version=3.0.0' + + developer.execute_command 'license_finder licenses add dep_b GPL --version=1.0.0' + developer.execute_command 'license_finder licenses add dep_b GPL --version=2.0.0' + developer.execute_command 'license_finder licenses add dep_b Apache-2.0 --version=3.0.0' + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, MIT' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + + developer.execute_command 'license_finder licenses remove dep_a MIT --version=2.0.0' + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + end end diff --git a/lib/license_finder/cli/licenses.rb b/lib/license_finder/cli/licenses.rb index f99edc18..9d876a39 100644 --- a/lib/license_finder/cli/licenses.rb +++ b/lib/license_finder/cli/licenses.rb @@ -7,19 +7,29 @@ class Licenses < Base include MakesDecisions auditable + method_option :version, desc: 'The version associated with the license' desc 'add DEPENDENCY LICENSE', "Set a dependency's licenses, overwriting any license_finder has found" def add(name, license) modifying { decisions.license(name, license, txn) } - printer.say "The #{name} dependency has been marked as using #{license} license!", :green + if options[:version] + printer.say "The #{name} dependency with version #{options[:version]} has been marked as using #{license} license!", :green + else + printer.say "The #{name} dependency has been marked as using #{license} license!", :green + end end auditable + method_option :version, desc: 'The version associated with the license' desc 'remove DEPENDENCY LICENSE', 'Remove a manually set license' def remove(dep, lic) modifying { decisions.unlicense(dep, lic, txn) } - printer.say "The dependency #{dep} no longer has a manual license" + if options[:version] + printer.say "The dependency #{dep} with version #{options[:version]} no longer has a manual license of #{lic}" + else + printer.say "The dependency #{dep} no longer has a manual license of #{lic}" + end end end end diff --git a/lib/license_finder/decision_applier.rb b/lib/license_finder/decision_applier.rb index e0e3d2e2..8a49956b 100644 --- a/lib/license_finder/decision_applier.rb +++ b/lib/license_finder/decision_applier.rb @@ -44,7 +44,7 @@ def ignored?(package) end def with_decided_licenses(package) - decisions.licenses_of(package.name).each do |license| + decisions.licenses_of(package.name, package.version).each do |license| package.decide_on_license license end package diff --git a/lib/license_finder/decisions.rb b/lib/license_finder/decisions.rb index b4849e47..294e43a4 100644 --- a/lib/license_finder/decisions.rb +++ b/lib/license_finder/decisions.rb @@ -11,8 +11,20 @@ class Decisions attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name, :inherited_decisions - def licenses_of(name) - @licenses[name] + ALL_VERSIONS = :all + private_constant :ALL_VERSIONS + + def licenses_of(name, version = nil) + return Set.new unless @licenses.key?(name) + + has_licenses_for_all_versions = @licenses[name].key?(ALL_VERSIONS) && @licenses[name].size == 1 + licenses_for_all_versions = has_licenses_for_all_versions ? @licenses[name][ALL_VERSIONS] : Set.new + + if version.nil? + licenses_for_all_versions + else + @licenses[name][version] || licenses_for_all_versions + end end def homepage_of(name) @@ -76,7 +88,7 @@ def self.from_hash(txn, versions) def initialize @decisions = [] @packages = Set.new - @licenses = Hash.new { |h, k| h[k] = Set.new } + @licenses = {} @homepages = {} @approvals = {} @permitted = Set.new @@ -100,13 +112,34 @@ def remove_package(name, txn = {}) def license(name, lic, txn = {}) add_decision [:license, name, lic, txn] - @licenses[name] << License.find_by_name(lic) + + versions = txn[:versions] + versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + + versions.each do |version| + @licenses[name] ||= {} + @licenses[name][version] ||= Set.new + @licenses[name][version] << License.find_by_name(lic) + end + self end def unlicense(name, lic, txn = {}) add_decision [:unlicense, name, lic, txn] - @licenses[name].delete(License.find_by_name(lic)) + + if @licenses[name] + versions = txn[:versions] + versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + + versions.each do |version| + if @licenses[name][version] + @licenses[name][version].delete(License.find_by_name(lic)) + @licenses[name].delete(version) if @licenses[name][version].empty? + end + end + end + self end diff --git a/spec/lib/license_finder/decision_applier_spec.rb b/spec/lib/license_finder/decision_applier_spec.rb index 68b79c9d..5ec8d04f 100644 --- a/spec/lib/license_finder/decision_applier_spec.rb +++ b/spec/lib/license_finder/decision_applier_spec.rb @@ -38,6 +38,16 @@ module LicenseFinder expect(decision_applier.acknowledged.last.licenses).to eq Set.new([License.find_by_name('MIT')]) end + it 'applies decided licenses for specific versions' do + decisions = Decisions.new + .add_package('manual', '2.0.0') + .license('manual', 'MIT', { versions: ['1.0.0'] }) + .license('manual', 'GPL', { versions: ['2.0.0'] }) + .license('manual', 'Apache-2.0', { versions: ['3.0.0'] }) + decision_applier = described_class.new(decisions: decisions, packages: []) + expect(decision_applier.acknowledged.last.licenses).to eq Set.new([License.find_by_name('GPL')]) + end + it 'applies decided homepage' do decisions = Decisions.new .add_package('manual', nil) diff --git a/spec/lib/license_finder/decisions_spec.rb b/spec/lib/license_finder/decisions_spec.rb index bf641229..dfacbd0a 100644 --- a/spec/lib/license_finder/decisions_spec.rb +++ b/spec/lib/license_finder/decisions_spec.rb @@ -42,6 +42,14 @@ module LicenseFinder expect(license).to eq License.find_by_name('MIT') end + it 'will report license for a dependency of any version' do + license = subject + .license('dep', 'MIT') + .licenses_of('dep', '1.0.0') + .first + expect(license).to eq License.find_by_name('MIT') + end + it 'will report multiple licenses' do licenses = subject .license('dep', 'MIT') @@ -60,6 +68,41 @@ module LicenseFinder .first expect(license).to eq License.find_by_name('MIT') end + + it 'supports specifying a package version' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to be_empty + expect(subject.licenses_of('dep', nil)).to be_empty + expect(subject.licenses_of('dep')).to be_empty + end + + it 'supports different licenses for different package versions' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + subject.license('dep', 'Apache-2.0', { versions: ['2.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set + expect(subject.licenses_of('dep', '2.0.0')).to eq [License.find_by_name('Apache-2.0')].to_set + end + + it 'ignores a license applied to all versions if any version-specific licenses are also defined' do + subject.license('dep', 'MIT', { versions: [] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', nil)).to eq [License.find_by_name('MIT')].to_set + + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to be_empty + expect(subject.licenses_of('dep', nil)).to be_empty + end end describe '.unlicense' do @@ -89,6 +132,34 @@ module LicenseFinder .first expect(license).to eq License.find_by_name('MIT') end + + it 'removes the license for the correct version' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: ['2.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to be_empty + end + + it 'reverts to the previous license for all versions after unlicensing all specific versions' do + subject.license('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + + subject.unlicense('dep', 'GPL', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to be_empty + end end describe '.homepage' do From be4c32baa2dbfd691d35d6bb4e4e16da06abbd94 Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Thu, 10 Nov 2022 11:50:08 -0500 Subject: [PATCH 17/20] Change implementation of how removing a license should work This makes the license optional when removing a license. When omitted, all licenses will be removed from the relevant package versions. --- .../configure/assign_licenses_spec.rb | 38 +++++++-- lib/license_finder/cli/licenses.rb | 17 ++-- lib/license_finder/decisions.rb | 40 +++------- lib/license_finder/manual_licenses.rb | 79 +++++++++++++++++++ spec/lib/license_finder/decisions_spec.rb | 66 +++++++++++----- 5 files changed, 177 insertions(+), 63 deletions(-) create mode 100644 lib/license_finder/manual_licenses.rb diff --git a/features/features/configure/assign_licenses_spec.rb b/features/features/configure/assign_licenses_spec.rb index 535debe0..a44fef4e 100644 --- a/features/features/configure/assign_licenses_spec.rb +++ b/features/features/configure/assign_licenses_spec.rb @@ -14,6 +14,7 @@ gem = developer.create_gem 'mislicensed_dep', license: 'Unknown' project.depend_on gem developer.execute_command 'license_finder licenses add mislicensed_dep Known' + expect(developer).to be_seeing('The mislicensed_dep dependency has been marked as using Known license!') developer.run_license_finder expect(developer).not_to be_seeing_something_like /mislicensed_dep.*Unknown/ @@ -22,17 +23,33 @@ specify 'can be removed, revealing the default license for a dependency' do project = developer.create_ruby_app - gem = developer.create_gem 'mislicensed_dep', license: 'Default' - project.depend_on gem - developer.execute_command 'license_finder licenses add mislicensed_dep Manual_license' + gem_a = developer.create_gem 'mislicensed_dep_a', license: 'Default' + gem_b = developer.create_gem 'mislicensed_dep_b', license: 'Default' + project.depend_on gem_a + project.depend_on gem_b + developer.execute_command 'license_finder licenses add mislicensed_dep_a Manual_license_a' + developer.execute_command 'license_finder licenses add mislicensed_dep_b Manual_license_b' + + developer.run_license_finder + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Manual_license_a/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Manual_license_b/ + + developer.execute_command 'license_finder licenses remove mislicensed_dep_a Manual_license_a' + expect(developer).to be_seeing('The dependency mislicensed_dep_a no longer has a manual license of Manual_license_a') + + developer.execute_command 'license_finder licenses remove mislicensed_dep_b Incorrect_license' + expect(developer).to be_seeing('The dependency mislicensed_dep_b no longer has a manual license of Incorrect_license') developer.run_license_finder - expect(developer).to be_seeing_something_like /mislicensed_dep.*Manual_license/ + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Manual_license_b/ - developer.execute_command 'license_finder licenses remove mislicensed_dep Manual_license' + developer.execute_command 'license_finder licenses remove mislicensed_dep_b' + expect(developer).to be_seeing('The dependency mislicensed_dep_b no longer has a manual license') developer.run_license_finder - expect(developer).to be_seeing_something_like /mislicensed_dep.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Default/ end specify 'can be assigned and removed by package version' do @@ -45,6 +62,7 @@ developer.execute_command 'license_finder licenses add dep_a GPL --version=1.0.0' developer.execute_command 'license_finder licenses add dep_a MIT --version=2.0.0' developer.execute_command 'license_finder licenses add dep_a GPL --version=3.0.0' + expect(developer).to be_seeing('The dep_a dependency with version 3.0.0 has been marked as using GPL license!') developer.execute_command 'license_finder licenses add dep_b GPL --version=1.0.0' developer.execute_command 'license_finder licenses add dep_b GPL --version=2.0.0' @@ -55,9 +73,17 @@ expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' developer.execute_command 'license_finder licenses remove dep_a MIT --version=2.0.0' + expect(developer).to be_seeing('The dependency dep_a with version 2.0.0 no longer has a manual license of MIT') developer.run_license_finder expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + + developer.execute_command 'license_finder licenses remove dep_b --version=3.0.0' + expect(developer).to be_seeing('The dependency dep_b with version 3.0.0 no longer has a manual license') + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, Unknown' end end diff --git a/lib/license_finder/cli/licenses.rb b/lib/license_finder/cli/licenses.rb index 9d876a39..d34c9849 100644 --- a/lib/license_finder/cli/licenses.rb +++ b/lib/license_finder/cli/licenses.rb @@ -12,24 +12,19 @@ class Licenses < Base def add(name, license) modifying { decisions.license(name, license, txn) } - if options[:version] - printer.say "The #{name} dependency with version #{options[:version]} has been marked as using #{license} license!", :green - else - printer.say "The #{name} dependency has been marked as using #{license} license!", :green - end + version_info = options[:version] ? " with version #{options[:version]}" : '' + printer.say "The #{name} dependency#{version_info} has been marked as using #{license} license!", :green end auditable method_option :version, desc: 'The version associated with the license' desc 'remove DEPENDENCY LICENSE', 'Remove a manually set license' - def remove(dep, lic) + def remove(dep, lic = nil) modifying { decisions.unlicense(dep, lic, txn) } - if options[:version] - printer.say "The dependency #{dep} with version #{options[:version]} no longer has a manual license of #{lic}" - else - printer.say "The dependency #{dep} no longer has a manual license of #{lic}" - end + version_info = options[:version] ? " with version #{options[:version]}" : '' + suffix = lic ? " of #{lic}" : '' + printer.say "The dependency #{dep}#{version_info} no longer has a manual license#{suffix}" end end end diff --git a/lib/license_finder/decisions.rb b/lib/license_finder/decisions.rb index 294e43a4..e1bc4a75 100644 --- a/lib/license_finder/decisions.rb +++ b/lib/license_finder/decisions.rb @@ -2,6 +2,7 @@ require 'open-uri' require 'license_finder/license' +require 'license_finder/manual_licenses' module LicenseFinder class Decisions @@ -11,20 +12,8 @@ class Decisions attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name, :inherited_decisions - ALL_VERSIONS = :all - private_constant :ALL_VERSIONS - def licenses_of(name, version = nil) - return Set.new unless @licenses.key?(name) - - has_licenses_for_all_versions = @licenses[name].key?(ALL_VERSIONS) && @licenses[name].size == 1 - licenses_for_all_versions = has_licenses_for_all_versions ? @licenses[name][ALL_VERSIONS] : Set.new - - if version.nil? - licenses_for_all_versions - else - @licenses[name][version] || licenses_for_all_versions - end + @manual_licenses.licenses_of(name, version) end def homepage_of(name) @@ -88,7 +77,7 @@ def self.from_hash(txn, versions) def initialize @decisions = [] @packages = Set.new - @licenses = {} + @manual_licenses = ManualLicenses.new @homepages = {} @approvals = {} @permitted = Set.new @@ -114,12 +103,11 @@ def license(name, lic, txn = {}) add_decision [:license, name, lic, txn] versions = txn[:versions] - versions = [ALL_VERSIONS] if versions.nil? || versions.empty? - versions.each do |version| - @licenses[name] ||= {} - @licenses[name][version] ||= Set.new - @licenses[name][version] << License.find_by_name(lic) + if versions.nil? || versions.empty? + @manual_licenses.assign_to_all_versions(name, lic) + else + @manual_licenses.assign_to_specific_versions(name, lic, versions) end self @@ -128,16 +116,12 @@ def license(name, lic, txn = {}) def unlicense(name, lic, txn = {}) add_decision [:unlicense, name, lic, txn] - if @licenses[name] - versions = txn[:versions] - versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + versions = txn[:versions] - versions.each do |version| - if @licenses[name][version] - @licenses[name][version].delete(License.find_by_name(lic)) - @licenses[name].delete(version) if @licenses[name][version].empty? - end - end + if versions.nil? || versions.empty? + @manual_licenses.unassign_from_all_versions(name, lic) + else + @manual_licenses.unassign_from_specific_versions(name, lic, versions) end self diff --git a/lib/license_finder/manual_licenses.rb b/lib/license_finder/manual_licenses.rb new file mode 100644 index 00000000..b8aac94d --- /dev/null +++ b/lib/license_finder/manual_licenses.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module LicenseFinder + class ManualLicenses + def initialize + @all_versions = {} + @specific_versions = {} + end + + def licenses_of(name, version = nil) + return @all_versions[name] if @all_versions[name] + + if version && @specific_versions[name] && @specific_versions[name][version] + @specific_versions[name][version] + else + Set.new + end + end + + def assign_to_all_versions(name, lic) + # Ex: licenses add foo_gem MIT => Adds MIT at "all" versions for this gem + + @all_versions[name] ||= Set.new + @all_versions[name] << to_license(lic) + + @specific_versions.delete(name) + end + + def assign_to_specific_versions(name, lic, versions) + # Ex: licenses add foo_gem MIT --version=1.0 => Adds MIT at only 1.0 for this gem + + @specific_versions[name] ||= {} + versions.each do |version| + @specific_versions[name][version] ||= Set.new + @specific_versions[name][version] << to_license(lic) + end + + @all_versions.delete(name) + end + + def unassign_from_all_versions(name, lic = nil) + if lic + # Ex: licenses remove foo_gem MIT => Removes MIT at all versions for this gem + @all_versions[name]&.delete(to_license(lic)) + + @specific_versions[name]&.each do |_version, licenses| + licenses.delete(to_license(lic)) + end + else + # Ex: licenses remove foo_gem => Removes all licenses for all versions of the gem + @all_versions.delete(name) + @specific_versions.delete(name) + end + end + + def unassign_from_specific_versions(name, lic, versions) + return unless @specific_versions[name] + + versions.each do |version| + if @specific_versions[name][version] + if lic + # Ex: licenses remove foo_gem MIT --version=1.0 => Removes MIT at only 1.0 for this gem + @specific_versions[name][version].delete(to_license(lic)) + @specific_versions[name].delete(version) if @specific_versions[name][version].empty? + else + # Ex: licenses remove foo_gem --version=1.0 => Removes all licenses at only 1.0 for the gem + @specific_versions[name].delete(version) + end + end + end + end + + private + + def to_license(lic) + License.find_by_name(lic) + end + end +end diff --git a/spec/lib/license_finder/decisions_spec.rb b/spec/lib/license_finder/decisions_spec.rb index dfacbd0a..d4238e82 100644 --- a/spec/lib/license_finder/decisions_spec.rb +++ b/spec/lib/license_finder/decisions_spec.rb @@ -123,6 +123,14 @@ module LicenseFinder expect(licenses).to eq [License.find_by_name('GPL')].to_set end + it 'will remove the license from all package versions' do + licenses = subject + .license('dep', 'MIT', { versions: ['1.0.0'] }) + .unlicense('dep', 'MIT') + .licenses_of('dep', '1.0.0') + expect(licenses).to be_empty + end + it 'is cumulative' do license = subject .license('dep', 'MIT') @@ -133,32 +141,54 @@ module LicenseFinder expect(license).to eq License.find_by_name('MIT') end - it 'removes the license for the correct version' do - subject.license('dep', 'MIT', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + it 'removes the license from a specific version' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep_b', 'GPL', { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_a', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set - subject.unlicense('dep', 'MIT', { versions: ['2.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + subject.unlicense('dep_a', 'MIT', { versions: ['1.0.0'] }) + subject.unlicense('dep_b', 'MIT', { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to be_empty + expect(subject.licenses_of('dep_a', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [License.find_by_name('GPL')].to_set end - it 'reverts to the previous license for all versions after unlicensing all specific versions' do - subject.license('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + it 'removes all licenses for all versions when license is omitted' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) - subject.license('dep', 'GPL', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [License.find_by_name('MIT')].to_set - subject.unlicense('dep', 'GPL', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + subject.unlicense('dep_a', nil, { versions: [] }) + subject.unlicense('dep_b', nil, { versions: [] }) + + expect(subject.licenses_of('dep_a')).to be_empty + expect(subject.licenses_of('dep_b', '1.0.0')).to be_empty + end + + it 'removes all licenses for a specific version when license is omitted' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep_b', 'GPL', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set + + subject.unlicense('dep_a', nil, { versions: ['1.0.0'] }) + subject.unlicense('dep_b', nil, { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to be_empty + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to be_empty end end From 03fb221e9db236edb35bd67f8e532be67b1a20d7 Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Fri, 11 Nov 2022 13:48:03 -0500 Subject: [PATCH 18/20] Update documentation on adding/removing manual licenses --- README.md | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fdd6768c..8dc450a1 100644 --- a/README.md +++ b/README.md @@ -345,12 +345,40 @@ you should manually research what the actual license is. When you have established the real license, you can record it with: ```sh -$ license_finder licenses add my_unknown_dependency MIT --homepage="www.unknown-code.org" +$ license_finder licenses add my_unknown_dependency MIT ``` -This command would assign the MIT license to the dependency -`my_unknown_dependency`. It will also set its homepage to `www.unknown-code.org`. +This command would assign the MIT license to all versions of the dependency +`my_unknown_dependency`. If you prefer, you could instead assign the license +to only a specific version of the dependency: +```sh +$ license_finder licenses add my_unknown_dependency MIT --version=1.0.0 +``` + +Please note that adding a license to a specific version of a dependency will +cause any licenses previously added to all versions of that dependency to be +forgotten. Similarly, adding a license to all versions of a dependency will +override any licenses previously added to specific versions of that dependency. + +There are several ways in which you can remove licenses that were previously +added through the `licenses add` command: + +```sh +# Removes all licenses from any version of the dependency +$ license_finder licenses remove my_unknown_dependency + +# Removes just the MIT license from any version of the dependency +$ license_finder licenses remove my_unknown_dependency MIT + +# Removes all licenses from only version 1.0.0 of the dependency +# This has no effect if you had last added a license to all versions of the dependency +$ license_finder licenses remove my_unknown_dependency --version=1.0.0 + +# Removes just the MIT license from only version 1.0.0 of the dependency +# This has no effect if you had last added a license to all versions of the dependency +$ license_finder licenses remove my_unknown_dependency MIT --version=1.0.0 +``` ### Adding Hidden Dependencies From 7ac7a505524c1b08a2679f50ad13bd0c7ecc1973 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Wed, 23 Nov 2022 15:47:29 +0000 Subject: [PATCH 19/20] test: update broken spec tests for PNPM --- features/support/testing_dsl.rb | 5 ++++- lib/license_finder/package_managers/pnpm.rb | 2 +- .../license_finder/package_managers/pnpm_spec.rb | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/features/support/testing_dsl.rb b/features/support/testing_dsl.rb index 70afa978..4bfb9a03 100644 --- a/features/support/testing_dsl.rb +++ b/features/support/testing_dsl.rb @@ -225,9 +225,12 @@ def add_dep class PNPMProject < Project def add_dep - add_to_file('pnpm-lock.yaml', '') add_to_file('package.json', '{"dependencies" : {"http-server": "0.11.1"}}') end + + def install + shell_out('pnpm install 2>/dev/null') + end end class MavenProject < Project diff --git a/lib/license_finder/package_managers/pnpm.rb b/lib/license_finder/package_managers/pnpm.rb index db5c0f3b..61c402ae 100644 --- a/lib/license_finder/package_managers/pnpm.rb +++ b/lib/license_finder/package_managers/pnpm.rb @@ -95,7 +95,7 @@ def project_has_workspaces # PNPM introduced the licenses command in 7.17.0 def supported_pnpm? Dir.chdir(project_path) do - version_string, stderr_str, status = Cmd.run('pnpm -v') + version_string, stderr_str, status = Cmd.run('pnpm --version') raise "Command 'pnpm -v' failed to execute: #{stderr_str}" unless status.success? version = version_string.split('.').map(&:to_i) diff --git a/spec/lib/license_finder/package_managers/pnpm_spec.rb b/spec/lib/license_finder/package_managers/pnpm_spec.rb index d0fac67f..772a4ace 100644 --- a/spec/lib/license_finder/package_managers/pnpm_spec.rb +++ b/spec/lib/license_finder/package_managers/pnpm_spec.rb @@ -49,7 +49,7 @@ module LicenseFinder context 'when using PNPM 6 throws error' do before do - allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['6.1.4', '', cmd_success]) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm --version').and_return(['6.1.4', '', cmd_success]) end context 'when the shell command fails' do @@ -63,7 +63,7 @@ module LicenseFinder context 'when using PNPM projects' do before do - allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['2.0.1', '', cmd_success]) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm --version').and_return(['7.17.0', '', cmd_success]) end it 'should call pnpm install with no cli parameters' do @@ -91,7 +91,7 @@ module LicenseFinder context 'when in a PNPM project' do before do - allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm --version').and_return(['7.17.0', '', cmd_success]) end subject { PNPM.new(project_path: Pathname(root), logger: double(:logger, active: nil)) } @@ -108,16 +108,16 @@ module LicenseFinder before do FileUtils.mkdir_p(Dir.tmpdir) FileUtils.mkdir_p(root) - allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm --version').and_return(['7.17.0', '', cmd_success]) end - context 'when using PNPM 7.14.1+' do + context 'when using PNPM 7.17.0+' do before do - allow(SharedHelpers::Cmd).to receive(:run).with('pnpm -v').and_return(['7.14.1', '', cmd_success]) + allow(SharedHelpers::Cmd).to receive(:run).with('pnpm --version').and_return(['7.17.0', '', cmd_success]) end end - it 'displays packages as returned from "pmpm list"' do + it 'displays packages as returned from "pnpm licenses list"' do allow(SharedHelpers::Cmd).to receive(:run).with(PNPM::SHELL_COMMAND + " --no-color --dir #{Pathname(root)}") do [pnpm_shell_command_output, '', cmd_success] end From 86198ef61b5a337ab0ca6811f3999e3b65078311 Mon Sep 17 00:00:00 2001 From: Weyert de Boer Date: Wed, 23 Nov 2022 15:47:48 +0000 Subject: [PATCH 20/20] docs: reference PNPM in `CONTRIBUTING.md` --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 13996d76..e6666cd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,6 +78,7 @@ If you come up with something useful, consider posting it to the Google Group To successfully run the test suite, you will need the following installed: - NPM (requires Node) - Yarn (requires Node) +- PNPM (requires Node) - Bower (requires Node and NPM) - Maven (requires Java) - Gradle (requires Java)