Skip to content

Commit

Permalink
Add ability to update package.json automatically.
Browse files Browse the repository at this point in the history
* Refactor usage output
* Ensure non-zero exit code for failures
* Add support for -w and --write to update package.json
  • Loading branch information
rwjblue committed May 15, 2019
1 parent 47bc549 commit cd73756
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 21 deletions.
76 changes: 63 additions & 13 deletions bin/ember-source-channel-url
Expand Up @@ -3,26 +3,76 @@

/* eslint-disable no-console */

const fs = require('fs');
const getChannelURL = require('../src');
const channel = process.argv[2];
const shouldUpdatePackage = process.argv.includes('-w') || process.argv.includes('--write');

if (['release', 'beta', 'canary'].indexOf(channel) === -1) {
console.log(
`ember-source-channel-url is a utility module to easily obtain the URL
function printUsage() {
console.log(`
ember-source-channel-url is a utility module to easily obtain the URL
to a tarball representing the latest \`ember-source\` build for a given
channel.
Usage:
USAGE:
ember-source-channel-url [CHANNEL] [FLAGS]
FLAGS:
-w, --write Update the local package.json to use the retrieved ember-source URL
-h, --help Prints help information
EXAMPLE:
* Print the most recent URL for the specified channel:
$ ember-source-channel-url canary
$ ember-source-channel-url beta
$ ember-source-channel-url release
* Update the local project's \`package.json\` to use the most recent URL for the canary channel:
$ ember-source-channel-url canary --write
`);
}

ember-source-channel-url canary
ember-source-channel-url beta
ember-source-channel-url release
`
);
if (['release', 'beta', 'canary'].indexOf(channel) === -1) {
printUsage();
process.exitCode = 1;
} else {
getChannelURL(channel).then(url =>
getChannelURL(channel).then(url => {
console.log(
`The URL for the latest tarball from ember-source's ${channel} channel is:\n\n\t${url}`
)
);
`The URL for the latest tarball from ember-source's ${channel} channel is:\n\n\t${url}\n`
);

if (shouldUpdatePackage) {
if (!fs.existsSync('package.json')) {
console.log(
`You passed --write to ember-source-channel-url but no package.json is available to update.`
);

process.exitCode = 2;
}

let contents = fs.readFileSync('package.json', { encoding: 'utf8' });
let pkg = JSON.parse(contents);

let dependencyType = ['dependencies', 'devDependencies'].find(
type => pkg[type] && pkg[type]['ember-source']
);

if (dependencyType) {
pkg[dependencyType]['ember-source'] = url;

let updatedContents = JSON.stringify(pkg, null, 2);
fs.writeFileSync('package.json', updatedContents, { encoding: 'utf8' });
} else {
console.log(
`You passed --write to ember-source-channel-url but ember-source is not included in dependencies or devDependencies in the package.json.`
);

process.exitCode = 3;
}
}
});
}
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -27,7 +27,8 @@
"prettier": "^1.17.1",
"qunit": "~2.9.2",
"qunit-eslint": "^2.0.0",
"rsvp": "^4.7.0"
"rsvp": "^4.7.0",
"tmp": "^0.1.0"
},
"engines": {
"node": "6.* || 8.* || >= 10.*"
Expand Down
105 changes: 99 additions & 6 deletions tests/index-test.js
@@ -1,24 +1,36 @@
'use strict';

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const execa = require('execa');
const createServer = require('./helpers/server').createServer;
const getChannelURL = require('../src');
const tmp = require('tmp');

tmp.setGracefulCleanup();

const ROOT = process.cwd();
const EXECUTABLE_PATH = path.join(__dirname, '..', 'bin', 'ember-source-channel-url');

QUnit.module('ember-source-channel-url', function(hooks) {
function randomString(length) {
return crypto.randomBytes(Math.ceil(length / 2)).toString('hex');
}

hooks.beforeEach(function() {
let dir = tmp.dirSync();
process.chdir(dir.name);

return createServer().then(server => {
process.env.EMBER_SOURCE_CHANNEL_URL_HOST = `http://localhost:${server.port}`;

this.server = server;
this.fakeSHA = randomString(20);
let assetPath = (this.assetPath = `/canary/shas/${this.fakeSHA}.tgz`);

this.expectedURL = `http://${server.host}:${server.port}/builds.emberjs.com${this.assetPath}`;

server.on('/builds.emberjs.com/canary.json', (req, res) => {
res.end(JSON.stringify({ assetPath }));
});
Expand All @@ -28,6 +40,8 @@ QUnit.module('ember-source-channel-url', function(hooks) {
});

hooks.afterEach(function() {
process.chdir(ROOT);

return this.server.close();
});

Expand All @@ -43,13 +57,92 @@ QUnit.module('ember-source-channel-url', function(hooks) {

QUnit.module('binary', function() {
QUnit.test('works', function(assert) {
let expected = `http://${this.server.host}:${this.server.port}/builds.emberjs.com${
this.assetPath
}`;
return execa(EXECUTABLE_PATH, ['canary']).then(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');
});
});

QUnit.test('updates local package.json when -w is passed (dependencies)', function(assert) {
fs.writeFileSync(
'package.json',
JSON.stringify({ dependencies: { 'ember-source': '^3.10.0' } }),
{ encoding: 'utf8' }
);

return execa(EXECUTABLE_PATH, ['canary', '-w']).then(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');

assert.deepEqual(JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' })), {
dependencies: {
'ember-source': this.expectedURL,
},
});
});
});

QUnit.test('updates local package.json when --write is passed (dependencies)', function(
assert
) {
fs.writeFileSync(
'package.json',
JSON.stringify({ dependencies: { 'ember-source': '^3.10.0' } }),
{ encoding: 'utf8' }
);

return execa(EXECUTABLE_PATH, ['canary', '--write']).then(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');

assert.deepEqual(JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' })), {
dependencies: {
'ember-source': this.expectedURL,
},
});
});
});

QUnit.test('updates local package.json when --write is passed (devDependencies)', function(
assert
) {
fs.writeFileSync(
'package.json',
JSON.stringify({ devDependencies: { 'ember-source': '^3.10.0' } }),
{ encoding: 'utf8' }
);

return execa(EXECUTABLE_PATH, ['canary', '--write']).then(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');

assert.deepEqual(JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' })), {
devDependencies: {
'ember-source': this.expectedURL,
},
});
});
});

QUnit.test('fails when package.json is missing', function(assert) {
return execa(EXECUTABLE_PATH, ['canary', '--write']).catch(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');

assert.ok(
results.stdout.includes('no package.json is available to update'),
'warning is printed indicating -w failed'
);
});
});

QUnit.test('fails when ember-source is not a dep', function(assert) {
fs.writeFileSync('package.json', JSON.stringify({}), { encoding: 'utf8' });

return execa(EXECUTABLE_PATH, ['canary', '--write']).catch(results => {
assert.ok(results.stdout.includes(this.expectedURL), 'URL is present in stdout');

let executablePath = path.join(__dirname, '..', 'bin', 'ember-source-channel-url');
return execa(executablePath, ['canary']).then(results => {
assert.ok(results.stdout.includes(expected), 'URL is present in stdout');
assert.ok(
results.stdout.includes(
'ember-source is not included in dependencies or devDependencies'
),
'warning is printed indicating -w failed'
);
});
});
});
Expand Down
9 changes: 8 additions & 1 deletion yarn.lock
Expand Up @@ -1192,7 +1192,7 @@ restore-cursor@^2.0.0:
onetime "^2.0.0"
signal-exit "^3.0.2"

rimraf@2.6.3:
rimraf@2.6.3, rimraf@^2.6.3:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
dependencies:
Expand Down Expand Up @@ -1379,6 +1379,13 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"

tmp@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877"
integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==
dependencies:
rimraf "^2.6.3"

tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
Expand Down

0 comments on commit cd73756

Please sign in to comment.