add yarn enumerator for berry version

This commit is contained in:
Jon Ruskin 2021-11-26 19:55:45 -07:00
Родитель 50ae9ab68d
Коммит 8debc0ea16
11 изменённых файлов: 1308 добавлений и 4 удалений

30
.github/workflows/test.yml поставляемый
Просмотреть файл

@ -577,3 +577,33 @@ jobs:
run: script/source-setup/yarn/v1
- name: Run tests
run: script/test yarn/v1
yarn-berry:
runs-on: ubuntu-latest
needs: core
steps:
- uses: actions/checkout@v2
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 12
- name: Install Yarn
run: npm install -g yarn
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 2.6
- run: bundle lock
- uses: actions/cache@v2
name: cache gem dependencies
with:
path: vendor/gems
key: ${{ runner.os }}-gem-2.6-${{ hashFiles('**/Gemfile.lock') }}
restore-keys: |
${{ runner.os }}-gem-2.6-
- name: Bootstrap
run: script/bootstrap
- name: Set up fixtures
run: script/source-setup/yarn/berry
- name: Run tests
run: script/test yarn/berry

5
.gitignore поставляемый
Просмотреть файл

@ -45,6 +45,11 @@ test/fixtures/mix/mix.lock
test/fixtures/yarn/*/*
!test/fixtures/yarn/*/package.json
!test/fixtures/yarn/*/yarn.lock
!test/fixtures/yarn/*/.yarn
!test/fixtures/yarn/*/.yarnrc.yml
test/fixtures/yarn/*/.yarn/*
!test/fixtures/yarn/*/.yarn/releases
!test/fixtures/yarn/*/.yarn/plugins
test/fixtures/nuget/obj/*

Просмотреть файл

@ -2,13 +2,14 @@
The yarn source will detect dependencies when `package.json` and `yarn.lock` are found at an app's `source_path`.
It uses `yarn list` to enumerate dependencies and `yarn info` to get metadata on each package.
It uses the `yarn` CLI commands to enumerate dependencies and gather metadata on each package.
### Including development dependencies
## Including development dependencies
Yarn versions < 1.3.0 will always include non-production dependencies due to a bug in those yarn versions.
**Note** Yarn versions < 1.3.0 will always include non-production dependencies due to a bug in those versions of yarn.
**Note** Yarn versions > 2.0 will always include non-production dependencies due to lack of filtering of production vs non-production dependencies in the yarn CLI.
Starting with yarn version >= 1.3.0, the yarn source excludes non-production dependencies by default. To include development and test dependencies, set `production_only: false` in `.licensed.yml`.
For yarn versions between 1.3.0 and 2.0, the yarn source excludes non-production dependencies by default. To include development and test dependencies in these versions, set `production_only: false` in `.licensed.yml`.
```yml
yarn:

Просмотреть файл

@ -0,0 +1,79 @@
# frozen_string_literal: true
require "json"
module Licensed
module Sources
class Yarn::Berry < Source
include Licensed::Sources::Yarn
def self.version_requirement
Gem::Requirement.new(">= 2.0")
end
def enumerate_dependencies
packages.map do |name, package|
Dependency.new(
name: name,
version: package["version"],
path: package["path"],
metadata: {
"type" => self.class.type,
"name" => package["name"],
"homepage" => package["homepage"]
}
)
end
end
# Finds packages that the current project relies on based on the output from `yarn info`
def packages
# parse all lines of output to json and find one that is "type": "tree"
yarn_info = JSON.parse("[#{yarn_info_command.lines.join(",")}]")
mapped_packages = yarn_info.reduce({}) do |accum, package|
name, _ = package["value"].rpartition("@")
version = package.dig("children", "Version")
id = "#{name}-#{version}"
accum[name] ||= []
accum[name] << {
"id" => id,
"name" => name,
"version" => version,
"homepage" => package.dig("children", "Manifest", "Homepage"),
"path" => dependency_paths[id]
}
accum
end
mapped_packages.each_with_object({}) do |(name, results), hsh|
results.uniq! { |package| package["version"] }
if results.size == 1
# if there is only one package for a name, reference it by name
hsh[name] = results[0]
else
# if there is more than one package for a name, reference each by id
results.each do |package|
hsh[package["id"]] = package
end
end
end
end
# Returns a hash that maps all dependency names to their location on disk
# by parsing every package.json file under node_modules.
def dependency_paths
@dependency_paths ||= Dir.glob(config.pwd.join("node_modules/**/package.json")).each_with_object({}) do |file, hsh|
dirname = File.dirname(file)
json = JSON.parse(File.read(file))
hsh["#{json["name"]}-#{json["version"]}"] = dirname
end
end
# Returns the output from running `yarn list` to get project dependencies
def yarn_info_command
args = %w(--json --manifest --recursive --all)
Licensed::Shell.execute("yarn", "info", *args)
end
end
end
end

17
script/source-setup/yarn/berry Executable file
Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/bash
set -e
if [ -z "$(which yarn)" ]; then
echo "A local yarn installation is required for yarn development." >&2
exit 127
fi
# setup test fixtures
BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd $BASE_PATH/test/fixtures/yarn/berry
if [ "$1" == "-f" ]; then
git clean -fX .
fi
yarn install

2
test/fixtures/command/yarn.yml поставляемый
Просмотреть файл

@ -4,3 +4,5 @@ sources:
apps:
- source_path: test/fixtures/yarn/v1
- source_path: test/fixtures/yarn/berry

768
test/fixtures/yarn/berry/.yarn/releases/yarn-3.1.1.cjs поставляемый Executable file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
test/fixtures/yarn/berry/.yarnrc.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
yarnPath: .yarn/releases/yarn-3.1.1.cjs
nodeLinker: node-modules

15
test/fixtures/yarn/berry/package.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
{
"name": "fixtures",
"version": "1.0.0",
"dependencies": {
"@github/query-selector": "1.0.3",
"autoprefixer": "5.2.0"
},
"devDependencies": {
"string.prototype.startswith": "0.2.0"
},
"description": "yarn test fixture",
"repository": "https://github.com/github/licensed",
"license": "MIT",
"packageManager": "yarn@3.1.1"
}

270
test/fixtures/yarn/berry/yarn.lock поставляемый Normal file
Просмотреть файл

@ -0,0 +1,270 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 5
cacheKey: 8
"@github/query-selector@npm:1.0.3":
version: 1.0.3
resolution: "@github/query-selector@npm:1.0.3"
checksum: bfce8e6e4ccbcc4706533bd972a1e8d57c0e8f81f1f8c62e2fef5610a8cb68491b5b07ed5a9d3516368c0d48e9104ad1040e0ca0d7b668f8fca477924d21b54e
languageName: node
linkType: hard
"amdefine@npm:>=0.0.4":
version: 1.0.1
resolution: "amdefine@npm:1.0.1"
checksum: 9d4e15b94641643a9385b2841b4cb2bcf4e8e2f741ea4bd475c93ad7bab261ad4ed827a32e9c549b38b98759c4526c173ae4e6dde8caeb75ee5cebedc9863762
languageName: node
linkType: hard
"autoprefixer-core@npm:~5.2.0":
version: 5.2.1
resolution: "autoprefixer-core@npm:5.2.1"
dependencies:
browserslist: ~0.4.0
caniuse-db: ^1.0.30000214
num2fraction: ^1.1.0
postcss: ~4.1.12
checksum: 417101f536926b67a071b76c8bf7e60281c7e2b785bd1f718536ab9fd9919c17fefc4e529d646a390886ddb0a1e6c2f96c2330ec8fea1b0a3ba4b1df06e5ec55
languageName: node
linkType: hard
"autoprefixer@npm:5.2.0":
version: 5.2.0
resolution: "autoprefixer@npm:5.2.0"
dependencies:
autoprefixer-core: ~5.2.0
fs-extra: ~0.18.4
postcss: ~4.1.11
bin:
autoprefixer: autoprefixer
checksum: 09afe11f92cf563cd6c8d576eca042748abe11b719c8134c815627d1172f7c34e33b43915868a35d11badb9407c16665a16993c438d2bf9c95ed50a92f7666b9
languageName: node
linkType: hard
"balanced-match@npm:^1.0.0":
version: 1.0.0
resolution: "balanced-match@npm:1.0.0"
checksum: 9b67bfe558772f40cf743a3469b48b286aecec2ea9fe80c48d74845e53aab1cef524fafedf123a63019b49ac397760573ef5f173f539423061f7217cbb5fbd40
languageName: node
linkType: hard
"brace-expansion@npm:^1.1.7":
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
dependencies:
balanced-match: ^1.0.0
concat-map: 0.0.1
checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07
languageName: node
linkType: hard
"browserslist@npm:~0.4.0":
version: 0.4.0
resolution: "browserslist@npm:0.4.0"
dependencies:
caniuse-db: ^1.0.30000153
checksum: 4c6351329e1d19095760850e0e8bf8ac6d12dd6ae23a21cba0a8aa24d7f5debf7c0bf4b52fee401c636254d1857c78121b0eea0d66ae806e7ab65f4672c41774
languageName: node
linkType: hard
"caniuse-db@npm:^1.0.30000153, caniuse-db@npm:^1.0.30000214":
version: 1.0.30001110
resolution: "caniuse-db@npm:1.0.30001110"
checksum: 903b03463e10b97a79627f11abac9400db22e3ad8509a81d92c4e95173fa6fb75d5dc97cde650238f954aebac7a68824ba522ffaa1e8dca1014fc5dd4cc1374a
languageName: node
linkType: hard
"concat-map@npm:0.0.1":
version: 0.0.1
resolution: "concat-map@npm:0.0.1"
checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af
languageName: node
linkType: hard
"es6-promise@npm:~2.3.0":
version: 2.3.0
resolution: "es6-promise@npm:2.3.0"
checksum: 4efbddb13a6b9563aa35ac13f7c74196e8ba2ad950c479f78d1203c93c60bc4c94289d4ddfda3d9db34cba6afc17fff383744b2a64c4617d661cf21042d4881f
languageName: node
linkType: hard
"fixtures@workspace:.":
version: 0.0.0-use.local
resolution: "fixtures@workspace:."
dependencies:
"@github/query-selector": 1.0.3
autoprefixer: 5.2.0
string.prototype.startswith: 0.2.0
languageName: unknown
linkType: soft
"fs-extra@npm:~0.18.4":
version: 0.18.4
resolution: "fs-extra@npm:0.18.4"
dependencies:
graceful-fs: ^3.0.5
jsonfile: ^2.0.0
rimraf: ^2.2.8
checksum: 4a506a916747ad3b16b3350c4de0cc9217042cc5473cead766a67d71f82db236e5b17fadcd8827fcd7df0f34fef8a7ccdd4bfd3300868293ea7f26634d1917a3
languageName: node
linkType: hard
"fs.realpath@npm:^1.0.0":
version: 1.0.0
resolution: "fs.realpath@npm:1.0.0"
checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0
languageName: node
linkType: hard
"glob@npm:^7.1.3":
version: 7.1.6
resolution: "glob@npm:7.1.6"
dependencies:
fs.realpath: ^1.0.0
inflight: ^1.0.4
inherits: 2
minimatch: ^3.0.4
once: ^1.3.0
path-is-absolute: ^1.0.0
checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6
languageName: node
linkType: hard
"graceful-fs@npm:^3.0.5":
version: 3.0.12
resolution: "graceful-fs@npm:3.0.12"
dependencies:
natives: ^1.1.3
checksum: c7c6c333a174d07492823fc2ff9231fb9247eac182831dce795dfd624e74b1376649cd0682f8d21ada648bf7b4c72bb497c98b4028b410ae37e5ad289d762824
languageName: node
linkType: hard
"graceful-fs@npm:^4.1.6":
version: 4.2.4
resolution: "graceful-fs@npm:4.2.4"
checksum: 9d58c444eb4f391ce30b451aae8a8af2bd675d9f6f624719e97306f571ab89b2bd2b5f9025199bc63a2edfe2e53e7701554012f32a708148d53aa689163728cc
languageName: node
linkType: hard
"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
dependencies:
once: ^1.3.0
wrappy: 1
checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd
languageName: node
linkType: hard
"inherits@npm:2":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
languageName: node
linkType: hard
"js-base64@npm:~2.1.8":
version: 2.1.9
resolution: "js-base64@npm:2.1.9"
checksum: 9a90987f413a361a9dbb9dd07bf7f1553339aa8640f7e7025d7ba652f31eb59ea4d7854f77b403a728026adc1ae9192c7139a10b3f9d9f8df10fac3ffec97f4e
languageName: node
linkType: hard
"jsonfile@npm:^2.0.0":
version: 2.4.0
resolution: "jsonfile@npm:2.4.0"
dependencies:
graceful-fs: ^4.1.6
dependenciesMeta:
graceful-fs:
optional: true
checksum: f5064aabbc9e35530dc471d8b203ae1f40dbe949ddde4391c6f6a6d310619a15f0efdae5587df594d1d70c555193aaeee9d2ed4aec9ffd5767bd5e4e62d49c3d
languageName: node
linkType: hard
"minimatch@npm:^3.0.4":
version: 3.0.4
resolution: "minimatch@npm:3.0.4"
dependencies:
brace-expansion: ^1.1.7
checksum: 66ac295f8a7b59788000ea3749938b0970344c841750abd96694f80269b926ebcafad3deeb3f1da2522978b119e6ae3a5869b63b13a7859a456b3408bd18a078
languageName: node
linkType: hard
"natives@npm:^1.1.3":
version: 1.1.6
resolution: "natives@npm:1.1.6"
checksum: f9823dddba1eb71dc34f7e684d6d2bb39e709bdc54c42e89f27ec4a87966564644d88d2ea332ddfb91d00129a03d8e73abc35b09a0d985a80d6eaad95f3c1887
languageName: node
linkType: hard
"num2fraction@npm:^1.1.0":
version: 1.2.2
resolution: "num2fraction@npm:1.2.2"
checksum: 1da9c6797b505d3f5b17c7f694c4fa31565bdd5c0e5d669553253aed848a580804cd285280e8a73148bd9628839267daee4967f24b53d4e893e44b563e412635
languageName: node
linkType: hard
"once@npm:^1.3.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
wrappy: 1
checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
languageName: node
linkType: hard
"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8
languageName: node
linkType: hard
"postcss@npm:~4.1.11, postcss@npm:~4.1.12":
version: 4.1.16
resolution: "postcss@npm:4.1.16"
dependencies:
es6-promise: ~2.3.0
js-base64: ~2.1.8
source-map: ~0.4.2
checksum: e6b952b5d6ca68f49bcb8ecb961327d69f1917bb7f3513c479ca986c02a2669ad1986262909031477aea219833c015a49d33f0f3379858254a86da6252a5ed59
languageName: node
linkType: hard
"rimraf@npm:^2.2.8":
version: 2.7.1
resolution: "rimraf@npm:2.7.1"
dependencies:
glob: ^7.1.3
bin:
rimraf: ./bin.js
checksum: cdc7f6eacb17927f2a075117a823e1c5951792c6498ebcce81ca8203454a811d4cf8900314154d3259bb8f0b42ab17f67396a8694a54cae3283326e57ad250cd
languageName: node
linkType: hard
"source-map@npm:~0.4.2":
version: 0.4.4
resolution: "source-map@npm:0.4.4"
dependencies:
amdefine: ">=0.0.4"
checksum: b31992fcb4a2a6c335617f187bd36f392896dfcc111830ebdb8b716923cf6554b665833b975fc998bdf3a63881b2c8b4c5c34fda0280357b8c18fe6aa5d148ea
languageName: node
linkType: hard
"string.prototype.startswith@npm:0.2.0":
version: 0.2.0
resolution: "string.prototype.startswith@npm:0.2.0"
checksum: b4224e2454c276baf0609c76f5db0ee6355a89a649041df9a2e49b4cdf4b424dbebbe2c070a3b5f6f00594f8f8fa98415799c7211e873e13f6ee007daae73516
languageName: node
linkType: hard
"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"
checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
languageName: node
linkType: hard

Просмотреть файл

@ -0,0 +1,115 @@
# frozen_string_literal: true
require "test_helper"
require "tmpdir"
require "fileutils"
if Licensed::Shell.tool_available?("yarn")
describe Licensed::Sources::Yarn::Berry do
let(:config) { Licensed::AppConfiguration.new({ "source_path" => Dir.pwd }) }
let(:fixtures) { File.expand_path("../../../fixtures/yarn/berry", __FILE__) }
let(:source) { Licensed::Sources::Yarn::Berry.new(config) }
describe "enabled?" do
it "is true if package.json and yarn.lock exists" do
Dir.chdir(fixtures) do
assert source.enabled?
end
end
it "is false if package.json does not exist" do
source.stubs(:yarn_version).returns(Gem::Version.new("2"))
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
File.write "yarn.lock", ""
refute source.enabled?
end
end
end
it "is false if yarn.lock does not exist" do
source.stubs(:yarn_version).returns(Gem::Version.new("2"))
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
File.write "package.json", ""
refute source.enabled?
end
end
end
it "is false if the local yarn version is < 2.0" do
source.stubs(:yarn_version).returns(Gem::Version.new("1"))
Dir.chdir(fixtures) do
refute source.enabled?
end
end
end
describe "dependencies" do
it "includes declared dependencies" do
Dir.chdir fixtures do
dep = source.dependencies.detect { |d| d.name == "autoprefixer" }
assert dep
assert_equal "yarn", dep.record["type"]
assert_equal "5.2.0", dep.version
end
end
it "handles scoped dependency names" do
Dir.chdir fixtures do
dep = source.dependencies.detect { |d| d.name == "@github/query-selector" }
assert dep
assert_equal "1.0.3", dep.version
end
end
it "includes indirect dependencies" do
Dir.chdir fixtures do
assert source.dependencies.detect { |dep| dep.name == "autoprefixer-core" }
end
end
it "does not include ignored dependencies" do
Dir.chdir fixtures do
config.ignore({ "type" => Licensed::Sources::Yarn::V1.type, "name" => "autoprefixer" })
refute source.dependencies.detect { |dep| dep.name == "autoprefixer" }
end
end
describe "with multiple instances of a dependency" do
it "includes version in the dependency name for multiple unique versions" do
Dir.chdir fixtures do
graceful_fs_dependencies = source.dependencies.select { |dep| dep.name == "graceful-fs" }
assert_empty graceful_fs_dependencies
graceful_fs_dependencies = source.dependencies.select { |dep| dep.name =~ /graceful-fs/ }
assert_equal 2, graceful_fs_dependencies.size
graceful_fs_dependencies.each do |dependency|
assert_equal "#{dependency.record["name"]}-#{dependency.version}", dependency.name
assert dependency.exist?
end
end
end
it "does not include version in the dependency name for a single unique version" do
Dir.chdir fixtures do
dep = source.dependencies.detect { |d| d.name == "wrappy" }
assert dep
assert_equal "wrappy", dep.name
end
end
end
end
describe "packages" do
it "raises an error if no packages are found" do
Dir.mktmpdir do |dir|
Dir.chdir dir do
assert_raises ::Licensed::Shell::Error do
source.packages
end
end
end
end
end
end
end