Skip to content

Commit

Permalink
Merge pull request #8094 from dependabot/deivid-rodriguez/pnpm-auth-fix
Browse files Browse the repository at this point in the history
Properly infer `.npmrc` for PNPM
  • Loading branch information
deivid-rodriguez committed Sep 27, 2023
2 parents f133a59 + 7bd8b2c commit a4e8046
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 288 deletions.
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

0 comments on commit a4e8046

Please sign in to comment.