-
Notifications
You must be signed in to change notification settings - Fork 13
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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, | ||
"typescript.tsdk": "node_modules/typescript/lib" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
] | ||
}, | ||
|
@@ -82,12 +91,18 @@ | |
"js" | ||
], | ||
"transform": { | ||
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js" | ||
".(ts|tsx)": "ts-jest" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After I upgraded this gave me warnings. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and fixed the message ++ |
||
</table> | ||
|
||
<details> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { Agent } from "http" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is partly lifted from There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
} |
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, | ||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if you've seen the totally insane There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hah, I've not - but if it works it works 👍 |
||
|
||
return registries | ||
} |
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 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cool, I see |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,26 @@ | ||
import { JSONDiff } from "../node_modules/danger/distribution/dsl/GitDSL" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(() => { | ||
|
@@ -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"], | ||
}, | ||
} | ||
|
@@ -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() | ||
}) | ||
}) |
There was a problem hiding this comment.
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...!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool, yep, I agree