diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 46c5403e1..6698be114 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3254,14 +3254,6 @@ } } }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "requires": { - "lru-cache": "^6.0.0" - } - }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -3456,6 +3448,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz", "integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -4892,17 +4885,6 @@ "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", "dev": true }, - "normalize-package-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.2.tgz", - "integrity": "sha512-6CdZocmfGaKnIHPVFhJJZ3GuR8SsLKvDANFp47Jmy51aKIr8akjAWTSxtpI+MBgBFdSMRyo4hMpDlT6dTffgZg==", - "requires": { - "hosted-git-info": "^4.0.1", - "resolve": "^1.20.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5188,7 +5170,8 @@ "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true }, "path-type": { "version": "4.0.0", @@ -5752,6 +5735,7 @@ "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, "requires": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" @@ -6298,6 +6282,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -6306,12 +6291,14 @@ "spdx-exceptions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true }, "spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "requires": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -6320,7 +6307,8 @@ "spdx-license-ids": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==" + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true }, "split-string": { "version": "3.1.0", @@ -7034,6 +7022,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "requires": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" diff --git a/src/helpers/files.js b/src/helpers/files.js index 1e7c3f6d3..46c7632c3 100644 --- a/src/helpers/files.js +++ b/src/helpers/files.js @@ -3,14 +3,15 @@ const childProcess = require('child_process') const fs = require('fs') const path = require('path') const glob = require('glob') +const { log } = require('./logger') /** * * @param {string} projectRoot * @returns Promise */ -async function getFileListing (projectRoot) { - return getAllFiles(projectRoot, projectRoot).join('') +async function getFileListing (projectRoot, args) { + return getAllFiles(projectRoot, projectRoot, args).join('') } function manualBlacklist () { @@ -121,7 +122,7 @@ function globBlacklist () { 'remapInstanbul.coverage*.json', 'scoverage.measurements.*', 'test_*_coverage.txt', - 'testrunner-coverage*', + 'testrunner-coverage*' ] } @@ -224,28 +225,30 @@ function parseGitIgnore (projectRoot) { * @param {string[]} arrayOfFiles * @returns {string[]} */ -function getAllFiles (projectRoot, dirPath, arrayOfFiles = []) { +function getAllFiles (projectRoot, dirPath, args, arrayOfFiles = []) { + log(`Searching for files in ${dirPath}`, { level: 'debug', args }) const files = fs.readdirSync(dirPath) files.forEach(function (file) { + if (isBlacklisted(projectRoot, file, manualBlacklist())) { + return + } if ( - fs.statSync(path.join(dirPath, file)).isDirectory() && - !isBlacklisted(projectRoot, file, manualBlacklist()) + fs.statSync(path.join(dirPath, file)).isDirectory() ) { arrayOfFiles = getAllFiles( projectRoot, path.join(dirPath, file), + args, arrayOfFiles ) } else { - if (!isBlacklisted(projectRoot, file, manualBlacklist())) { - arrayOfFiles.push( + arrayOfFiles.push( `${path.join(dirPath.replace(projectRoot, '.'), file)}\n` - ) - } + ) } }) - + log(`Search complete for files in ${dirPath}`, { level: 'debug', args }) return arrayOfFiles } diff --git a/src/helpers/logger.js b/src/helpers/logger.js new file mode 100644 index 000000000..ce67bec57 --- /dev/null +++ b/src/helpers/logger.js @@ -0,0 +1,33 @@ +// @ts-check + +/** + * + * @param {string} message + * @param {Object} options + * @param {string} options.level + * @param {Object} options.args + * @returns void + */ +function log (message, options) { + if (!options || !options.level) { + return console.log(message) + } + switch (options.level) { + case 'debug': + if (options.args && options.args.verbose) { + console.debug(message) + } + + break + case 'error': + console.error(message) + break + default: + console.log(message) + break + } +} + +module.exports = { + log +} diff --git a/src/helpers/web.js b/src/helpers/web.js index 5d8f16507..fbf19e518 100644 --- a/src/helpers/web.js +++ b/src/helpers/web.js @@ -1,5 +1,6 @@ const superagent = require('superagent') const validateHelpers = require('./validate') +const { log } = require('./logger') /** * @@ -26,7 +27,7 @@ function populateBuildParams (inputs, serviceParams) { * @returns {Promise<{ status: string, resultURL: string }>} */ async function uploadToCodecovPUT (uploadURL, uploadFile) { - console.log('Uploading...') + log('Uploading...') const parts = uploadURL.split('\n') const putURL = parts[1] diff --git a/src/index.js b/src/index.js index 1851d5e0c..9425a9a20 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ const { version } = require('../package.json') const fileHelpers = require('./helpers/files') const validateHelpers = require('./helpers/validate') const webHelpers = require('./helpers/web') +const { log } = require('./helpers/logger') const providers = require('./ci_providers') /** @@ -12,13 +13,17 @@ const providers = require('./ci_providers') * @param {string} token * @param {string} query * @param {string} uploadFile + * @param {Object} args + * @param {Date} start */ -function dryRun (uploadHost, token, query, uploadFile) { - console.log('==> Dumping upload file (no upload)') - console.log( +function dryRun (uploadHost, token, query, uploadFile, args, start) { + log('==> Dumping upload file (no upload)') + log( `${uploadHost}/upload/v4?package=uploader-${version}&token=${token}&${query}` ) - console.log(uploadFile) + log(uploadFile) + const end = Date.now() + log(`End of uploader: ${end - start} milliseconds`, { level: 'debug', args }) process.exit() } @@ -45,6 +50,8 @@ function dryRun (uploadHost, token, query, uploadFile) { * @param {string} args.url Change the upload host (Enterprise use) */ async function main (args) { + const start = Date.now() + log(`Start of uploader: ${start}...`, { level: 'debug', args }) try { /* Step 1: validate and sanitize inputs @@ -70,20 +77,21 @@ async function main (args) { token = process.env.CODECOV_TOKEN || '' } token = args.token || process.env.CODECOV_TOKEN || '' - console.log(generateHeader(getVersion())) + log(generateHeader(getVersion())) // == Step 2: detect if we are in a git repo const projectRoot = args.rootDir || fileHelpers.fetchGitRoot() if (projectRoot === '') { - console.log( + log( '=> No git repo detected. Please use the -R flag if the below detected directory is not correct.' ) } - console.log(`=> Project root located at: ${projectRoot}`) + log(`=> Project root located at: ${projectRoot}`) // == Step 3: get network - const fileListing = await fileHelpers.getFileListing(projectRoot) + log('Start of network processing...', { level: 'debug', args }) + const fileListing = await fileHelpers.getFileListing(projectRoot, args) // == Step 4: select coverage files (search or specify) @@ -96,13 +104,13 @@ async function main (args) { fileHelpers.coverageFilePatterns() ) if (coverageFilePaths.length > 0) { - console.log( + log( `=> Found ${coverageFilePaths.length} possible coverage files:` ) - console.log(coverageFilePaths.join('\n')) + log(coverageFilePaths.join('\n')) } else { - console.error( - 'No coverage files located, please try use `-f`, or change the project root with `-R`' + log( + 'No coverage files located, please try use `-f`, or change the project root with `-R`', { level: 'error' } ) process.exit(args.nonZero ? -1 : 0) } @@ -111,10 +119,11 @@ async function main (args) { ? args.file : '' if (coverageFilePaths.length === 0) { - console.error('Not coverage file found, exiting.') + log('Not coverage file found, exiting.', { level: 'error' }) process.exit(args.nonZero ? -1 : 0) } } + log('End of network processing', { level: 'debug', args }) // == Step 5: generate upload file // TODO: capture envs @@ -154,7 +163,7 @@ async function main (args) { let serviceParams for (const provider of providers) { if (provider.detect(envs)) { - console.log( + log( `Detected ${provider.getServiceName()} as the CI provider.` ) serviceParams = provider.getServiceParams(inputs) @@ -163,7 +172,7 @@ async function main (args) { } if (serviceParams === undefined) { - console.error('Unable to detect service, please specify manually.') + log('Unable to detect service, please specify manually.', { level: 'error' }) process.exit(args.nonZero ? -1 : 0) } @@ -174,9 +183,9 @@ async function main (args) { ) if (args.dryRun) { - dryRun(uploadHost, token, query, uploadFile) + dryRun(uploadHost, token, query, uploadFile, args, start) } else { - console.log( + log( `Pinging Codecov: ${uploadHost}/v4?package=uploader-${version}&token=*******&${query}` ) const uploadURL = await webHelpers.uploadToCodecov( @@ -190,14 +199,18 @@ async function main (args) { uploadURL, gzippedFile ) - console.log(result) + log(result) return result } } catch (error) { // Output any exceptions and exit - console.error(error.message) + log(error.message, { level: 'error' }) + const end = Date.now() + log(`End of uploader: ${end - start} milliseconds`, { level: 'debug', args }) process.exit(args.nonZero ? -1 : 0) } + const end = Date.now() + log(`End of uploader: ${end - start} milliseconds`, { level: 'debug', args }) } /** diff --git a/test/helpers/logger.test.js b/test/helpers/logger.test.js new file mode 100644 index 000000000..db3178e25 --- /dev/null +++ b/test/helpers/logger.test.js @@ -0,0 +1,40 @@ +// @ts-check +const logger = require('../../src/helpers/logger') + +describe('Logger Helper', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + + it('Should call logger with default options', () => { + // eslint-disable-next-line + jest.spyOn(console, 'log').mockImplementation(() => {}) + logger.log('message with no options') + expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/no options/)) + }) + + it('Should not call logger with default options.level = debug and verbose not set', () => { + // eslint-disable-next-line + jest.spyOn(console, 'debug').mockImplementation(() => {}) + logger.log('message with debug level', { level: 'debug' }) + expect(console.debug).not.toHaveBeenCalled() + }) + + it('Should call logger with options.level = debug and verbose set', () => { + jest.spyOn(console, 'debug').mockImplementation(() => {}) + logger.log('message with debug level and verbose', { level: 'debug', args: { verbose: true } }) + expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(/debug level and verbose/)) + }) + + it('Should call logger with options.level = error', () => { + jest.spyOn(console, 'error').mockImplementation(() => {}) + logger.log('message with error level', { level: 'error' }) + expect(console.error).toHaveBeenCalledWith(expect.stringMatching(/error level/)) + }) + + it('Should call logger with unsupported options.level ', () => { + jest.spyOn(console, 'log').mockImplementation(() => {}) + logger.log('message with error level of foobar', { level: 'foobar' }) + expect(console.log).toHaveBeenCalledWith(expect.stringMatching(/of foobar/)) + }) +}) diff --git a/test/helpers/web.test.js b/test/helpers/web.test.js index 9667949f7..3b0f6013d 100644 --- a/test/helpers/web.test.js +++ b/test/helpers/web.test.js @@ -23,6 +23,7 @@ describe('Web Helpers', function () { afterEach(function () { uploadURL = '' + jest.restoreAllMocks() }) it('Can POST to the uploader endpoint (HTTP)', async function () { @@ -48,6 +49,7 @@ describe('Web Helpers', function () { }) it('Can POST to the uploader endpoint (HTTPS)', async function () { + jest.spyOn(console, 'log').mockImplementation(() => {}) uploadURL = 'https://codecov.io' // deepcode ignore WrongNumberOfArgs/test: believe this is a false positive nock('https://codecov.io') @@ -66,6 +68,7 @@ describe('Web Helpers', function () { }) it('Can PUT to the storage endpoint', async function () { + jest.spyOn(console, 'log').mockImplementation(() => {}) uploadURL = 'https://results.codecov.io\nhttps://codecov.io' const response = await webHelper.uploadToCodecovPUT( uploadURL, diff --git a/test/index.test.js b/test/index.test.js index f0006e5ab..79039eacb 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -28,6 +28,7 @@ describe('Uploader Core', function () { }) it('Can upload with custom name', async function () { + jest.spyOn(console, 'log').mockImplementation(() => {}) process.env.CI = 'true' process.env.CIRCLECI = 'true' @@ -52,7 +53,7 @@ describe('Uploader Core', function () { process.env.SOMETHING = 'red' process.env.ANOTHER = 'blue' jest.spyOn(process, 'exit').mockImplementation(() => {}) - const log = jest.spyOn(console, 'log') + const log = jest.spyOn(console, 'log').mockImplementation(() => {}) await app.main({ name: 'customname', token: 'abcdefg', @@ -112,7 +113,7 @@ describe('Uploader Core', function () { it('Can find all coverage from root dir', async function () { jest.spyOn(process, 'exit').mockImplementation(() => {}) - const log = jest.spyOn(console, 'log') + const log = jest.spyOn(console, 'log').mockImplementation(() => {}) await app.main({ name: 'customname', token: 'abcdefg', @@ -125,7 +126,7 @@ describe('Uploader Core', function () { it('Can find only coverage from custom dir', async function () { jest.spyOn(process, 'exit').mockImplementation(() => {}) - const log = jest.spyOn(console, 'log') + const log = jest.spyOn(console, 'log').mockImplementation(() => {}) await app.main({ name: 'customname', token: 'abcdefg',