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

[WIP] Made the default registry configurable. Added support for scoped registries with different auth tokens, proxies, multiple package.json files and changing the output renderers #18

Closed
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
17 changes: 7 additions & 10 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
{
"prettier.singleQuote": false,
"prettier.trailingComma": "es5",
"prettier.semi": false,
"prettier.printWidth": 120,
"files.exclude": {
"**/.git": true,
"**/dist": true,
"**/node_modules": true
},
"editor.formatOnSave": false
"files.exclude": {
"**/.git": true,
"**/dist": true,
"**/node_modules": true
},
"editor.formatOnSave": true,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard for me to live without this...!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, yep, I agree

"typescript.tsdk": "node_modules/typescript/lib"
}
61 changes: 38 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,56 +22,65 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"precommit": "lint-staged",
"commit": "git-cz",
"commitmsg": "validate-commit-msg",
"build": "tsc",
"test": "jest",
"predocs": "rm -rf docs/",
"docs": "esdoc -c .esdoc.json",
"prepublishOnly": "npm run build",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"prettier": "prettier",
"prettier-write": "npm run prettier -- --parser typescript --no-semi --trailing-comma es5 --write --print-width 120",
"prettier-project": "npm run prettier-write -- 'src/**/*.{ts,tsx}'"
"prettier-write": "yarn prettier --write",
"prettier-project": "yarn prettier-write 'src/**/*.{ts,tsx}'"
},
"license": "MIT",
"engines": {
"node": ">=4.0.0"
},
"dependencies": {
"date-fns": "^1.28.5",
"date-fns": "^1.29.0",
"https-proxy-agent": "^2.2.1",
"lodash.flatten": "^4.4.0",
"lodash.includes": "^4.3.0",
"node-fetch": "^1.7.1",
"semver": "^5.4.1"
"semver": "^5.6.0"
},
"devDependencies": {
"@types/jest": "^19.2.4",
"@types/node": "^7.0.29",
"commitizen": "^2.9.6",
"cz-conventional-changelog": "^2.0.0",
"danger": "*",
"esdoc": "^0.5.2",
"husky": "^0.13.3",
"jest": "^20.0.1",
"lint-staged": "^3.4.1",
"prettier": "^1.3.1",
"semantic-release": "^6.3.6",
"ts-jest": "^20.0.0",
"tslint": "^5.4.3",
"typescript": "^2.3.2",
"validate-commit-msg": "^2.12.1"
"@types/jest": "^23.3.10",
"@types/node": "^10.12.12",
"@types/node-fetch": "^2.1.4",
"@types/semver": "^5.5.0",
"commitizen": "^3.0.5",
"cz-conventional-changelog": "^2.1.0",
"danger": "6.1.9",
"esdoc": "^1.1.0",
"husky": "^1.2.0",
"jest": "^23.6.0",
"lint-staged": "^8.1.0",
"prettier": "^1.15.3",
"semantic-release": "^15.12.4",
"ts-jest": "^23.10.5",
"tslint": "^5.11.0",
"typescript": "^3.2.1",
"validate-commit-msg": "^2.14.0"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"prettier": {
"printWidth": 120,
"semi": false,
"singleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"proseWrap": "always"
},
"lint-staged": {
"*.@(ts|tsx)": [
"tslint --fix",
"npm run prettier-write --",
"yarn prettier-write --",
"git add"
]
},
Expand All @@ -82,12 +91,18 @@
"js"
],
"transform": {
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
".(ts|tsx)": "ts-jest"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After I upgraded this gave me warnings.

},
"testRegex": "(.test)\\.(ts|tsx)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
]
},
"husky": {
"hooks": {
"commit-msg": "validate-commit-msg",
"pre-commit": "lint-staged"
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After I upgraded this gave me warnings.

}
}
4 changes: 2 additions & 2 deletions src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ exports[`npm metadata Shows a bunch of useful text for a new dep 1`] = `
<p>Homepage: <a href=\\"https://github.com/danger/danger-js#readme\\">https://github.com/danger/danger-js#readme</a></p>

<table>
<thead><tr><th></th><th width=\\"100%\\"></th></tr></thead>
<tr><td>Created</td><td>almost 2 years ago</td></tr><tr><td>Last Updated</td><td>11 months ago</td></tr><tr><td>License</td><td>MIT</td></tr><tr><td>Maintainers</td><td>3</td></tr><tr><td>Releases</td><td>45</td></tr><tr><td>Direct Dependencies</td><td>undefined</td></tr><tr><td>Keywords</td><td>undefined</td></tr>
<thead><tr><th></th><th width=\\"100%\\"></th></tr></thead>
<tr><td>Created</td><td>over 2 years ago</td></tr><tr><td>Last Updated</td><td>over 1 year ago</td></tr><tr><td>License</td><td>MIT</td></tr><tr><td>Maintainers</td><td>3</td></tr><tr><td>Releases</td><td>45</td></tr><tr><td>Direct Dependencies</td><td>mocked</td></tr><tr><td>Keywords</td><td>mocked</td></tr>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably the times changed because they were calculated in relation to some fixed dates.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and fixed the message ++

</table>

<details>
Expand Down
93 changes: 93 additions & 0 deletions src/getProxyAgentFromUri.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Agent } from "http"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is partly lifted from request#getProxyFromUri but: in TypeScript, with support for reading from the Yarn config, and returning an Agent instead of a proxy string (for inter-operation with node-fetch).

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, I've never used this stuff so I'll believe it 👍

import HttpsProxyAgent from "https-proxy-agent"
import { URL } from "url"

import { YarnConfig } from "./getYarnConfig"

function formatHostname(hostname: string): string {
// canonicalize the hostname, so that 'oogle.com' won't match 'google.com'
return hostname.replace(/^\.*/, ".").toLowerCase()
}

function parseNoProxyZone(zone: string): Readonly<{ hostname: string; port: string; hasPort: boolean }> {
zone = zone.trim().toLowerCase()

const zoneParts = zone.split(":", 2)
const zoneHost = formatHostname(zoneParts[0])
const zonePort = zoneParts[1]
const hasPort = zone.indexOf(":") > -1

return {
hostname: zoneHost,
port: zonePort,
hasPort,
}
}

function uriInNoProxy(uri: URL, noProxy: string): boolean {
const port = uri.port || (uri.protocol === "https:" ? "443" : "80")
const hostname = formatHostname(uri.hostname)
const noProxyList = noProxy.split(",")

// iterate through the noProxyList until it finds a match.
return noProxyList.map(parseNoProxyZone).some(noProxyZone => {
const isMatchedAt = hostname.indexOf(noProxyZone.hostname)
const hostnameMatched = isMatchedAt > -1 && isMatchedAt === hostname.length - noProxyZone.hostname.length

if (noProxyZone.hasPort) {
return port === noProxyZone.port && hostnameMatched
}

return hostnameMatched
})
}

function getProxyFromUri(uri: URL, config: YarnConfig): string | undefined {
// Decide the proper request proxy to use based on the request URI object and the
// environmental variables (NO_PROXY, HTTP_PROXY, etc.)
// Respect NO_PROXY environment variables.
// See: http://lynx.isc.org/current/breakout/lynx_help/keystrokes/environments.html
const noProxy = process.env.NO_PROXY || process.env.no_proxy

// If the noProxy is a wildcard then return undefined
if (noProxy === "*") {
return undefined
}

// If the noProxy is not empty and the uri is found return undefined
if (noProxy && uriInNoProxy(uri, noProxy)) {
return undefined
}

// Check for HTTP or HTTPS proxy in environment else default to undefined
if (uri.protocol === "http:") {
return process.env.HTTP_PROXY || process.env.http_proxy || config["http-proxy"] || config.proxy || undefined
}

if (uri.protocol === "https:") {
return (
process.env.HTTPS_PROXY ||
process.env.https_proxy ||
config["https-proxy"] ||
process.env.HTTP_PROXY ||
process.env.http_proxy ||
config["http-proxy"] ||
config.proxy ||
undefined
)
}

// If none of that works, return undefined
// (What uri protocol are you using then?)
return undefined
}

export function getProxyAgentFromUri(uri: URL, config: YarnConfig): Agent | undefined {
const proxy = getProxyFromUri(uri, config)

if (proxy) {
return new HttpsProxyAgent(proxy) as Agent
}

return undefined
}
34 changes: 34 additions & 0 deletions src/getRegistries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { YarnConfig } from "./getYarnConfig"

export type Registry = Readonly<{
url: string
authToken?: string
}>

export interface Registries {
readonly default: Registry
[scopeName: string]: Registry
}

export function getRegistries(config: YarnConfig): Registries {
const registries = {
default: {
url: config.registry,
authToken: config._auth,
},
}

for (const [key, value] of Object.entries(config)) {
if (key.endsWith(":registry")) {
const [scopeName] = key.split(":registry")
const url = value
const authToken = config[`${url}:_auth`]
registries[scopeName] = {
url,
authToken,
}
}
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if you've seen the totally insane .npmrc config support for multiple registries and auth tokens for each of these before. I will try to find some docs for it to support this...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah, I've not - but if it works it works 👍


return registries
}
40 changes: 40 additions & 0 deletions src/getYarnConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as child_process from "child_process"
import { promisify } from "util"

const exec = promisify(child_process.exec)

export interface YarnConfig {
registry: string
_auth?: string
"https-proxy"?: string
"http-proxy"?: string
proxy?: string
}

export interface YarnConfigListMessage {
type: "info" | "inspect"
data: object
}

export async function getYarnConfig(): Promise<YarnConfig> {
const defaultConfig = {
registry: "https://registry.npmjs.org/",
}

const { stdout } = await exec("yarn config list --json")

if (stdout) {
const jsonLines: YarnConfigListMessage[] = stdout.split("\n").map(line => JSON.parse(line.trim()))
// The json lines are ordered from yarn to npm, but we wish to produce
// a config in which npm is overridden with yarn config.
return jsonLines.reduceRight((config, jsonLine) => {
if (jsonLine.type === "inspect") {
return { ...config, ...jsonLine.data }
}

return config
}, defaultConfig)
}

return defaultConfig
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, I see

41 changes: 27 additions & 14 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { JSONDiff } from "../node_modules/danger/distribution/dsl/GitDSL"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah nice use 👍


import * as mockfs from "fs"
import { checkForLockfileDiff, checkForRelease, checkForTypesInDeps, getNPMMetadataForDep } from "./index"

jest.mock("node-fetch", () => () =>
Promise.resolve({
ok: true,
json: () => Promise.resolve(JSON.parse(mockfs.readFileSync("src/fixtures/danger-npm-info.json", "utf8"))),
})
)

import yarn, {
checkForLockfileDiff,
checkForNewDependencies,
checkForRelease,
checkForTypesInDeps,
getNPMMetadataForDep,
} from "./index"

declare const global: any
beforeEach(() => {
global.warn = jest.fn()
global.message = jest.fn()
global.fail = jest.fn()
global.markdown = jest.fn()
global.danger = { utils: { sentence: jest.fn() } }
global.danger = {
utils: {
sentence: jest.fn().mockReturnValue("mocked"),
},
}
})

afterEach(() => {
Expand Down Expand Up @@ -59,8 +59,10 @@ describe("checkForTypesInDeps", () => {
})

it("when there is an @types dependency, it should call fail", () => {
const deps = {
const deps: JSONDiff = {
dependencies: {
before: [],
after: ["@types/danger"],
added: ["@types/danger"],
},
}
Expand All @@ -77,24 +79,35 @@ describe("checkForLockfileDiff", () => {

it("when there are dependency changes, and no lockfile in modified - warn", () => {
global.danger = { git: { modified_files: [] } }
const deps = {
dependencies: {},
const deps: JSONDiff = {
dependencies: {
before: [],
after: [],
},
}
checkForLockfileDiff(deps)
expect(global.warn).toHaveBeenCalledTimes(1)
})

it("when there are dependency changes, and a lockfile in modified - do not warn", () => {
global.danger = { git: { modified_files: ["yarn.lock"] } }
const deps = { dependencies: {} }
const deps: JSONDiff = {
dependencies: {
before: [],
after: [],
},
}
checkForLockfileDiff(deps)
expect(global.warn).toHaveBeenCalledTimes(0)
})
})

describe("npm metadata", () => {
it("Shows a bunch of useful text for a new dep", async () => {
const data = await getNPMMetadataForDep("danger")
const defaultConfig = {
registry: "https://registry.npmjs.org/",
}
const data = await getNPMMetadataForDep("danger", defaultConfig)
expect(data).toMatchSnapshot()
})
})
Loading