diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 5c3a94e1c..252d3cbc3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -7304,6 +7304,15 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "globals": { "version": "13.10.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", @@ -7319,6 +7328,16 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7355,6 +7374,27 @@ "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + } } }, "@istanbuljs/schema": { @@ -8070,13 +8110,9 @@ } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-union": { "version": "2.1.0", @@ -8763,6 +8799,15 @@ "@babel/highlight": "^7.10.4" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -8784,6 +8829,16 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10170,13 +10225,11 @@ "dev": true }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsdom": { diff --git a/package.json b/package.json index baeb41be9..1e1e322a2 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "homepage": "https://github.com/codecov/uploader#readme", "dependencies": { "glob": "7.1.7", + "js-yaml": "4.1.0", "line-reader": "0.4.0", "superagent": "6.1.0", "typescript": "4.3.5", diff --git a/src/helpers/files.js b/src/helpers/files.js index c29198896..629fcbfeb 100644 --- a/src/helpers/files.js +++ b/src/helpers/files.js @@ -1,8 +1,9 @@ // @ts-check const childProcess = require('child_process') const fs = require('fs') -const path = require('path').posix const glob = require('glob') +const path = require('path').posix + const { log } = require('./logger') /** @@ -337,16 +338,16 @@ function removeFile(projectRoot, filePath) { } module.exports = { - readCoverageFile, - getFileListing, + coverageFilePatterns, + endEnvironmentMarker, endFileMarker, endNetworkMarker, - endEnvironmentMarker, - fileHeader, fetchGitRoot, - parseGitIgnore, + fileHeader, getCoverageFiles, - coverageFilePatterns, + getFileListing, getFilePath, + parseGitIgnore, + readCoverageFile, removeFile, } diff --git a/src/helpers/token.js b/src/helpers/token.js new file mode 100644 index 000000000..39336c7a4 --- /dev/null +++ b/src/helpers/token.js @@ -0,0 +1,69 @@ +const fs = require('fs') +const path = require('path') +const yaml = require('js-yaml') + +const { log } = require('./logger') +const validateHelpers = require('./validate') + +function getToken(inputs, projectRoot) { + const { args, envs } = inputs + const options = [ + [args.token, 'arguments'], + [envs.CODECOV_TOKEN, 'environment variables'], + [getTokenFromYaml(projectRoot), 'Codecov yaml config'], + ] + + for (const option of options) { + if (option[0] && validateHelpers.validateToken(option[0])) { + log(`-> Token set by ${option[1]}`) + return option[0] + } + } + + return '' +} + +function getTokenFromYaml(projectRoot) { + const dirNames = [ + '', + '.github', + 'dev', + ] + + const yamlNames = [ + '.codecov.yaml', + '.codecov.yml', + 'codecov.yaml', + 'codecov.yml', + ] + + for (const dir of dirNames) { + for (const name of yamlNames) { + const filePath = path.join(projectRoot, dir, name); + + try { + if (fs.existsSync(filePath)) { + const fileContents = fs.readFileSync(filePath, { + encoding: 'utf-8', + }); + const yamlConfig = yaml.load(fileContents); + if ( + yamlConfig['codecov_token'] && + validateHelpers.validateToken(yamlConfig['codecov_token']) + ) { + return yamlConfig['codecov_token'] + } + } + } catch(err) { + log(`Error searching for upload token in ${filePath}: ${err}`, { level: 'debug' }) + } + } + } + return '' +} + + +module.exports = { + getToken, + getTokenFromYaml, +} diff --git a/src/index.js b/src/index.js index d3d67f0d7..18319b152 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ const zlib = require('zlib') const { version } = require('../package.json') const fileHelpers = require('./helpers/files') const validateHelpers = require('./helpers/validate') +const tokenHelpers = require('./helpers/token') const webHelpers = require('./helpers/web') const { log } = require('./helpers/logger') const providers = require('./ci_providers') @@ -71,7 +72,6 @@ async function main(args) { ? args.url : 'https://codecov.io' - const token = validateHelpers.getToken(args) log(generateHeader(getVersion())) // == Step 2: detect if we are in a git repo @@ -84,7 +84,10 @@ async function main(args) { log(`=> Project root located at: ${projectRoot}`) - // == Step 3: get network + // == Step 3: sanitize and set token + const token = await tokenHelpers.getToken(inputs, projectRoot) + + // == Step 4: get network let uploadFile = '' if (!args.feature || args.feature.split(',').includes('network') === false) { @@ -101,7 +104,7 @@ async function main(args) { .concat(fileHelpers.endNetworkMarker()) } - // == Step 4: select coverage files (search or specify) + // == Step 5: select coverage files (search or specify) // Look for files let coverageFilePaths = [] @@ -136,7 +139,7 @@ async function main(args) { } log('End of network processing', { level: 'debug', args }) - // == Step 5: generate upload file + // == Step 6: generate upload file // TODO: capture envs // Get coverage report contents @@ -181,7 +184,7 @@ async function main(args) { const gzippedFile = zlib.gzipSync(uploadFile) - // == Step 6: determine CI provider + // == Step 7: determine CI provider // Determine CI provider let serviceParams @@ -197,7 +200,7 @@ async function main(args) { throw new Error('Unable to detect service, please specify manually.') } - // == Step 7: either upload or dry-run + // == Step 8: either upload or dry-run const query = webHelpers.generateQuery( webHelpers.populateBuildParams(inputs, serviceParams), diff --git a/test/fixtures/yaml/.codecov.yaml b/test/fixtures/yaml/.codecov.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/yaml/.codecov.yml b/test/fixtures/yaml/.codecov.yml new file mode 100644 index 000000000..d26f19555 --- /dev/null +++ b/test/fixtures/yaml/.codecov.yml @@ -0,0 +1 @@ +codecov_token: invalid token diff --git a/test/fixtures/yaml/codecov.yaml b/test/fixtures/yaml/codecov.yaml new file mode 100644 index 000000000..70973190f --- /dev/null +++ b/test/fixtures/yaml/codecov.yaml @@ -0,0 +1 @@ +codecov_token: faketoken diff --git a/test/fixtures/yaml/codecov.yml b/test/fixtures/yaml/codecov.yml new file mode 100644 index 000000000..4e75028d8 --- /dev/null +++ b/test/fixtures/yaml/codecov.yml @@ -0,0 +1 @@ +codecov_token: anotherfaketoken diff --git a/test/helpers/token.test.js b/test/helpers/token.test.js new file mode 100644 index 000000000..2d89304a1 --- /dev/null +++ b/test/helpers/token.test.js @@ -0,0 +1,52 @@ +const path = require('path') + +const fileHelpers = require('../../src/helpers/files') +const tokenHelpers = require('../../src/helpers/token') + +describe('Get tokens', () => { + const fixturesDir = path.join(fileHelpers.fetchGitRoot(), 'test/fixtures/yaml') + console.log(fixturesDir) + describe('From yaml', () => { + it('Returns empty with no yaml file', () => { + expect(tokenHelpers.getTokenFromYaml('.')).toBe('') + }) + + it('Returns the correct token from file', () => { + expect(tokenHelpers.getTokenFromYaml(fixturesDir)).toBe('faketoken') + }) + }) + + describe('From right source', () => { + it('Returns from args', () => { + const inputs = { + args: { token: 'argtoken' }, + envs: { CODECOV_TOKEN: 'envtoken' } + } + expect(tokenHelpers.getToken(inputs, fixturesDir)).toBe('argtoken') + }) + + it('Returns from env', () => { + const inputs = { + args: {}, + envs: { CODECOV_TOKEN: 'envtoken' } + } + expect(tokenHelpers.getToken(inputs, fixturesDir)).toBe('envtoken') + }) + + it('Returns from env', () => { + const inputs = { + args: {}, + envs: {} + } + expect(tokenHelpers.getToken(inputs, fixturesDir)).toBe('faketoken') + }) + + it('Returns from no source', () => { + const inputs = { + args: {}, + envs: {} + } + expect(tokenHelpers.getToken(inputs, '.')).toBe('') + }) + }) +})