Skip to content

Commit

Permalink
feat(Variables): sls:stage variable (#9296)
Browse files Browse the repository at this point in the history
A shortcut to

```
${opt:stage, self:provider.stage, "dev"}
```
  • Loading branch information
mnapoli committed Apr 20, 2021
1 parent ff589ba commit ef91ae1
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 29 deletions.
2 changes: 1 addition & 1 deletion docs/deprecations.md
Expand Up @@ -281,7 +281,7 @@ Starting with v3.0.0, API Gateway naming will be changed from `${stage}-${servic

Adapt to this convention now by setting `provider.apiGateway.shouldStartNameWithService` to `true`.

Eventually if you have a strong reason to stick to current convention, you may ensure it's kept after upgrading by setting: `provider.apiName: ${opt:stage, self:provider.stage, 'dev'}`
Eventually if you have a strong reason to stick to current convention, you may ensure it's kept after upgrading by setting: `provider.apiName: ${sls:stage}`

<a name="ALEXA_SKILL_EVENT_WITHOUT_APP_ID"><div>&nbsp;</div></a>

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/cicd/preview-deployments.md
Expand Up @@ -31,7 +31,7 @@ As development teams, we often have multiple pull requests and branches open at

To avoid this collision, you have the convenient option to "use branch name as stage", which ensures that each pull request will deploy to a unique stage. Since the stage is not preconfigured and may not yet exist at the time of deployment, the default deployment profile will be used. In this case, ensure that the default deployment profile is configured with an [AWS Access Role](/framework/docs/dashboard/access-roles/).

If you use the "use branch name as stage", you may also want to reference the branch name in your configuration using [Variables](/framework/docs/providers/aws/guide/variables/). Since the stage name matches the branch name, you can use the `${opt:stage, self:provider.stage, 'dev'}` variable in your `serverless.yml` to reference the stage name, which will match the branch name.
If you use the "use branch name as stage", you may also want to reference the branch name in your configuration using [Variables](/framework/docs/providers/aws/guide/variables/). Since the stage name matches the branch name, you can use the `${sls:stage}` variable in your `serverless.yml` to reference the stage name, which will match the branch name.

Branch names may also include characters such as `/` which are invalid characters for stage names. Invalid characters are replaced with `-` in Serverless CI/CD. For example, a branch `feature/ph-api` will be normalized as `feature-ph-api`.

Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/guide/credentials.md
Expand Up @@ -198,7 +198,7 @@ service: new-service
provider:
name: aws
runtime: nodejs12.x
profile: ${self:custom.profiles.${opt:stage, self:provider.stage, 'dev'}}
profile: ${self:custom.profiles.${sls:stage}}
custom:
profiles:
dev: devProfile
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/guide/functions.md
Expand Up @@ -36,7 +36,7 @@ provider:
functions:
hello:
handler: handler.hello # required, handler set in AWS Lambda
name: ${opt:stage, self:provider.stage, 'dev'}-lambdaName # optional, Deployed Lambda name
name: ${sls:stage}-lambdaName # optional, Deployed Lambda name
description: Description of what the lambda function does # optional, Description to publish to AWS
runtime: python2.7 # optional overwrite, default is provider runtime
memorySize: 512 # optional, in MB, default is 1024
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/guide/iam.md
Expand Up @@ -117,7 +117,7 @@ service: new-service
provider:
iam:
role:
name: your-custom-name-${self:provider.stage}-role
name: your-custom-name-${sls:stage}-role
```

## Custom IAM Roles
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/aws/guide/layers.md
Expand Up @@ -31,7 +31,7 @@ provider:
layers:
hello:
path: layer-dir # required, path to layer contents on disk
name: ${opt:stage, self:provider.stage, 'dev'}-layerName # optional, Deployed Lambda layer name
name: ${sls:stage}-layerName # optional, Deployed Lambda layer name
description: Description of what the lambda layer does # optional, Description to publish to AWS
compatibleRuntimes: # optional, a list of runtimes this layer is compatible with
- python3.8
Expand Down
6 changes: 3 additions & 3 deletions docs/providers/aws/guide/serverless.yml.md
Expand Up @@ -176,7 +176,7 @@ provider:
sessionTimeout: 7000
httpApi:
id: 'my-id' # If we want to attach to externally created HTTP API its id should be provided here
name: 'dev-my-service' # Use custom name for the API Gateway API, default is ${opt:stage, self:provider.stage, 'dev'}-${self:service}
name: 'dev-my-service' # Use custom name for the API Gateway API, default is ${sls:stage}-${self:service}
payload: '2.0' # Specify payload format version for Lambda integration ('1.0' or '2.0'), default is '2.0'
cors: true # Implies default behavior, can be fine tuned with specific options
authorizers:
Expand Down Expand Up @@ -293,7 +293,7 @@ functions:
usersCreate: # A Function
handler: users.create # The file and module for this specific function. Cannot be used when `image` is defined.
image: baseimage # Image to be used by function, cannot be used when `handler` is defined. It can be configured as concrete uri of Docker image in ECR or as a reference to image defined in `provider.ecr.images`
name: ${opt:stage, self:provider.stage, 'dev'}-lambdaName # optional, Deployed Lambda name
name: ${sls:stage}-lambdaName # optional, Deployed Lambda name
description: My function # The description of your function.
memorySize: 512 # memorySize for this specific function.
reservedConcurrency: 5 # optional, reserved concurrency limit for this function. By default, AWS uses account concurrency limit
Expand Down Expand Up @@ -570,7 +570,7 @@ functions:
layers:
hello: # A Lambda layer
path: layer-dir # required, path to layer contents on disk
name: ${opt:stage, self:provider.stage, 'dev'}-layerName # optional, Deployed Lambda layer name
name: ${sls:stage}-layerName # optional, Deployed Lambda layer name
description: Description of what the lambda layer does # optional, Description to publish to AWS
compatibleRuntimes: # optional, a list of runtimes this layer is compatible with
- python3.8
Expand Down
4 changes: 2 additions & 2 deletions docs/providers/aws/guide/services.md
Expand Up @@ -107,8 +107,8 @@ provider:
runtime: nodejs12.x
stage: dev # Set the default stage used. Default is dev
region: us-east-1 # Overwrite the default region used. Default is us-east-1
stackName: my-custom-stack-name-${opt:stage, self:provider.stage, 'dev'} # Overwrite default CloudFormation stack name. Default is ${self:service}-${opt:stage, self:provider.stage, 'dev'}
apiName: my-custom-api-gateway-name-${opt:stage, self:provider.stage, 'dev'} # Overwrite default API Gateway name. Default is ${opt:stage, self:provider.stage, 'dev'}-${self:service}
stackName: my-custom-stack-name-${sls:stage} # Overwrite default CloudFormation stack name. Default is ${self:service}-${sls:stage}
apiName: my-custom-api-gateway-name-${sls:stage} # Overwrite default API Gateway name. Default is ${sls:stage}-${self:service}
profile: production # The default profile to use with this service
memorySize: 512 # Overwrite the default memory size. Default is 1024
deploymentBucket:
Expand Down
4 changes: 4 additions & 0 deletions docs/providers/aws/guide/variables.md
Expand Up @@ -134,6 +134,10 @@ functions:
APIG_DEPLOYMENT_ID: ApiGatewayDeployment${sls:instanceId}
```

**stage**

The stage used by the Serverless CLI. The `${sls:stage}` variable is a shortcut for `${opt:stage, self:provider.stage, "dev"}`.

## Referencing Environment Variables

To reference environment variables, use the `${env:SOME_VAR}` syntax in your `serverless.yml` configuration file. It is valid to use the empty string in place of `SOME_VAR`. This looks like "`${env:}`" and the result of declaring this in your `serverless.yml` is to embed the complete `process.env` object (i.e. all the variables defined in your environment).
Expand Down
4 changes: 1 addition & 3 deletions docs/providers/spotinst/cli-reference/stage.md
Expand Up @@ -37,11 +37,9 @@ You will also need to update the environment parameter to point to the config.js

```yaml
 spotinst:
environment: ${file(./config.json):${opt:stage, self:provider.stage, 'dev'}}
environment: ${file(./config.json):${sls:stage}}
```

Note that while I am using 'dev' as the default stage, you may change this parameter to a custom default stage.

## Through the .yml File

To change the stage in the serverless.yml file you need to add the following into the provider tag then deploy your function as usual
Expand Down
Expand Up @@ -5,7 +5,7 @@ const ServerlessError = require('../../../../serverless-error');

module.exports = (serverlessInstance) => {
return {
resolve: ({ address }) => {
resolve: async ({ address, resolveConfigurationProperty, options }) => {
if (!address) {
throw new ServerlessError(
'Missing address argument in variable "sls" source',
Expand All @@ -20,6 +20,12 @@ module.exports = (serverlessInstance) => {
switch (address) {
case 'instanceId':
return { value: serverlessInstance.instanceId };
case 'stage': {
let stage = options.stage;
if (!stage) stage = await resolveConfigurationProperty(['provider', 'stage']);
if (!stage) stage = 'dev';
return { value: stage };
}
default:
throw new ServerlessError(
`Unsupported "${address}" address argument in variable "sls" source`,
Expand Down
@@ -1,6 +1,7 @@
'use strict';

const { expect } = require('chai');
const _ = require('lodash');

const resolveMeta = require('../../../../../../../lib/configuration/variables/resolve-meta');
const resolve = require('../../../../../../../lib/configuration/variables/resolve');
Expand All @@ -13,18 +14,24 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s
let variablesMeta;
let serverlessInstance;

before(async () => {
const initializeServerless = async (configExt, options) => {
configuration = {
service: 'foo',
provider: { name: 'aws' },
provider: {
name: 'aws',
},
custom: {
sls: '${sls:instanceId}',
stage: '${sls:stage}',
missingAddress: '${sls:}',
unsupportedAddress: '${sls:foo}',
nonStringAddress: '${sls:${self:custom.someObject}}',
someObject: {},
},
};
if (configExt) {
configuration = _.merge(configuration, configExt);
}
variablesMeta = resolveMeta(configuration);
serverlessInstance = new Serverless({
configuration,
Expand All @@ -40,29 +47,61 @@ describe('test/unit/lib/configuration/variables/sources/instance-dependent/get-s
configuration,
variablesMeta,
sources: { self: selfSource, sls: getSlsSource(serverlessInstance) },
options: {},
options: options || {},
fulfilledSources: new Set(['self', 'sls']),
});
});
};

it('should resolve instanceId', () => {
it('should resolve ${sls.instanceId}', async () => {
await initializeServerless();
if (variablesMeta.get('custom\0sls')) throw variablesMeta.get('custom\0sls').error;
expect(typeof serverlessInstance.instanceId).to.equal('string');
expect(configuration.custom.sls).to.equal(serverlessInstance.instanceId);
});

it('should report with an error missing address', () =>
expect(variablesMeta.get('custom\0missingAddress').error.code).to.equal(
it('should resolve ${sls:stage}', async () => {
// Dev by default
await initializeServerless();
expect(configuration.custom.stage).to.equal('dev');
// Resolves to provider.stage if it exists
await initializeServerless({
provider: {
stage: 'prod',
},
});
expect(configuration.custom.stage).to.equal('prod');
// Resolves to `--stage=` if the option is set
await initializeServerless(
{
provider: {
stage: 'prod',
},
},
{
stage: 'staging',
}
);
expect(configuration.custom.stage).to.equal('staging');
});

it('should report with an error missing address', async () => {
await initializeServerless();
return expect(variablesMeta.get('custom\0missingAddress').error.code).to.equal(
'VARIABLE_RESOLUTION_ERROR'
));
);
});

it('should report with an error unsupported address', () =>
expect(variablesMeta.get('custom\0unsupportedAddress').error.code).to.equal(
it('should report with an error unsupported address', async () => {
await initializeServerless();
return expect(variablesMeta.get('custom\0unsupportedAddress').error.code).to.equal(
'VARIABLE_RESOLUTION_ERROR'
));
);
});

it('should report with an error a non-string address', () =>
expect(variablesMeta.get('custom\0nonStringAddress').error.code).to.equal(
it('should report with an error a non-string address', async () => {
await initializeServerless();
return expect(variablesMeta.get('custom\0nonStringAddress').error.code).to.equal(
'VARIABLE_RESOLUTION_ERROR'
));
);
});
});

0 comments on commit ef91ae1

Please sign in to comment.