From f22e265bcf269da86968307353f95b1970b1293b Mon Sep 17 00:00:00 2001 From: Chris Hewell Garrett Date: Thu, 12 Aug 2021 15:30:04 -0400 Subject: [PATCH 1/2] Adds registry-url and always-auth parameters These parameters match the ones passed to the setup-node GH action. The implementation is mostly taken from there, with a few updates/modifications (e.g. using `fs/promises`). --- .github/workflows/CI.yml | 23 ++++++ action.yml | 5 ++ package-lock.json | 151 +++++++++++++++++++++++++++++++++------ package.json | 4 +- src/index.ts | 8 +++ src/registry.test.ts | 44 ++++++++++++ src/registry.ts | 69 ++++++++++++++++++ tests/check-registry.sh | 19 +++++ 8 files changed, 299 insertions(+), 24 deletions(-) create mode 100644 src/registry.test.ts create mode 100644 src/registry.ts create mode 100644 tests/check-registry.sh diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e8955d15..29fc2dbf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -136,3 +136,26 @@ jobs: - run: tests/check-version.sh 'node' 'v12.0.0' - run: tests/check-version.sh 'npm' '7.5.2' - run: tests/check-version.sh 'yarn' '1.22.0' + + test-specified-registry-url: + runs-on: "${{ matrix.os }}-latest" + + strategy: + fail-fast: false + matrix: + os: [ubuntu, macOS, windows] + + steps: + - uses: actions/checkout@v1 + - run: npm ci + - run: npm run build + - uses: ./ + with: + registry-url: "https://some.path.here.com/lol/" + + - run: tests/log-info.sh + - run: tests/check-version.sh 'volta' 'current' + - run: volta install node@10.17.0 yarn@1.19.0 + - run: tests/check-version.sh 'node' 'v10.17.0' + - run: tests/check-version.sh 'yarn' '1.19.0' + - run: tests/check-registry.sh 'https://some.path.here.com/lol/' diff --git a/action.yml b/action.yml index 68f640b1..848644d5 100644 --- a/action.yml +++ b/action.yml @@ -14,6 +14,11 @@ inputs: yarn-version: description: 'Version Spec of the yarn version to use. Examples: 1.6.x, 10.15.1, >=10.15.0' default: '' + registry-url: + description: 'Optional registry to set up for auth. Will set the registry in a project level .npmrc file, and set up auth to read in from env.NODE_AUTH_TOKEN' + always-auth: + description: 'Set always-auth in npmrc' + default: 'false' branding: icon: 'zap' color: 'yellow' diff --git a/package-lock.json b/package-lock.json index 423f2fb2..972d8bf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,126 @@ "@actions/io": "^1.0.1" } }, + "@actions/github": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.0.tgz", + "integrity": "sha512-QvE9eAAfEsS+yOOk0cylLBIO/d6WyWIOvsxxzdrPFaud39G6BOkUwScXZn1iBzQzHyu9SBkkLSWlohDWdsasAQ==", + "requires": { + "@actions/http-client": "^1.0.11", + "@octokit/core": "^3.4.0", + "@octokit/plugin-paginate-rest": "^2.13.3", + "@octokit/plugin-rest-endpoint-methods": "^5.1.1" + }, + "dependencies": { + "@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@octokit/core": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", + "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.0", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-9.7.0.tgz", + "integrity": "sha512-TUJ16DJU8mekne6+KVcMV5g6g/rJlrnIKn7aALG9QrNpnEipFc1xjoarh0PKaAWf2Hf+HwthRKYt+9mCm5RsRg==" + }, + "@octokit/plugin-paginate-rest": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.15.1.tgz", + "integrity": "sha512-47r52KkhQDkmvUKZqXzA1lKvcyJEfYh3TKAIe5+EzMeyDM3d+/s5v11i2gTk8/n6No6DPi3k5Ind6wtDbo/AEg==", + "requires": { + "@octokit/types": "^6.24.0" + }, + "dependencies": { + "@octokit/types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", + "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "requires": { + "@octokit/openapi-types": "^9.5.0" + } + } + } + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.8.0.tgz", + "integrity": "sha512-qeLZZLotNkoq+it6F+xahydkkbnvSK0iDjlXFo3jNTB+Ss0qIbYQb9V/soKLMkgGw8Q2sHjY5YEXiA47IVPp4A==", + "requires": { + "@octokit/types": "^6.25.0", + "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", + "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "requires": { + "@octokit/openapi-types": "^9.5.0" + } + } + } + }, + "@octokit/request": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.1.tgz", + "integrity": "sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==", + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/types": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.25.0.tgz", + "integrity": "sha512-bNvyQKfngvAd/08COlYIN54nRgxskmejgywodizQNyiKoXmWRAjKup2/LYwm+T9V0gsKH6tuld1gM0PzmOiB4Q==", + "requires": { + "@octokit/openapi-types": "^9.5.0" + } + } + } + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, "@actions/glob": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz", @@ -952,7 +1072,6 @@ "version": "2.4.5", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz", "integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==", - "dev": true, "requires": { "@octokit/types": "^6.0.3" } @@ -975,7 +1094,6 @@ "version": "6.0.11", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz", "integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==", - "dev": true, "requires": { "@octokit/types": "^6.0.3", "is-plain-object": "^5.0.0", @@ -985,8 +1103,7 @@ "is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" } } }, @@ -994,7 +1111,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.0.tgz", "integrity": "sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ==", - "dev": true, "requires": { "@octokit/request": "^5.3.0", "@octokit/types": "^6.0.3", @@ -1004,8 +1120,7 @@ "@octokit/openapi-types": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-5.2.2.tgz", - "integrity": "sha512-b3nHy/0uufJJsaZERwZM0syLRO6gfr6vvBPLewQxBKzzbhGDx1ygTyoELMNADD7mIPPzGMqbfdCeJTSeZueZwA==", - "dev": true + "integrity": "sha512-b3nHy/0uufJJsaZERwZM0syLRO6gfr6vvBPLewQxBKzzbhGDx1ygTyoELMNADD7mIPPzGMqbfdCeJTSeZueZwA==" }, "@octokit/plugin-paginate-rest": { "version": "2.11.0", @@ -1036,7 +1151,6 @@ "version": "5.4.14", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.14.tgz", "integrity": "sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==", - "dev": true, "requires": { "@octokit/endpoint": "^6.0.1", "@octokit/request-error": "^2.0.0", @@ -1051,8 +1165,7 @@ "is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" } } }, @@ -1060,7 +1173,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz", "integrity": "sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==", - "dev": true, "requires": { "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", @@ -1083,7 +1195,6 @@ "version": "6.11.2", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.11.2.tgz", "integrity": "sha512-EKQRFZU/oOfUlqk9ntLIE5UO/bcOx8exFpdXGBciJP90f05me3mza0sacIpqVqmiIQP3nJsBjnZHMmtijE5XwQ==", - "dev": true, "requires": { "@octokit/openapi-types": "^5.2.2" } @@ -2943,8 +3054,7 @@ "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, "detect-file": { "version": "1.0.0", @@ -6952,8 +7062,7 @@ "node-fetch": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", - "dev": true + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-int64": { "version": "0.4.0", @@ -7259,7 +7368,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -9735,8 +9843,7 @@ "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "dev": true + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" }, "tunnel-agent": { "version": "0.6.0", @@ -9857,8 +9964,7 @@ "universal-user-agent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" }, "unpipe": { "version": "1.0.0", @@ -10253,8 +10359,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", diff --git a/package.json b/package.json index 82ae5664..f904b7cb 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "lint:types": "tsc --noEmit", "test": "jest" }, - "dependencies": {}, + "dependencies": { + "@actions/github": "^5.0.0" + }, "devDependencies": { "@actions/core": "^1.2.6", "@actions/exec": "^1.0.4", diff --git a/src/index.ts b/src/index.ts index 54847254..df0caeff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import * as core from '@actions/core'; import findUp from 'find-up'; import * as installer from './installer'; +import * as registry from './registry'; import addMatchers from './matchers'; async function run(): Promise { @@ -42,6 +43,13 @@ async function run(): Promise { } } + const registryUrl = core.getInput('registry-url', { required: false }); + const alwaysAuth = core.getInput('always-auth', { required: false }); + if (registryUrl !== '') { + core.info(`setting up registry url: ${registryUrl}`); + await registry.configAuthentication(registryUrl, alwaysAuth); + } + await addMatchers(); } catch (error) { core.setFailed(error.message); diff --git a/src/registry.test.ts b/src/registry.test.ts new file mode 100644 index 00000000..1591a19c --- /dev/null +++ b/src/registry.test.ts @@ -0,0 +1,44 @@ +import * as path from 'path'; +import { writeRegistryToFile } from './registry'; +import { createTempDir } from 'broccoli-test-helper'; + +const ORIGNAL_CONSOLE = Object.assign({}, console); + +describe('registry', () => { + afterEach(() => { + Object.assign(console, ORIGNAL_CONSOLE); + }); + + test('creates an .npmrc with the proper registry url', async () => { + const tmpdir = await createTempDir(); + + await writeRegistryToFile(path.join(tmpdir.path(), '.npmrc'), 'some.registry.url', 'false'); + + expect(tmpdir.read()).toMatchInlineSnapshot(` +Object { + ".npmrc": "some.registry.url:_authToken=\${NODE_AUTH_TOKEN} +registry=some.registry.url +always-auth=false", +} +`); + }); + + test('includes existing npmrc', async () => { + const tmpdir = await createTempDir(); + + tmpdir.write({ + '.npmrc': 'some-scope:registry=https://npm.pkg.github.com', + }); + + await writeRegistryToFile(path.join(tmpdir.path(), '.npmrc'), 'some.registry.url', 'false'); + + expect(tmpdir.read()).toMatchInlineSnapshot(` +Object { + ".npmrc": "some-scope:registry=https://npm.pkg.github.com +some.registry.url:_authToken=\${NODE_AUTH_TOKEN} +registry=some.registry.url +always-auth=false", +} +`); + }); +}); diff --git a/src/registry.ts b/src/registry.ts new file mode 100644 index 00000000..d61ff3f6 --- /dev/null +++ b/src/registry.ts @@ -0,0 +1,69 @@ +import { promises as fs } from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as core from '@actions/core'; +import * as github from '@actions/github'; + +// Mostly copied from https://github.com/actions/setup-node/blob/main/src/authutil.ts + +export async function configAuthentication(registryUrl: string, alwaysAuth: string): Promise { + const npmrc: string = path.resolve(process.env['RUNNER_TEMP'] || process.cwd(), '.npmrc'); + + if (!registryUrl.endsWith('/')) { + registryUrl += '/'; + } + + await writeRegistryToFile(npmrc, registryUrl, alwaysAuth); +} + +export async function writeRegistryToFile( + fileLocation: string, + registryUrl: string, + alwaysAuth: string +): Promise { + let scope: string = core.getInput('scope'); + + if (!scope && registryUrl.indexOf('npm.pkg.github.com') > -1) { + scope = github.context.repo.owner; + } + if (scope && scope[0] != '@') { + scope = '@' + scope; + } + if (scope) { + scope = scope.toLowerCase(); + } + + core.debug(`Setting auth in ${fileLocation}`); + let newContents = ''; + + try { + const curContents = await fs.readFile(fileLocation, 'utf8'); + + curContents.split(os.EOL).forEach((line: string) => { + // Add current contents unless they are setting the registry + if (!line.toLowerCase().startsWith('registry')) { + newContents += line + os.EOL; + } + }); + } catch (_) { + // do nothing... + } + + // Remove http: or https: from front of registry. + const authString: string = + registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}'; + + const registryString: string = scope + ? `${scope}:registry=${registryUrl}` + : `registry=${registryUrl}`; + const alwaysAuthString = `always-auth=${alwaysAuth}`; + + newContents += `${authString}${os.EOL}${registryString}${os.EOL}${alwaysAuthString}`; + + await fs.writeFile(fileLocation, newContents, { flag: 'w' }); + + core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation); + + // Export empty node_auth_token if didn't exist so npm doesn't complain about not being able to find it + core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN || 'XXXXX-XXXXX-XXXXX-XXXXX'); +} diff --git a/tests/check-registry.sh b/tests/check-registry.sh new file mode 100644 index 00000000..e3899456 --- /dev/null +++ b/tests/check-registry.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# exit when any command fails +set -e + +expectedRegistry=$1 + +# Dummy auth token so that the registry can be read +export NODE_AUTH_TOKEN="test-123" + +actualRegistry="$(npm config get registry)" + +if [[ $expectedRegistry == $actualRegistry ]]; then + echo "npm registry was set to \"$expectedRegistry\"" + exit 0 +else + echo "npm registry was set to \"$actualRegistry\" which was incorrect, expected \"$expectedRegistry\"" + exit 1 +fi From 6bbd3f2034186a46bff43e3b9a412fc0ca6f3bd6 Mon Sep 17 00:00:00 2001 From: Chris Hewell Garrett Date: Tue, 31 Aug 2021 11:52:38 -0400 Subject: [PATCH 2/2] wip --- tests/check-registry.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tests/check-registry.sh diff --git a/tests/check-registry.sh b/tests/check-registry.sh old mode 100644 new mode 100755