Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly infer .npmrc for PNPM #8094

Merged
merged 3 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ class NpmrcBuilder

SCOPED_REGISTRY = /^\s*@(?<scope>\S+):registry\s*=\s*(?<registry>\S+)/

def initialize(dependency_files:, credentials:)
def initialize(dependency_files:, credentials:, dependencies: [])
@dependency_files = dependency_files
@credentials = credentials
@dependencies = dependencies
end

# PROXY WORK
Expand Down Expand Up @@ -52,7 +53,7 @@ def yarnrc_content

private

attr_reader :dependency_files, :credentials
attr_reader :dependency_files, :credentials, :dependencies

def build_npmrc_content_from_lockfile
return unless yarn_lock || package_lock
Expand Down Expand Up @@ -134,6 +135,17 @@ def dependency_urls
return @dependency_urls if defined?(@dependency_urls)

@dependency_urls = []

if dependencies.any?
@dependency_urls = dependencies.map do |dependency|
UpdateChecker::RegistryFinder.new(
dependency: dependency,
credentials: credentials
).dependency_url
end
return @dependency_urls
end

if package_lock
@dependency_urls +=
package_lock.content.scan(/"resolved"\s*:\s*"(.*)"/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Dependabot
module NpmAndYarn
class FileUpdater
class PnpmLockfileUpdater
require_relative "npmrc_builder"
require_relative "package_json_updater"

def initialize(dependencies:, dependency_files:, repo_contents_path:, credentials:)
Expand Down Expand Up @@ -38,6 +39,8 @@ def updated_pnpm_lock_content(pnpm_lock)

def run_pnpm_update(pnpm_lock:)
SharedHelpers.in_a_temporary_repo_directory(base_dir, repo_contents_path) do
File.write(".npmrc", npmrc_content(pnpm_lock))

SharedHelpers.with_git_configured(credentials: credentials) do
run_pnpm_updater

Expand Down Expand Up @@ -120,6 +123,14 @@ def write_final_package_json_files
end
end

def npmrc_content(pnpm_lock)
NpmrcBuilder.new(
credentials: credentials,
dependency_files: dependency_files,
dependencies: lockfile_dependencies(pnpm_lock)
).npmrc_content
end

def updated_package_json_content(file)
@updated_package_json_content ||= {}
@updated_package_json_content[file.name] ||=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
let(:npmrc_builder) do
described_class.new(
dependency_files: dependency_files,
credentials: credentials
credentials: credentials,
dependencies: dependencies
)
end

Expand All @@ -22,56 +23,58 @@
}]
end

let(:dependencies) do
[]
end

describe "#npmrc_content" do
subject(:npmrc_content) { npmrc_builder.npmrc_content }

context "with a yarn.lock" do
context "with no private sources and no credentials" do
let(:dependency_files) { project_dependency_files("yarn/simple") }

it { is_expected.to eq("") }

context "and an npmrc file" do
let(:dependency_files) { project_dependency_files("yarn/npmrc_auth_token") }

it "returns the npmrc file unaltered" do
expect(npmrc_content)
.to eq(fixture("projects", "yarn", "npmrc_auth_token", ".npmrc"))
end
context "with an npmrc file" do
let(:dependency_files) { project_dependency_files("generic/npmrc_auth_token") }

context "that needs an authToken sanitizing" do
let(:dependency_files) { project_dependency_files("yarn/npmrc_env_auth_token") }

it "removes the env variable use" do
expect(npmrc_content)
.to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n")
end
end
it "returns the npmrc file unaltered" do
expect(npmrc_content)
.to eq(fixture("projects", "generic", "npmrc_auth_token", ".npmrc"))
end

context "that needs an auth sanitizing" do
let(:dependency_files) { project_dependency_files("yarn/npmrc_env_auth") }
context "that needs an authToken sanitizing" do
let(:dependency_files) { project_dependency_files("generic/npmrc_env_auth_token") }

it "removes the env variable use" do
expect(npmrc_content)
.to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n")
end
end
it "removes the env variable use" do
expect(npmrc_content)
.to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n")
end
end

context "and a yarnrc file" do
let(:dependency_files) { project_dependency_files("yarn/yarnrc_global_registry") }
context "that needs an auth sanitizing" do
let(:dependency_files) { project_dependency_files("generic/npmrc_env_auth") }

it "uses the yarnrc file registry" do
expect(npmrc_content).to eq(
"registry = https://npm-proxy.fury.io/password/dependabot/\n"
)
end
it "removes the env variable use" do
expect(npmrc_content)
.to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n")
end
end
end

context "with no private sources and some credentials" do
let(:dependency_files) { project_dependency_files("yarn/simple") }
context "with no private sources and some credentials" do
let(:dependency_files) { project_dependency_files("generic/simple") }

let(:credentials) do
[{
"type" => "git_source",
"host" => "github.com",
"username" => "x-access-token",
"password" => "token"
}, {
"type" => "npm_registry",
"registry" => "registry.npmjs.org",
"token" => "my_token"
}]
end
it { is_expected.to eq("//registry.npmjs.org/:_authToken=my_token") }

context "and using basic auth" do
let(:credentials) do
[{
"type" => "git_source",
Expand All @@ -81,39 +84,41 @@
}, {
"type" => "npm_registry",
"registry" => "registry.npmjs.org",
"token" => "my_token"
"token" => "my:token"
}]
end
it { is_expected.to eq("//registry.npmjs.org/:_authToken=my_token") }
it "includes Basic auth details" do
expect(npmrc_content).to eq(
"always-auth = true\n//registry.npmjs.org/:_auth=bXk6dG9rZW4="
)
end
end

context "that uses basic auth" do
let(:credentials) do
[{
"type" => "git_source",
"host" => "github.com",
"username" => "x-access-token",
"password" => "token"
}, {
"type" => "npm_registry",
"registry" => "registry.npmjs.org",
"token" => "my:token"
}]
end
it "includes Basic auth details" do
expect(npmrc_content).to eq(
"always-auth = true\n//registry.npmjs.org/:_auth=bXk6dG9rZW4="
)
end
context "and an npmrc file" do
let(:dependency_files) { project_dependency_files("generic/npmrc_auth_token") }

it "appends to the npmrc file" do
expect(npmrc_content)
.to include(fixture("projects", "generic", "npmrc_auth_token", ".npmrc"))
expect(npmrc_content)
.to end_with("\n\n//registry.npmjs.org/:_authToken=my_token")
end
end
end

context "and an npmrc file" do
let(:dependency_files) { project_dependency_files("yarn/npmrc_auth_token") }
context "with a yarn.lock" do
context "with no private sources and no credentials" do
let(:dependency_files) { project_dependency_files("yarn/simple") }

it "appends to the npmrc file" do
expect(npmrc_content)
.to include(fixture("projects", "yarn", "npmrc_auth_token", ".npmrc"))
expect(npmrc_content)
.to end_with("\n\n//registry.npmjs.org/:_authToken=my_token")
it { is_expected.to eq("") }

context "and a yarnrc file" do
let(:dependency_files) { project_dependency_files("yarn/yarnrc_global_registry") }

it "uses the yarnrc file registry" do
expect(npmrc_content).to eq(
"registry = https://npm-proxy.fury.io/password/dependabot/\n"
)
end
end
end
Expand Down Expand Up @@ -459,59 +464,6 @@
end

context "with a package-lock.json" do
context "with no private sources and no credentials" do
let(:dependency_files) { project_dependency_files("npm6/simple") }

it { is_expected.to eq("") }

context "and an npmrc file" do
let(:dependency_files) { project_dependency_files("npm6/npmrc_auth_token") }

it "returns the npmrc file unaltered" do
expect(npmrc_content)
.to eq(fixture("projects", "npm6", "npmrc_auth_token", ".npmrc"))
end

context "that need sanitizing" do
let(:dependency_files) { project_dependency_files("npm6/npmrc_env_auth_token") }

it "removes the env variable use" do
expect(npmrc_content)
.to eq("@dependabot:registry=https://npm.fury.io/dependabot/\n")
end
end
end
end

context "with no private sources and some credentials" do
let(:dependency_files) { project_dependency_files("npm6/simple") }

let(:credentials) do
[{
"type" => "git_source",
"host" => "github.com",
"username" => "x-access-token",
"password" => "token"
}, {
"type" => "npm_registry",
"registry" => "registry.npmjs.org",
"token" => "my_token"
}]
end
it { is_expected.to eq("//registry.npmjs.org/:_authToken=my_token") }

context "and an npmrc file" do
let(:dependency_files) { project_dependency_files("npm6/npmrc_auth_token") }

it "appends to the npmrc file" do
expect(npmrc_content)
.to include(fixture("projects", "npm6", "npmrc_auth_token", ".npmrc"))
expect(npmrc_content)
.to end_with("\n\n//registry.npmjs.org/:_authToken=my_token")
end
end
end

context "with no private sources and credentials cleared" do
let(:dependency_files) { project_dependency_files("npm6/private_source") }

Expand Down Expand Up @@ -830,6 +782,41 @@
end
end

context "with a pnpm-lock.yaml" do
let(:dependency_files) { project_dependency_files("pnpm/private_source") }
let(:dependencies) do
[
Dependabot::Dependency.new(name: "@dependabot/etag", version: "1.8.1", package_manager: "npm_and_yarn",
requirements: []),
Dependabot::Dependency.new(name: "semver", version: "7.5.4", package_manager: "npm_and_yarn",
requirements: [])
]
end

context "and a private registry configured that lists a specific dependency" do
let(:credentials) do
[{
"type" => "npm_registry",
"registry" => "pkgs.dev.azure.com/dependabot/my-project/_packaging/my-feed/npm/registry/",
"token" => "my_token"
}]
end

before do
stub_request(:get, "https://pkgs.dev.azure.com/dependabot/my-project/_packaging/my-feed/npm/registry/@dependabot%2Fetag")
.with(headers: { "Authorization" => "Bearer my_token" })
.to_return(status: 200, body: "{}")
stub_request(:get, "https://pkgs.dev.azure.com/dependabot/my-project/_packaging/my-feed/npm/registry/semver")
.with(headers: { "Authorization" => "Bearer my_token" })
.to_return(status: 404)
end

it "adds a scoped registry for the dependency" do
expect(npmrc_content).to include("@dependabot:registry=https://pkgs.dev.azure.com/dependabot/my-project/_packaging/my-feed/npm/registry/")
end
end
end

context "registry scope generation" do
let(:credentials) do
[{
Expand Down
25 changes: 25 additions & 0 deletions npm_and_yarn/spec/fixtures/projects/generic/simple/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "{{ name }}",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no\\ test\ specified\" && exit 1",
"prettify": "prettier --write \"{{packages/*/src,examples,cypress,scripts}/**/,}*.{js,jsx,ts,tsx,css,md}\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/waltfy/PROTO_TEST.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/waltfy/PROTO_TEST/issues"
},
"homepage": "https://github.com/waltfy/PROTO_TEST#readme",
"dependencies": {
"fetch-factory": "^0.0.1"
},
"devDependencies": {
"etag" : "^1.0.0"
}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "test",
"version": "1.0.0",
"description": "testing out private registry usage",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/waltfy/PROTO_TEST.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/waltfy/PROTO_TEST/issues"
},
"homepage": "https://github.com/waltfy/PROTO_TEST#readme",
"dependencies": {
"@dependabot/etag": "^1.8.0",
"semver": "^7.5.4"
}
}
Loading