weixu365 / serverless-scriptable-plugin

Adding script support to Serverless 1.x which enables you to customize Serverless behavior without writing a plugin.
MIT License
111 stars 11 forks source link

scripts should be given a callback to define when work is done #10

Closed tommedema closed 6 years ago

tommedema commented 6 years ago

If I define a script like so:

custom:
  scriptHooks:
    before:aws:package:finalize:saveServiceState: scripts/define-certificate-is-valid.js # define whether the stack's certificate is valid prior to consuming it

It should run BEFORE aws:package:finalize:saveServiceState. SaveServiceState runs code that compiles the cloudformation template. I can then confirm if the script is run at the correct time by adding a log line at saveCompiledTemplate.js:

'use strict';

const BbPromise = require('bluebird');
const path = require('path');

module.exports = {
  saveCompiledTemplate() {
    const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();

    const compiledTemplateFilePath = path.join(
      this.serverless.config.servicePath,
      '.serverless',
      compiledTemplateFileName
    );

    console.log('!!! writing compiled template file')
    console.log(this.serverless.service.provider.compiledCloudFormationTemplate)
    this.serverless.utils.writeFileSync(compiledTemplateFilePath,
      this.serverless.service.provider.compiledCloudFormationTemplate);

    return BbPromise.resolve();
  },
};

And a log line in my script, which should run first:

/* global serverless, options */

log('initializing')

const getServerlessPlugin = require(__dirname + '/lib/get-serverless-plugin')(serverless)
const awsInfo = getServerlessPlugin('AwsInfo')
const getOutput = require(__dirname + '/lib/get-cf-output')(serverless, awsInfo)
const get = require('lodash.get')
const util = require('util')

awsInfo.getStackInfo()
.then(() => {

  var condition = get(serverless,
    ['service', 'provider', 'compiledCloudFormationTemplate',
    'Conditions', 'ShouldConsumeCertificate'])

  log(`initial certificate condition is ${util.inspect(condition, { depth: 10 })}`)

  var updatedCondition = JSON.parse(
    JSON.stringify(condition).replace('%SCRIPT_SUBSTITUTE_CERTIFICATE_IS_VALID%', 'true'))

  condition = updatedCondition

  serverless.service.provider.compiledCloudFormationTemplate.Conditions.ShouldConsumeCertificate = updatedCondition
  serverless.service.resources.Conditions.ShouldConsumeCertificate = updatedCondition

  log(`updated certificate condition is ${util.inspect(condition, { depth: 10 })}`)
})
.catch((err) => console.error(err))

function log(msg) {
  serverless.cli.log(`define-certificate-is-valid: ${msg}`)
}

And the console logs:

Serverless: Invoke aws:package:finalize
Running javascript file: scripts/define-certificate-is-valid.js
Serverless: define-certificate-is-valid: initializing
!!! writing compiled template file
{ ... }
Serverless: Invoke aws:common:moveArtifactsToPackage
Serverless: Invoke aws:common:validate
Serverless: Invoke aws:deploy:deploy
Serverless: define-certificate-is-valid: initial certificate condition is { 'Fn::Equals': [ '%SCRIPT_SUBSTITUTE_CERTIFICATE_IS_VALID%', 'true' ] }
Serverless: define-certificate-is-valid: updated certificate condition is { 'Fn::Equals': [ 'true', 'true' ] }

So, while it outputs initializing at the right time, the rest is executed after awsInfo.getStackInfo() resolves. Meanwhile serverless continues its work before my script can do its job.

Serverless should pause execution while my script its doing its job.

tommedema commented 6 years ago

Note that with plugins this can be solved by returning a promise (see https://github.com/serverless/serverless/issues/4557).

But in this case this is not possible since you are calling a node script without a function wrapper, and there is no return statement.

weixu365 commented 6 years ago

Fixed in the new version 0.7.0, could you please upgrade the module and have a test?

weixu365 commented 6 years ago

@tommedema Any updates on this issue?

tommedema commented 6 years ago

Hey @weixu365 . I actually switched to using plugins, because I didn't like how this plugin requires me to use globals (serverless and options)

Let me know if you really want me to test this anyway