Skip to content

Commit

Permalink
Include composer-report version in reports (hyperledger-archives#3380)
Browse files Browse the repository at this point in the history
Signed-off-by: James Taylor <[email protected]>
  • Loading branch information
jt-nti authored Feb 21, 2018
1 parent 25f1451 commit e20ef07
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 107 deletions.
28 changes: 21 additions & 7 deletions packages/composer-cli/lib/cmds/report/lib/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const chalk = require('chalk');
*/
class Report {
/**
* Command implementation.
* Command process for report command
* @param {Object} args argument list from composer command
* @return {Promise} promise when command complete
*/
Expand All @@ -33,14 +33,28 @@ class Report {

/**
* Get the current environment data
* @return {Promise} Resolved when report completed
*/
static createReport() {
cmdUtil.log(chalk.bold.blue('Creating Composer report'));
let tmpDirectory = report.setupReportDir();
cmdUtil.log(chalk.blue('Triggering node report...'));
report.createNodeReport(tmpDirectory);
let outputFile = report.archiveReport(tmpDirectory);
cmdUtil.log(chalk.bold.blue('Created archive file: '+outputFile));
try {
cmdUtil.log(chalk.bold.blue('Creating Composer report'));
const {reportId, reportDir} = report.beginReport();

cmdUtil.log(chalk.blue('Collecting diagnostic data...'));
report.collectBasicDiagnostics(reportId, reportDir);

const archiveName = report.completeReport(reportId, reportDir);
cmdUtil.log(chalk.bold.blue('\nCreated archive file: ') + archiveName);

} catch (err) {
if (err.name === 'DirectoryAccessError') {
return Promise.reject(err);
} else {
throw err;
}
}

return Promise.resolve();
}
}
module.exports = Report;
63 changes: 49 additions & 14 deletions packages/composer-cli/test/report/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,25 @@ const ReportCmd = require('../../lib/cmds/report/reportCommand.js');

const chai = require('chai');
const sinon = require('sinon');
const assert = sinon.assert;
const sinonChai = require('sinon-chai');
const should = chai.should();
chai.use(sinonChai);

describe('composer report CLI', function() {
const sandbox = sinon.sandbox.create();
let consoleLogSpy;
let setupStub;
let reportStub;
let archiveStub;
let beginReportStub;
let collectBasicDiagnosticsStub;
let completeReportStub;

beforeEach(function() {
consoleLogSpy = sandbox.spy(console, 'log');
setupStub = sandbox.stub(composerReport, 'setupReportDir').returns('DIR');
reportStub = sandbox.stub(composerReport, 'createNodeReport');
archiveStub = sandbox.stub(composerReport, 'archiveReport').returns('ARCHIVE');
beginReportStub = sandbox.stub(composerReport, 'beginReport').returns({
reportId: 'REPORT',
reportDir: 'DIR'
});
collectBasicDiagnosticsStub = sandbox.stub(composerReport, 'collectBasicDiagnostics');
completeReportStub = sandbox.stub(composerReport, 'completeReport').returns('ARCHIVE');
});

afterEach(function() {
Expand All @@ -46,13 +49,13 @@ describe('composer report CLI', function() {
it('should successfully run the composer report command', function() {
const args = {};
return ReportCmd.handler(args).then(() => {
assert.calledThrice(consoleLogSpy);
assert.calledWith(consoleLogSpy, sinon.match('Creating Composer report'));
assert.calledWith(consoleLogSpy, sinon.match('Triggering node report...'));
assert.calledWith(consoleLogSpy, sinon.match('Created archive file: ARCHIVE'));
assert.calledOnce(setupStub);
assert.calledWith(reportStub, 'DIR');
assert.calledWith(archiveStub, 'DIR');
consoleLogSpy.should.have.been.calledThrice;
consoleLogSpy.should.have.been.calledWith(sinon.match('Creating Composer report'));
consoleLogSpy.should.have.been.calledWith(sinon.match('Collecting diagnostic data...'));
consoleLogSpy.should.have.been.calledWith(sinon.match(/Created archive file: .*ARCHIVE/));
beginReportStub.should.have.been.calledOnce;
collectBasicDiagnosticsStub.should.have.been.calledWith('REPORT', 'DIR');
completeReportStub.should.have.been.calledWith('REPORT', 'DIR');
});
});

Expand All @@ -61,4 +64,36 @@ describe('composer report CLI', function() {
result.should.be.an.instanceOf(Promise);
});

it('should handle errors', function() {
let testErr = new Error('ERROR');
beginReportStub.throws(testErr);

let result;
try {
const args = {};
return ReportCmd.handler(args).then(() => {
should.fail('Should have thrown an error!');
});
} catch (err) {
result = err;
}

should.exist(result);
result.name.should.not.equal('DirectoryAccessError');
});

it('should throw DirectoryAccessError if the current directory is not writeable', function() {
let testErr = new Error('Access denied');
testErr.name = 'DirectoryAccessError';
beginReportStub.throws(testErr);

const args = {};
return ReportCmd.handler(args).then(() => {
should.fail('Should have been rejected!');
}).catch((err) => {
should.exist(err);
err.name.should.equal('DirectoryAccessError');
});
});

});
11 changes: 8 additions & 3 deletions packages/composer-report/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@ bower_components
# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Tarballs
*.tgz

# Typescript v1 declaration files
typings/

Expand All @@ -61,11 +64,13 @@ typings/
# JSDoc
out

# Mac files.
# next.js build output
.next

# OSX files
**/.DS_Store

*.swp

# Build generated files should be ignored by git, but not by npm.
index.d.ts

22 changes: 21 additions & 1 deletion packages/composer-report/bin/cmd.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,24 @@

const report = require('../lib/report.js');

report.report();
try {
const {reportId, reportDir} = report.beginReport();

// eslint-disable-next-line no-console
console.log('Collecting diagnostic data...');
report.collectBasicDiagnostics(reportId, reportDir);

const archiveName = report.completeReport(reportId, reportDir);

// eslint-disable-next-line no-console
console.log(`Created archive file: ${archiveName}`);

} catch (err) {
if (err.name === 'DirectoryAccessError') {
// eslint-disable-next-line no-console
console.log(err.message);
return 1;
} else {
throw err;
}
}
111 changes: 83 additions & 28 deletions packages/composer-report/lib/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,56 +16,65 @@

const fs = require('fs');
const os = require('os');
const path = require('path');
const { sep } = require('path');
const moment = require('moment');
const nodereport = require('node-report');
const tar = require('tar');

/**
* Main API called from cmd and composer report.
* @return {String} the name of the archive file that was created.
* Prepares a report ID and temporary ready to begin collecting
* diagnostic data
* @return {Object} the report ID and temporary directory
* @throws {Error} DirectoryAccessError if the current directory is not writeable
*/
function report() {
let tmpDirectory = setupReportDir();
function beginReport() {
// Make sure the current directory is writeable for when we get to creating
// the report archive
const currentDirectory = process.cwd();
try {
fs.accessSync(currentDirectory, fs.constants.R_OK | fs.constants.W_OK);
} catch (err) {
if (err.code === 'EACCES') {
let reportError = new Error('Cannot create report in current directory: permission denied');
reportError.name = 'DirectoryAccessError';
throw reportError;
} else {
throw err;
}
}

// TODO write readme file inc. version of the composer-report module
// Plus versions of the other composer modules?
const reportId = _createReportId();
const reportDir = _setupReportDir();

createNodeReport(tmpDirectory);
return archiveReport(tmpDirectory);
return {
reportId: reportId,
reportDir: reportDir
};
}

/**
* Sets up the temp directory for the report
* @return {String} the Path to the temporary directory
*/
function setupReportDir() {
const tmpDir = os.tmpdir();
return fs.mkdtempSync(`${tmpDir}${sep}`);
}

/**
* Trigger node-report to write report in the temp directory
* Collects diagnostic data into the temp directory for the report
* @param {String} reportId report identifier
* @param {String} tmpDirectory the temporary directory for collecting report output
*/
function createNodeReport(tmpDirectory) {
nodereport.setDirectory(tmpDirectory);
nodereport.triggerReport();
function collectBasicDiagnostics(reportId, tmpDirectory) {
_createComposerReport(reportId, tmpDirectory);
_createNodeReport(tmpDirectory);
}

/**
* Creates an archive of the temp directory for the report
* Creates an archive of the temp directory for the report in the current directory
* @param {String} reportId report identifier
* @param {String} tmpDirectory the temporary directory for collecting report output
* @return {String} the name of the archive file that has been created.
*/
function archiveReport(tmpDirectory) {
let timestamp = moment().utc().format('YYYYMMDD[T]HHmmss');
let prefix = 'composer-report-' + timestamp;
let filename = prefix + '.tgz';
function completeReport(reportId, tmpDirectory) {
let filename = reportId + '.tgz';
tar.c(
{
cwd: tmpDirectory+'/',
prefix: prefix,
prefix: reportId,
gzip: true,
file: filename,
sync: true
Expand All @@ -75,4 +84,50 @@ function archiveReport(tmpDirectory) {
return filename;
}

module.exports = { report, setupReportDir, createNodeReport, archiveReport } ;
module.exports = { beginReport, collectBasicDiagnostics, completeReport } ;

/**
* Creates a report identifer for use in filenames etc.
* @return {String} the report identifier
* @private
*/
function _createReportId() {
let timestamp = moment().utc().format('YYYYMMDD[T]HHmmss');
return 'composer-report-' + timestamp;
}

/**
* Sets up the temp directory for the report
* @return {String} the Path to the temporary directory
* @private
*/
function _setupReportDir() {
const tmpDir = os.tmpdir();
return fs.mkdtempSync(`${tmpDir}${sep}`);
}

/**
* Write simple composer report in the temp directory
* @param {String} reportId report identifier
* @param {String} tmpDirectory the temporary directory for collecting report output
* @private
*/
function _createComposerReport(reportId, tmpDirectory) {
const packageJsonPath = path.join(__dirname, '..', 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));

const composerReportVersion = 'composer-report version: ' + packageJson.version;

const reportPath = path.join(tmpDirectory, reportId + '.txt');
fs.writeFileSync(reportPath, composerReportVersion);
}

/**
* Trigger node-report to write report in the temp directory
* @param {String} tmpDirectory the temporary directory for collecting report output
* @private
*/
function _createNodeReport(tmpDirectory) {
nodereport.setDirectory(tmpDirectory);
nodereport.triggerReport();
}
8 changes: 4 additions & 4 deletions packages/composer-report/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@
],
"all": true,
"check-coverage": true,
"statements": 83,
"branches": 64,
"functions": 78,
"lines": 83
"statements": 100,
"branches": 100,
"functions": 100,
"lines": 100
}
}
Loading

0 comments on commit e20ef07

Please sign in to comment.