Repository: serverless/serverless (JavaScript)
Date: 2017-05-29 09:20:25+00:00
Task type: Feature

Issue:
Provide partial local environment across providers
# This is a Feature Proposal

## Description

https://github.com/serverless/serverless/pull/3642 Adds support for the `IS_LOCAL` environment variable during local invocations. However due to the nature of the invoke local implementation it's currently only available when using the `aws` provider.

The setting of the `IS_LOCAL` environment variable should be moved to the general `invoke local` plugin so that it's available across providers.

Thanks to @DavidWells and @HyperBrain who discussed this in https://github.com/serverless/serverless/pull/3642#issuecomment-302490452


Issue discussion:
@pmuens You can assign this one to me - I will implement it in the next days then.
@HyperBrain great! 🎉  Thanks for jumping into that!

I'll assign you. Looking forward to the PR!

Solution patch:
diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js
index fcaf7339de6..29a59a28130 100644
--- a/lib/plugins/aws/invokeLocal/index.js
+++ b/lib/plugins/aws/invokeLocal/index.js
@@ -92,7 +92,6 @@ class AwsInvokeLocal {
       AWS_LAMBDA_FUNCTION_MEMORY_SIZE: memorySize,
       AWS_LAMBDA_FUNCTION_VERSION: '$LATEST',
       NODE_PATH: '/var/runtime:/var/task:/var/runtime/node_modules',
-      IS_LOCAL: 'true',
     };
 
     const providerEnvVars = this.serverless.service.provider.environment || {};
diff --git a/lib/plugins/invoke/invoke.js b/lib/plugins/invoke/invoke.js
index 394273ce609..c5ecdfee574 100644
--- a/lib/plugins/invoke/invoke.js
+++ b/lib/plugins/invoke/invoke.js
@@ -1,5 +1,8 @@
 'use strict';
 
+const BbPromise = require('bluebird');
+const _ = require('lodash');
+
 class Invoke {
   constructor(serverless) {
     this.serverless = serverless;
@@ -45,6 +48,7 @@ class Invoke {
           local: {
             usage: 'Invoke function locally',
             lifecycleEvents: [
+              'loadEnvVars',
               'invoke',
             ],
             options: {
@@ -66,6 +70,24 @@ class Invoke {
         },
       },
     };
+
+    this.hooks = {
+      'invoke:local:loadEnvVars': () => BbPromise.bind(this)
+        .then(this.loadEnvVarsForLocal),
+    };
+  }
+
+  /**
+   * Set environment variables for "invoke local" that are provider independent.
+   */
+  loadEnvVarsForLocal() {
+    const defaultEnvVars = {
+      IS_LOCAL: 'true',
+    };
+
+    _.merge(process.env, defaultEnvVars);
+
+    return BbPromise.resolve();
   }
 }
 


Test patch:
diff --git a/lib/plugins/aws/invokeLocal/index.test.js b/lib/plugins/aws/invokeLocal/index.test.js
index 63001f2102f..c0e5d51726c 100644
--- a/lib/plugins/aws/invokeLocal/index.test.js
+++ b/lib/plugins/aws/invokeLocal/index.test.js
@@ -241,7 +241,6 @@ describe('AwsInvokeLocal', () => {
         expect(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE).to.equal('1024');
         expect(process.env.AWS_LAMBDA_FUNCTION_VERSION).to.equal('$LATEST');
         expect(process.env.NODE_PATH).to.equal('/var/runtime:/var/task:/var/runtime/node_modules');
-        expect(process.env.IS_LOCAL).to.equal('true');
       })
     );
 
diff --git a/lib/plugins/invoke/invoke.test.js b/lib/plugins/invoke/invoke.test.js
index 0c0f9faf3ff..da707b70a97 100644
--- a/lib/plugins/invoke/invoke.test.js
+++ b/lib/plugins/invoke/invoke.test.js
@@ -1,9 +1,13 @@
 'use strict';
 
-const expect = require('chai').expect;
+const chai = require('chai');
 const Invoke = require('./invoke');
 const Serverless = require('../../Serverless');
 
+chai.use(require('chai-as-promised'));
+
+const expect = chai.expect;
+
 describe('Invoke', () => {
   let invoke;
   let serverless;
@@ -15,5 +19,28 @@ describe('Invoke', () => {
 
   describe('#constructor()', () => {
     it('should have commands', () => expect(invoke.commands).to.be.not.empty);
+    it('should have hooks', () => expect(invoke.hooks).to.be.not.empty);
+  });
+
+  describe('#loadEnvVarsForLocal()', () => {
+    it('should set IS_LOCAL', () => {
+      delete process.env.IS_LOCAL;
+      return expect(invoke.loadEnvVarsForLocal()).to.be.fulfilled
+      .then(() => expect(process.env.IS_LOCAL).to.equal('true'));
+    });
+  });
+
+  describe('hooks', () => {
+    describe('invoke:local:loadEnvVars', () => {
+      it('should be an event', () => {
+        expect(invoke.commands.invoke.commands.local.lifecycleEvents).to.contain('loadEnvVars');
+      });
+
+      it('should set IS_LOCAL', () => {
+        delete process.env.IS_LOCAL;
+        return expect(invoke.hooks['invoke:local:loadEnvVars']()).to.be.fulfilled
+        .then(() => expect(process.env.IS_LOCAL).to.equal('true'));
+      });
+    });
   });
 });