Skip to content

Commit

Permalink
feat: default + scoped registries with proxy + auth support; support …
Browse files Browse the repository at this point in the history
…for different renders (orta#17)
  • Loading branch information
sebinsua committed Dec 7, 2018
1 parent 9c0d0e3 commit 4e89c4f
Show file tree
Hide file tree
Showing 11 changed files with 5,820 additions and 1,619 deletions.
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,
"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"
},
"testRegex": "(.test)\\.(ts|tsx)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
]
},
"husky": {
"hooks": {
"commit-msg": "validate-commit-msg",
"pre-commit": "lint-staged"
}
}
}
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>
</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"
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,
}
}
}

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
}
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"

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

0 comments on commit 4e89c4f

Please sign in to comment.