Skip to content

Commit

Permalink
feat: add ignore-hash flag to deploy command (#163)
Browse files Browse the repository at this point in the history
  • Loading branch information
kakhaUrigashvili authored May 5, 2020
1 parent 7035dd2 commit 057860e
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 28 deletions.
12 changes: 12 additions & 0 deletions docs/concepts/Deploy-Command.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@

This document focuses on explaining the deploy command in details for each type of resource that CLI deploys. If you are looking for references of the ask-resources config file, please check [here](./Alexa-Skill-Project-Definition.md).

**Structure of Deploy command:**

`ask deploy [--ignore-hash] [-p|--profile <profile>] [--debug]`

**Options Description:**

**ignore-hash**: Optional. Forces ASK CLI deploy skill package even if the hash of current skill package folder does not change.

**profile**: Optional. Specify a profile name to be used. Defaults to use `default` as the profile name, if this option or environmental variable `ASK_DEFAULT_PROFILE` is not set.

**debug**: Optional. Appends a debug message to the standard error.

## Skill Metadata
CLI relies on the [Skill Package service](https://developer.amazon.com/en-US/docs/alexa/smapi/skill-package-api-reference.html) to deploy the skill metadata component. To control the deployment of skillMetadata:

Expand Down
5 changes: 3 additions & 2 deletions lib/commands/deploy/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ module.exports = {
* @param {Boolean} doDebug
* @param {*} callback (error)
*/
function deploySkillMetadata(profile, doDebug, callback) {
function deploySkillMetadata(options, callback) {
let vendorId, skillMetaController;
const { profile, doDebug, ignoreHash } = options;
try {
vendorId = profileHelper.resolveVendorId(profile);
skillMetaController = new SkillMetadataController({ profile, doDebug });
} catch (err) {
return callback(err);
}
skillMetaController.deploySkillPackage(vendorId, (deployErr) => {
skillMetaController.deploySkillPackage(vendorId, ignoreHash, (deployErr) => {
if (deployErr) {
return callback(deployErr);
}
Expand Down
10 changes: 6 additions & 4 deletions lib/commands/deploy/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DeployCommand extends AbstractCommand {
}

optionalOptions() {
return ['profile', 'debug'];
return ['ignore-hash', 'profile', 'debug'];
}

handle(cmd, cb) {
Expand All @@ -50,7 +50,8 @@ class DeployCommand extends AbstractCommand {
}
return cb(err);
}
deployResources(profile, cmd.debug, (deployErr) => {
const options = { profile, doDebug: cmd.debug, ignoreHash: cmd.ignoreHash };
deployResources(options, (deployErr) => {
// Write updates back to resources file
if (deployErr) {
Messenger.getInstance().error(deployErr);
Expand Down Expand Up @@ -102,12 +103,13 @@ class DeployCommand extends AbstractCommand {
* @param {Boolean} doDebug The flag of debug or not
* @param {Function} callback
*/
function deployResources(profile, doDebug, callback) {
function deployResources(options, callback) {
const { profile, doDebug } = options;
// Skill Metadata
Messenger.getInstance().info('==================== Deploy Skill Metadata ====================');
const skillMetaSpinner = new SpinnerView();
skillMetaSpinner.start('Uploading the entire skill package (it may take few minutes to build the skill metadata)...');
helper.deploySkillMetadata(profile, doDebug, (metaErr) => {
helper.deploySkillMetadata(options, (metaErr) => {
skillMetaSpinner.terminate();
if (metaErr && metaErr !== 'The hash of current skill package folder does not change compared to the last deploy hash result, '
+ 'CLI will skip the deploy of skill package.') {
Expand Down
6 changes: 6 additions & 0 deletions lib/commands/option-model.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
"alias": null,
"stringInput": "NONE"
},
"ignore-hash": {
"name": "ignore-hash",
"description": "Forces ASK CLI deploy skill package even if the hash of current skill package folder does not change.",
"alias": null,
"stringInput": "NONE"
},
"no-browser": {
"name": "no-browser",
"description": "ASK CLI displays a URL that you can use to sign in with your Amazon developer account from anywhere",
Expand Down
4 changes: 2 additions & 2 deletions lib/controllers/skill-metadata-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ module.exports = class SkillMetadataController {
* @param {String} vendorId
* @param {Function} callback (error)
*/
deploySkillPackage(vendorId, callback) {
deploySkillPackage(vendorId, ignoreHash, callback) {
// 1.get valid skillMetada src path
const skillPackageSrc = ResourcesConfig.getInstance().getSkillMetaSrc(this.profile);
if (!stringUtils.isNonBlankString(skillPackageSrc)) {
Expand All @@ -48,7 +48,7 @@ module.exports = class SkillMetadataController {
return callback(hashErr);
}
const lastDeployHash = ResourcesConfig.getInstance().getSkillMetaLastDeployHash(this.profile);
if (stringUtils.isNonBlankString(lastDeployHash) && lastDeployHash === currentHash) {
if (!ignoreHash && stringUtils.isNonBlankString(lastDeployHash) && lastDeployHash === currentHash) {
return callback('The hash of current skill package folder does not change compared to the last deploy hash result, '
+ 'CLI will skip the deploy of skill package.');
}
Expand Down
11 changes: 6 additions & 5 deletions test/unit/commands/deploy/helper-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('Commands deploy test - helper test', () => {
const TEST_PROFILE = 'default';
const TEST_VENDOR_ID = 'vendor';
const TEST_DO_DEBUG = false;
const TEST_OPTIONS = { profile: TEST_PROFILE, doDebug: TEST_DO_DEBUG };

describe('# test helper method - deploySkillMetadata', () => {
afterEach(() => {
Expand All @@ -22,7 +23,7 @@ describe('Commands deploy test - helper test', () => {
// setup
sinon.stub(profileHelper, 'resolveVendorId').throws(new Error('error'));
// call
helper.deploySkillMetadata(TEST_PROFILE, TEST_DO_DEBUG, (err, res) => {
helper.deploySkillMetadata(TEST_OPTIONS, (err, res) => {
// verify
expect(err.message).equal('error');
expect(res).equal(undefined);
Expand All @@ -33,9 +34,9 @@ describe('Commands deploy test - helper test', () => {
it('| skillMetaController deploySkillPackage fails, expect callback error', (done) => {
// setup
sinon.stub(profileHelper, 'resolveVendorId').returns(TEST_VENDOR_ID);
sinon.stub(SkillMetadataController.prototype, 'deploySkillPackage').callsArgWith(1, 'error');
sinon.stub(SkillMetadataController.prototype, 'deploySkillPackage').callsArgWith(2, 'error');
// call
helper.deploySkillMetadata(TEST_PROFILE, TEST_DO_DEBUG, (err, res) => {
helper.deploySkillMetadata(TEST_OPTIONS, (err, res) => {
// verify
expect(err).equal('error');
expect(res).equal(undefined);
Expand All @@ -46,9 +47,9 @@ describe('Commands deploy test - helper test', () => {
it('| skillMetaController deploySkillPackage passes, expect no error callback', (done) => {
// setup
sinon.stub(profileHelper, 'resolveVendorId').returns(TEST_VENDOR_ID);
sinon.stub(SkillMetadataController.prototype, 'deploySkillPackage').callsArgWith(1);
sinon.stub(SkillMetadataController.prototype, 'deploySkillPackage').callsArgWith(2);
// call
helper.deploySkillMetadata(TEST_PROFILE, TEST_DO_DEBUG, (err, res) => {
helper.deploySkillMetadata(TEST_OPTIONS, (err, res) => {
// verify
expect(err).equal(undefined);
expect(res).equal(undefined);
Expand Down
16 changes: 8 additions & 8 deletions test/unit/commands/deploy/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('Commands deploy test - command class test', () => {
expect(instance.name()).equal('deploy');
expect(instance.description()).equal('deploy the skill project');
expect(instance.requiredOptions()).deep.equal([]);
expect(instance.optionalOptions()).deep.equal(['profile', 'debug']);
expect(instance.optionalOptions()).deep.equal(['ignore-hash', 'profile', 'debug']);
});

describe('validate command handle', () => {
Expand Down Expand Up @@ -165,7 +165,7 @@ describe('Commands deploy test - command class test', () => {

it('| helper deploy skill metadata fails, expect throw error', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2, 'error');
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1, 'error');
// call
instance.handle(TEST_CMD, (err) => {
// verify
Expand All @@ -179,7 +179,7 @@ describe('Commands deploy test - command class test', () => {

it('| helper deploy skill metadata with same hash skip result, expect display the message and continue', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2,
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1,
'The hash of current skill package folder does not change compared to the last deploy hash result, '
+ 'CLI will skip the deploy of skill package.');
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, 'error');
Expand All @@ -203,7 +203,7 @@ describe('Commands deploy test - command class test', () => {

it('| helper build skill code fails, expect throw error', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2);
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1);
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, 'error');
// call
instance.handle(TEST_CMD, (err) => {
Expand Down Expand Up @@ -238,7 +238,7 @@ describe('Commands deploy test - command class test', () => {

it('| helper deploy skill infra without infraType, expect skip the flow by calling back', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2);
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1);
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, null, TEST_CODE_BUILD_RESULT);
sinon.stub(helper, 'deploySkillInfrastructure').callsArgWith(2, 'error');
sinon.stub(helper, 'enableSkill').callsArgWith(2);
Expand Down Expand Up @@ -268,7 +268,7 @@ with build flow ${TEST_CODE_BUILD_RESULT[0].buildFlow}.`);

it('| helper deploy skill infra fails, expect throw error', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2);
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1);
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, null, TEST_CODE_BUILD_RESULT);
sinon.stub(helper, 'deploySkillInfrastructure').callsArgWith(2, 'error');
sinon.stub(helper, 'enableSkill').callsArgWith(2);
Expand Down Expand Up @@ -297,7 +297,7 @@ with build flow ${TEST_CODE_BUILD_RESULT[0].buildFlow}.`);

it('| deploy skill all pass, expect deploy succeeds and enbalSkill get called', (done) => {
// setup
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2);
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1);
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, null, TEST_CODE_BUILD_RESULT);
sinon.stub(helper, 'deploySkillInfrastructure').callsArgWith(2);
sinon.stub(helper, 'enableSkill').callsArgWith(2);
Expand Down Expand Up @@ -346,7 +346,7 @@ with build flow ${TEST_CODE_BUILD_RESULT[0].buildFlow}.`);
it('| can callbcak error when enable fails', (done) => {
// setup
const TEST_ERROR = 'error';
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(2);
sinon.stub(helper, 'deploySkillMetadata').callsArgWith(1);
sinon.stub(helper, 'buildSkillCode').callsArgWith(2, null, TEST_CODE_BUILD_RESULT);
sinon.stub(helper, 'deploySkillInfrastructure').callsArgWith(2);
sinon.stub(helper, 'enableSkill').callsArgWith(2, 'error');
Expand Down
34 changes: 27 additions & 7 deletions test/unit/controller/skill-metadata-controller-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('Controller test - skill metadata controller test', () => {
const TEST_PROFILE = 'default'; // test file contains 'default' profile
const TEST_ROOT_PATH = 'root';
const TEST_VENDOR_ID = 'vendorId';
const TEST_IGNORE_HASH = false;
const TEST_SKILL_ID = 'skillId';
const TEST_STAGE = 'stage';
const TEST_PATH = 'path';
Expand Down Expand Up @@ -60,7 +61,7 @@ describe('Controller test - skill metadata controller test', () => {
// setup
ResourcesConfig.getInstance().setSkillMetaSrc(TEST_PROFILE, null);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(res).equal(undefined);
expect(err).equal('Skill package src is not found in ask-resources.json.');
Expand All @@ -73,7 +74,7 @@ describe('Controller test - skill metadata controller test', () => {
ResourcesConfig.getInstance().setSkillMetaSrc(TEST_PROFILE, TEST_PATH);
sinon.stub(fs, 'existsSync').withArgs(TEST_PATH).returns(false);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(res).equal(undefined);
expect(err).equal(`File ${TEST_PATH} does not exist.`);
Expand All @@ -87,7 +88,7 @@ describe('Controller test - skill metadata controller test', () => {
sinon.stub(fs, 'existsSync').withArgs(TEST_PATH).returns(true);
sinon.stub(hashUtils, 'getHash').callsArgWith(1, 'hashError', null);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(res).equal(undefined);
expect(err).equal('hashError');
Expand All @@ -103,7 +104,7 @@ describe('Controller test - skill metadata controller test', () => {
ResourcesConfig.getInstance().setSkillMetaLastDeployHash(TEST_PROFILE, LAST_DEPLOY);
sinon.stub(hashUtils, 'getHash').callsArgWith(1, null, LAST_DEPLOY);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(err).equal('The hash of current skill package folder does not change compared to the '
+ 'last deploy hash result, CLI will skip the deploy of skill package.');
Expand All @@ -112,6 +113,25 @@ describe('Controller test - skill metadata controller test', () => {
});
});

it('| hash did not change and ignore hash flag passed, expect resourcesConfig updated correctly', (done) => {
// setup
const LAST_DEPLOY = 'lastDeploy';
const IGNORE_HASH = true;
ResourcesConfig.getInstance().setSkillMetaSrc(TEST_PROFILE, TEST_PATH);
sinon.stub(fs, 'existsSync').withArgs(TEST_PATH).returns(true);
sinon.stub(hashUtils, 'getHash').callsArgWith(1, null, LAST_DEPLOY);
ResourcesConfig.getInstance().setSkillMetaLastDeployHash(TEST_PROFILE, LAST_DEPLOY);
sinon.stub(SkillMetadataController.prototype, 'putSkillPackage').callsArgWith(2, null, TEST_SKILL_ID);
ResourcesConfig.getInstance().setSkillId(TEST_PROFILE, TEST_SKILL_ID);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, IGNORE_HASH, (err, res) => {
// verify
expect(err).equal(undefined);
expect(res).equal(undefined);
done();
});
});

it('| hash does change, skillId exists and putSkillPackage passes, expect resourcesConfig updated correctly', (done) => {
// setup
ResourcesConfig.getInstance().setSkillMetaSrc(TEST_PROFILE, TEST_PATH);
Expand All @@ -120,7 +140,7 @@ describe('Controller test - skill metadata controller test', () => {
sinon.stub(SkillMetadataController.prototype, 'putSkillPackage').callsArgWith(2, null, TEST_SKILL_ID);
ResourcesConfig.getInstance().setSkillId(TEST_PROFILE, TEST_SKILL_ID);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(ResourcesConfig.getInstance().getSkillMetaLastDeployHash(TEST_PROFILE)).equal(TEST_CURRENT_HASH);
expect(err).equal(undefined);
Expand All @@ -137,7 +157,7 @@ describe('Controller test - skill metadata controller test', () => {
sinon.stub(SkillMetadataController.prototype, 'putSkillPackage').callsArgWith(2, null, TEST_SKILL_ID);
ResourcesConfig.getInstance().setSkillId(TEST_PROFILE, '');
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(ResourcesConfig.getInstance().getSkillMetaLastDeployHash(TEST_PROFILE)).equal(TEST_CURRENT_HASH);
expect(ResourcesConfig.getInstance().getSkillId(TEST_PROFILE)).equal(TEST_SKILL_ID);
Expand All @@ -155,7 +175,7 @@ describe('Controller test - skill metadata controller test', () => {
sinon.stub(SkillMetadataController.prototype, 'putSkillPackage').callsArgWith(2, 'putErr');
ResourcesConfig.getInstance().setSkillId(TEST_PROFILE, TEST_SKILL_ID);
// call
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, (err, res) => {
skillMetaController.deploySkillPackage(TEST_VENDOR_ID, TEST_IGNORE_HASH, (err, res) => {
// verify
expect(err).equal('putErr');
expect(res).equal(undefined);
Expand Down

0 comments on commit 057860e

Please sign in to comment.