pulling in the iam-role model to the project, improving stage state syncing
This commit is contained in:
parent
676359c4d8
commit
440ac6c6f4
24 changed files with 645 additions and 376 deletions
8
.d.ts
vendored
8
.d.ts
vendored
|
|
@ -1,7 +1,6 @@
|
||||||
declare module '@serverless/core' {
|
declare module '@serverless/core' {
|
||||||
import { Credentials } from 'aws-sdk';
|
import { Credentials } from 'aws-sdk';
|
||||||
export class Component {
|
export class Component {
|
||||||
load(modulePath: string, moduleName?: string): any;
|
|
||||||
save(): void;
|
save(): void;
|
||||||
state: any;
|
state: any;
|
||||||
context: {
|
context: {
|
||||||
|
|
@ -60,6 +59,7 @@ type BaseDeploymentOptions = {
|
||||||
build?: BuildOptions;
|
build?: BuildOptions;
|
||||||
nextConfigDir?: string;
|
nextConfigDir?: string;
|
||||||
domain?: string | string[];
|
domain?: string | string[];
|
||||||
|
stage?: Stage;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BuildOptions = {
|
type BuildOptions = {
|
||||||
|
|
@ -67,3 +67,9 @@ type BuildOptions = {
|
||||||
cmd: string;
|
cmd: string;
|
||||||
args: string[];
|
args: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Stage = {
|
||||||
|
bucketName: string;
|
||||||
|
name: string;
|
||||||
|
versioned?: boolean;
|
||||||
|
};
|
||||||
|
|
|
||||||
113
README.md
113
README.md
|
|
@ -1,6 +1,6 @@
|
||||||
# Next Deploy
|
# Next Deploy
|
||||||
|
|
||||||
Effortless deployment for Next.js apps 🚀
|
Effortless deployment to AWS and GitHub Pages for Next.js apps 🚀
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
|
@ -10,12 +10,12 @@ Effortless deployment for Next.js apps 🚀
|
||||||
- [CLI](#CLI)
|
- [CLI](#CLI)
|
||||||
- [Distributed Deployments](#Distributed-Deployments)
|
- [Distributed Deployments](#Distributed-Deployments)
|
||||||
- [Environment](#Environment)
|
- [Environment](#Environment)
|
||||||
- [GitHub](#GitHub)
|
|
||||||
- [AWS](#AWS)
|
- [AWS](#AWS)
|
||||||
|
- [GitHub](#GitHub)
|
||||||
- [Configuration Options](#Configuration-Options)
|
- [Configuration Options](#Configuration-Options)
|
||||||
- [Base Options](#Base-Options)
|
- [Base Options](#Base-Options)
|
||||||
- [GitHub Options](#GitHub-Options)
|
|
||||||
- [AWS Options](#AWS-Options)
|
- [AWS Options](#AWS-Options)
|
||||||
|
- [GitHub Options](#GitHub-Options)
|
||||||
- [Advanced Usage](#Advanced-Usage)
|
- [Advanced Usage](#Advanced-Usage)
|
||||||
- [Redirecting Domains](#Redirecting-Domains)
|
- [Redirecting Domains](#Redirecting-Domains)
|
||||||
- [Deployment State](#Deployment-State)
|
- [Deployment State](#Deployment-State)
|
||||||
|
|
@ -25,37 +25,33 @@ Effortless deployment for Next.js apps 🚀
|
||||||
|
|
||||||
Make sure your environment is [configured to deploy](#Environment).
|
Make sure your environment is [configured to deploy](#Environment).
|
||||||
|
|
||||||
Run your deployment with a one-liner:
|
|
||||||
|
|
||||||
- `npx next-deploy`
|
|
||||||
|
|
||||||
Optionally you can also add and run `next deploy` from your Next.js app:
|
|
||||||
|
|
||||||
- `yarn add --dev next-deploy`
|
- `yarn add --dev next-deploy`
|
||||||
- `yarn next-deploy`
|
- `yarn next-deploy`
|
||||||
|
|
||||||
|
Or as a one-liner:
|
||||||
|
|
||||||
|
- `npx next-deploy`
|
||||||
|
|
||||||
You can safely add the `.next-deploy` and `.next-deploy-build` directories to your `.gitignore`.
|
You can safely add the `.next-deploy` and `.next-deploy-build` directories to your `.gitignore`.
|
||||||
|
|
||||||
<img src="deploy-term.gif" width="550" height="321" >
|
<img src="deploy-term.gif" width="550" height="321" >
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Next Deploy strives to support all the latest major Next.js features and allow for effortless deployment to either GitHub Pages ([static exports](#https://nextjs.org/docs/advanced-features/static-html-export) only) or AWS (full functionality).
|
Next Deploy strives to support the latest
|
||||||
|
|
||||||
AWS deployments will be created by running `next build` in `serverless-trace` mode. The pre-rendered and static files will be published to S3 and served through CloudFront. The handling of application-specific routing and some advanced functionality will be provided through [Lambda@Edge](#https://aws.amazon.com/lambda/edge/) functions.
|
- ✔ effortless deployment to AWS and GitHub pages
|
||||||
AWS deployments will also create new Route 53 records (if domains are configured). Note that you will need to [migrate to Route 53](#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/MigratingDNS.html) for DNS hosting.
|
- ✔ deployments to a custom domain
|
||||||
|
- ✔ static generation (SSG), server side rendering (SSR)
|
||||||
Work is underway to provide support for:
|
- ✔ serverless AWS architecture serving your Next.js content globally via [CloudFront](https://aws.amazon.com/cloudfront/) and [Lambda@Edge](https://aws.amazon.com/lambda/edge/)
|
||||||
|
- ✔ multi-environment support
|
||||||
- getStaticPaths with fallback
|
- ⚠ getStaticPaths with fallback
|
||||||
- preview mode
|
- ⚠ preview mode
|
||||||
- incremental static regeneration
|
- ⚠ incremental static regeneration (beta)
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
Next Deploy was created to deploy web applications built using the wonderful [Next.js](https://nextjs.org/) framework. It allows teams to easily integrate with the supported engines (AWS, GitHub Pages) and keep the entirety of their code in source control. From frontend, to backend, to the deployment logic.
|
Next Deploy was created to deploy web applications built using the wonderful [Next.js](https://nextjs.org/) framework. It allows teams to easily integrate with the supported engines (AWS, GitHub Pages) and keep the entirety of their code in source control. From frontend, to backend, to the deployment logic. Next Deploy started as a fork of serverless-next.js which itself is an orchestrator of various orphaned serverless-components.
|
||||||
|
|
||||||
Next Deploy started as a fork of [serverless-next.js](https://github.com/serverless-nextjs/serverless-next.js) which itself is an orchestrator of various [serverless-components](https://github.com/serverless-components/).
|
|
||||||
|
|
||||||
## CLI
|
## CLI
|
||||||
|
|
||||||
|
|
@ -88,10 +84,6 @@ Note how `build` and `deploy` can be run separately through the CLI. This allows
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
### GitHub
|
|
||||||
|
|
||||||
No specific environment configuration is necessary. By default, your app will be built and [exported](#https://nextjs.org/docs/advanced-features/static-html-export) to the `gh-pages` branch.
|
|
||||||
|
|
||||||
### AWS
|
### AWS
|
||||||
|
|
||||||
To deploy to AWS you will need to set your credentials in your environment:
|
To deploy to AWS you will need to set your credentials in your environment:
|
||||||
|
|
@ -156,6 +148,10 @@ You will need the following permissions:
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### GitHub
|
||||||
|
|
||||||
|
No specific environment configuration is necessary. By default, your app will be built and [exported](#https://nextjs.org/docs/advanced-features/static-html-export) to the `gh-pages` branch.
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
The next-deploy config varies by the provider (engine) that you're deploying to. All configuration options are optional and come with sensible defaults.
|
The next-deploy config varies by the provider (engine) that you're deploying to. All configuration options are optional and come with sensible defaults.
|
||||||
|
|
@ -166,15 +162,16 @@ The deployment configuration is to be provided through `next-deploy.config.js`,
|
||||||
All engines support the basic options:
|
All engines support the basic options:
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| ------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
|
| ------------- | --------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| build | [`Build`](#Build) | `{}` | Build related options. |
|
| build | [`Build`](#Build) | `{}` | Build related options. |
|
||||||
| debug | `boolean` | `false` | Print helpful messages to |
|
| debug | `boolean` | `false` | Print helpful messages to |
|
||||||
| domain | `string\|string[]` | `null` | The domain to deploy to . |
|
| domain | `string \| string[]` | `null` | The deployment domain. |
|
||||||
| engine | `"aws"\|"github"` | `aws` | The platform to deploy to. |
|
| engine | `"aws" \| "github"` | `aws` | The platform to deploy to. |
|
||||||
| nextConfigDir | `string` | `./` | The directory holding the `next.config.js`. |
|
| nextConfigDir | `string` | `./` | The directory holding the `next.config.js`. |
|
||||||
| onPostDeploy | `() => Promise<void>` | `null` | A callback that gets called after the deployment successfully finishes. |
|
| onPostDeploy | `() => Promise<void>` | `null` | A callback that gets called after the deployment successfully finishes. |
|
||||||
| onPreDeploy | `() => Promise<void>` | `null` | A callback that gets called before the deployment. |
|
| onPreDeploy | `() => Promise<void>` | `null` | A callback that gets called before the deployment. |
|
||||||
| onShutdown | `() => Promise<void>` | `null` | A callback that gets called after the deployment is shutdown by a INT/QUIT/TERM signal like from ctrl+c. |
|
| onShutdown | `() => Promise<void>` | `null` | A callback that gets called after the deployment is shutdown by a INT/QUIT/TERM signal like from ctrl+c. |
|
||||||
|
| stage | [`Stage`](#Stage) | `local` | Configure the stage ('dev', 'staging', 'production') of your deployment that will be used to synchronize its deployed state to an S3 bucket. |
|
||||||
|
|
||||||
#### Build
|
#### Build
|
||||||
|
|
||||||
|
|
@ -184,29 +181,36 @@ All engines support the basic options:
|
||||||
| cmd | `string` | `node_modules/.bin/next` | The build command. |
|
| cmd | `string` | `node_modules/.bin/next` | The build command. |
|
||||||
| cwd | `string` | `./` | The current working directory. |
|
| cwd | `string` | `./` | The current working directory. |
|
||||||
|
|
||||||
|
#### Stage
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| ---------- | --------- | -------------------------- | ----------------------------------------------------------------------------------------- |
|
||||||
|
| bucketName | `string` | `next-deploy-environments` | The S3 bucket name to sync the deployment stage to. `local` deployments don't get synced. |
|
||||||
|
| name | `string` | `local` | The name of the stage. |
|
||||||
|
| versioned | `boolean` | `false` | Whether the S3 bucket containing the stage's state should be versioned. |
|
||||||
|
|
||||||
|
### AWS Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
| -------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| bucketName | `string` | `*auto generated*` | Custom bucket name where static assets are stored. |
|
||||||
|
| bucketRegion | `string` | `us-east-1` | Region where you want to host your S3 bucket. |
|
||||||
|
| cloudfront | [`CloudFront`](#CloudFront) | `{}` | Additional cloudfront options. |
|
||||||
|
| description | `string` | <details>`"*lambda type* handler for the Next CloudFront distribution."`</details> | A description of the lambda. |
|
||||||
|
| domainType | `"www" \| "apex" \| "both"` | `both` | Can be one of: "**apex**" - apex domain only, don't create a www subdomain. "**www**" - www domain only, don't create an apex subdomain. "**both**" - create both www and apex domains when either one is provided. |
|
||||||
|
| memory | `number` | `512` | The amount of memory that a lambda has access to. Increasing the lambda's memory also increases its CPU allocation. The value must be a multiple of 64 MB. |
|
||||||
|
| name | `string` | `*auto generated*` | The name of the lambda function. |
|
||||||
|
| policy | `string` | <details>`arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole`</details> | The arn policy of the lambda. |
|
||||||
|
| publicDirectoryCache | `boolean \|`[`PublicDirectoryCache`](#PublicDirectoryCache) | `true` | Customize the public/static directory asset caching policy. Assigning an object lets you customize the caching policy and the types of files being cached. Assigning false disables caching. |
|
||||||
|
| runtime | `string` | `nodejs12.x` | The identifier of the lambda's runtime. |
|
||||||
|
| timeout | `number` | `10` | The amount of time that the lambda allows a function to run before stopping it. The maximum allowed value is 900 seconds. |
|
||||||
|
|
||||||
### Github Options
|
### Github Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| ------- | -------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
| ------- | -------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||||
| publish | [`Publish`](https://github.com/tschaub/gh-pages#options) | `{message: "Next Deployment Update", dotfiles: true}` | The [git-hub page options](https://github.com/tschaub/gh-pages#options) to publish with. |
|
| publish | [`Publish`](https://github.com/tschaub/gh-pages#options) | `{message: "Next Deployment Update", dotfiles: true}` | The [git-hub page options](https://github.com/tschaub/gh-pages#options) to publish with. |
|
||||||
|
|
||||||
### AWS Options
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| -------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| bucketName | `string` | `*auto generated*` | Custom bucket name where static assets are stored. |
|
|
||||||
| bucketRegion | `string` | `us-east-1` | Region where you want to host your S3 bucket. |
|
|
||||||
| cloudfront | [`CloudFront`](#CloudFront) | `{}` | Additional cloudfront options. |
|
|
||||||
| description | `string` | <details>`"*lambda type* handler for the Next CloudFront distribution."`</details> | A description of the lambda. |
|
|
||||||
| domainType | `"www"\|"apex"\|"both"` | `both` | Can be one of: "**apex**" - apex domain only, don't create a www subdomain. "**www**" - www domain only, don't create an apex subdomain. "**both**" - create both www and apex domains when either one is provided. |
|
|
||||||
| memory | `number` | `512` | The amount of memory that a lambda has access to. Increasing the lambda's memory also increases its CPU allocation. The value must be a multiple of 64 MB. |
|
|
||||||
| name | `string` | `*auto generated*` | The name of the lambda function. |
|
|
||||||
| policy | `string` | <details>`arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole`</details> | The arn policy of the lambda. |
|
|
||||||
| publicDirectoryCache | `boolean\|`[`PublicDirectoryCache`](#PublicDirectoryCache) | `true` | Customize the public/static directory asset caching policy. Assigning an object lets you customize the caching policy and the types of files being cached. Assigning false disables caching. |
|
|
||||||
| runtime | `string` | `nodejs12.x` | The identifier of the lambda's runtime. |
|
|
||||||
| stage | `boolean \|`[`Stage`](#Stage) | `false` | Configure the stage ('dev', 'staging', 'production') of your deployment that will be used to synchronize its deployed state to an S3 bucket.. |
|
|
||||||
| timeout | `number` | `10` | The amount of time that the lambda allows a function to run before stopping it. The maximum allowed value is 900 seconds. |
|
|
||||||
|
|
||||||
#### PublicDirectoryCache
|
#### PublicDirectoryCache
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|
|
@ -217,28 +221,21 @@ All engines support the basic options:
|
||||||
#### CloudFront
|
#### CloudFront
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| ---------------------- | ----------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | ---------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| fieldLevelEncryptionId | `string` | `""` | The value of the ID for the field-level encryption configuration that you want to use. |
|
| fieldLevelEncryptionId | `string` | `""` | The value of the ID for the field-level encryption configuration that you want to use. |
|
||||||
| forward | [`Forward`](#Forward) | `{}` | Determines the forwarding configuration |
|
| forward | [`Forward`](#Forward) | `{}` | Determines the forwarding configuration |
|
||||||
| smoothStreaming | `boolean` | `false` | Indicates whether you want to distribute media files in the Microsoft Smooth Streaming format. |
|
| smoothStreaming | `boolean` | `false` | Indicates whether you want to distribute media files in the Microsoft Smooth Streaming format. |
|
||||||
|
| priceClass | `"PriceClass_All" \| "PriceClass_200" \| "PriceClass_100"` | `PriceClass_All` | THe price class which determines the reach of the edge locations that will be used to serve your app. |
|
||||||
| ttl | `number` | `0` | The amount of time that you want objects to stay in CloudFront's cache before it forwards another request to determine whether the object has been updated. |
|
| ttl | `number` | `0` | The amount of time that you want objects to stay in CloudFront's cache before it forwards another request to determine whether the object has been updated. |
|
||||||
| viewerCertificate | [`ViewerCertificate`](#ViewerCertificate) | `{}` | Determines the SSL/TLS configuration for communicating with viewers. |
|
| viewerCertificate | [`ViewerCertificate`](#ViewerCertificate) | `{}` | Determines the SSL/TLS configuration for communicating with viewers. |
|
||||||
| viewerProtocolPolicy | `string` | `redirect-to-https` | The policy for viewers to access the content. |
|
| viewerProtocolPolicy | `string` | `redirect-to-https` | The policy for viewers to access the content. |
|
||||||
| "lambda@edge" | [`LambdaAtEdge`](#LambdaAtEdge) | `{}` | Additional lambda@edge functions. |
|
| "lambda@edge" | [`LambdaAtEdge`](#LambdaAtEdge) | `{}` | Additional lambda@edge functions. |
|
||||||
|
|
||||||
#### Stage
|
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
|
||||||
| ---------- | --------- | -------------------------- | ----------------------------------------------------------------------- |
|
|
||||||
| bucketName | `string` | `next-deploy-environments` | The S3 bucket name to sync the deployment stage to. |
|
|
||||||
| name | `string` | `dev` | The name of the stage. |
|
|
||||||
| versioned | `boolean` | `false` | Whether the S3 bucket containing the stage's state should be versioned. |
|
|
||||||
|
|
||||||
#### Forward
|
#### Forward
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| -------------------- | ------------------ | ------- | ------------------------------------------------------------------------ |
|
| -------------------- | -------------------- | ------- | ------------------------------------------------------------------------ |
|
||||||
| cookies | `string\|string[]` | `all` | Indicates which cookies should be forwarded. |
|
| cookies | `string \| string[]` | `all` | Indicates which cookies should be forwarded. |
|
||||||
| queryString | `boolean` | `true` | Indicates whether the query string should be forwarded. |
|
| queryString | `boolean` | `true` | Indicates whether the query string should be forwarded. |
|
||||||
| headers | `string[]` | `[]` | Headers to forward (whitelisted headers). |
|
| headers | `string[]` | `[]` | Headers to forward (whitelisted headers). |
|
||||||
| queryStringCacheKeys | `string[]` | `[]` | Details of the query string parameters that you want to use for caching. |
|
| queryStringCacheKeys | `string[]` | `[]` | Details of the query string parameters that you want to use for caching. |
|
||||||
|
|
@ -254,8 +251,8 @@ All engines support the basic options:
|
||||||
#### LambdaAtEdge
|
#### LambdaAtEdge
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| -------------------- | ------------------------------------------ | ------- | ----------------------------------------------------------------------- |
|
| -------------------- | -------------------------------------------- | ------- | ----------------------------------------------------------------------- |
|
||||||
| \*cloudfront event\* | `string\|{arn:string,includeBody:boolean}` | `null` | The customization for a new CloudFront event handler (lambda function). |
|
| \*cloudfront event\* | `string \| {arn:string,includeBody:boolean}` | `null` | The customization for a new CloudFront event handler (lambda function). |
|
||||||
|
|
||||||
## Advanced Usage
|
## Advanced Usage
|
||||||
|
|
||||||
|
|
|
||||||
10
package.json
10
package.json
|
|
@ -1,17 +1,19 @@
|
||||||
{
|
{
|
||||||
"name": "next-deploy",
|
"name": "next-deploy",
|
||||||
"version": "0.3.1",
|
"version": "1.0.0-alpha.0",
|
||||||
"description": "Effortless deployment for Next.js apps 🚀",
|
"description": "Effortless deployment for Next.js apps 🚀",
|
||||||
"author": "Nidratech Ltd. <egor@nidratech.com>",
|
"author": "Nidratech Ltd. <egor@nidratech.com>",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"next",
|
"next",
|
||||||
"deploy",
|
"deploy",
|
||||||
"nextjs",
|
"nextjs",
|
||||||
|
"serverless",
|
||||||
"aws",
|
"aws",
|
||||||
"github",
|
"github",
|
||||||
"lambda",
|
"lambda",
|
||||||
|
"lambda@edge",
|
||||||
"cloudfront",
|
"cloudfront",
|
||||||
"github-pages"
|
"gh-pages"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "lerna run --parallel build:watch",
|
"dev": "lerna run --parallel build:watch",
|
||||||
|
|
@ -33,8 +35,6 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/nidratech/next-deploy#readme",
|
"homepage": "https://github.com/nidratech/next-deploy#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@serverless/aws-iam-role": "^1.0.0",
|
|
||||||
"@serverless/aws-lambda-layer": "^1.0.0",
|
|
||||||
"@serverless/core": "^1.1.2",
|
"@serverless/core": "^1.1.2",
|
||||||
"@zeit/node-file-trace": "^0.8.0",
|
"@zeit/node-file-trace": "^0.8.0",
|
||||||
"ansi-escapes": "^4.3.1",
|
"ansi-escapes": "^4.3.1",
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
"@types/webpack": "^4.41.21",
|
"@types/webpack": "^4.41.21",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
"@typescript-eslint/eslint-plugin": "^3.6.1",
|
||||||
"@typescript-eslint/parser": "^3.6.1",
|
"@typescript-eslint/parser": "^3.6.1",
|
||||||
"eslint": "^7.4.0",
|
"eslint": "^7.5.0",
|
||||||
"eslint-config-prettier": "^6.11.0",
|
"eslint-config-prettier": "^6.11.0",
|
||||||
"eslint-plugin-prettier": "^3.1.4",
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
"husky": "^4.2.5",
|
"husky": "^4.2.5",
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ class CloudFront extends Component {
|
||||||
inputs.enabled = inputs.enabled === false ? false : true;
|
inputs.enabled = inputs.enabled === false ? false : true;
|
||||||
inputs.comment =
|
inputs.comment =
|
||||||
inputs.comment === null || inputs.comment === undefined ? '' : String(inputs.comment);
|
inputs.comment === null || inputs.comment === undefined ? '' : String(inputs.comment);
|
||||||
|
inputs.priceClass = ['PriceClass_All', 'PriceClass_200', 'PriceClass_100'].includes(
|
||||||
|
inputs.priceClass || ''
|
||||||
|
)
|
||||||
|
? inputs.priceClass
|
||||||
|
: 'PriceClass_All';
|
||||||
|
|
||||||
this.context.debug(
|
this.context.debug(
|
||||||
`Starting deployment of CloudFront distribution to the ${inputs.region} region.`
|
`Starting deployment of CloudFront distribution to the ${inputs.region} region.`
|
||||||
|
|
@ -45,12 +50,15 @@ class CloudFront extends Component {
|
||||||
region: inputs.region,
|
region: inputs.region,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.state.id = inputs.distributionId || this.state.id;
|
||||||
|
|
||||||
if (this.state.id) {
|
if (this.state.id) {
|
||||||
if (
|
if (
|
||||||
!equals(this.state.origins, inputs.origins) ||
|
!equals(this.state.origins, inputs.origins) ||
|
||||||
!equals(this.state.defaults, inputs.defaults) ||
|
!equals(this.state.defaults, inputs.defaults) ||
|
||||||
!equals(this.state.enabled, inputs.enabled) ||
|
!equals(this.state.enabled, inputs.enabled) ||
|
||||||
!equals(this.state.comment, inputs.comment)
|
!equals(this.state.comment, inputs.comment) ||
|
||||||
|
!equals(this.state.priceClass, inputs.priceClass)
|
||||||
) {
|
) {
|
||||||
this.context.debug(`Updating CloudFront distribution of ID ${this.state.id}.`);
|
this.context.debug(`Updating CloudFront distribution of ID ${this.state.id}.`);
|
||||||
this.state = await updateCloudFrontDistribution(cf, s3, this.state.id, inputs);
|
this.state = await updateCloudFrontDistribution(cf, s3, this.state.id, inputs);
|
||||||
|
|
@ -63,8 +71,10 @@ class CloudFront extends Component {
|
||||||
this.state.region = inputs.region;
|
this.state.region = inputs.region;
|
||||||
this.state.enabled = inputs.enabled;
|
this.state.enabled = inputs.enabled;
|
||||||
this.state.comment = inputs.comment;
|
this.state.comment = inputs.comment;
|
||||||
|
this.state.priceClass = inputs.priceClass;
|
||||||
this.state.origins = inputs.origins;
|
this.state.origins = inputs.origins;
|
||||||
this.state.defaults = inputs.defaults;
|
this.state.defaults = inputs.defaults;
|
||||||
|
|
||||||
await this.save();
|
await this.save();
|
||||||
|
|
||||||
this.context.debug(`CloudFront deployed successfully with URL: ${this.state.url}.`);
|
this.context.debug(`CloudFront deployed successfully with URL: ${this.state.url}.`);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ const triggersAllowedBody = ['viewer-request', 'origin-request'];
|
||||||
|
|
||||||
const makeCacheItem = (eventType: string, lambdaConfig: string | LambdaAtEdgeConfig) => {
|
const makeCacheItem = (eventType: string, lambdaConfig: string | LambdaAtEdgeConfig) => {
|
||||||
let arn, includeBody;
|
let arn, includeBody;
|
||||||
|
|
||||||
if (typeof lambdaConfig === 'string') {
|
if (typeof lambdaConfig === 'string') {
|
||||||
arn = lambdaConfig;
|
arn = lambdaConfig;
|
||||||
includeBody = triggersAllowedBody.includes(eventType);
|
includeBody = triggersAllowedBody.includes(eventType);
|
||||||
|
|
@ -37,12 +38,18 @@ const addLambdaAtEdgeToCacheBehavior = (cacheBehavior: any, lambdaAtEdge: Lambda
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const haveValidConfig =
|
||||||
|
//@ts-ignore
|
||||||
|
typeof lambdaAtEdge[eventType] === 'string' || lambdaAtEdge[eventType]?.arn;
|
||||||
|
|
||||||
|
if (haveValidConfig) {
|
||||||
cacheBehavior.LambdaFunctionAssociations.Items.push(
|
cacheBehavior.LambdaFunctionAssociations.Items.push(
|
||||||
makeCacheItem(eventType, lambdaAtEdge[eventType])
|
makeCacheItem(eventType, lambdaAtEdge[eventType])
|
||||||
);
|
);
|
||||||
|
|
||||||
cacheBehavior.LambdaFunctionAssociations.Quantity =
|
cacheBehavior.LambdaFunctionAssociations.Quantity =
|
||||||
cacheBehavior.LambdaFunctionAssociations.Quantity + 1;
|
cacheBehavior.LambdaFunctionAssociations.Quantity + 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export const createCloudFrontDistribution = async (
|
||||||
Items: [],
|
Items: [],
|
||||||
},
|
},
|
||||||
Origins,
|
Origins,
|
||||||
PriceClass: 'PriceClass_All',
|
PriceClass: inputs.priceClass,
|
||||||
Enabled: inputs.enabled as boolean,
|
Enabled: inputs.enabled as boolean,
|
||||||
HttpVersion: 'http2',
|
HttpVersion: 'http2',
|
||||||
DefaultCacheBehavior: getDefaultCacheBehavior(Origins.Items[0].Id, inputs.defaults),
|
DefaultCacheBehavior: getDefaultCacheBehavior(Origins.Items[0].Id, inputs.defaults),
|
||||||
|
|
@ -126,6 +126,7 @@ export const updateCloudFrontDistribution = async (
|
||||||
IfMatch: distributionConfigResponse.ETag,
|
IfMatch: distributionConfigResponse.ETag,
|
||||||
DistributionConfig: {
|
DistributionConfig: {
|
||||||
...distributionConfigResponse.DistributionConfig,
|
...distributionConfigResponse.DistributionConfig,
|
||||||
|
PriceClass: inputs.priceClass,
|
||||||
Enabled: inputs.enabled as boolean,
|
Enabled: inputs.enabled as boolean,
|
||||||
Comment: inputs.comment as string,
|
Comment: inputs.comment as string,
|
||||||
DefaultCacheBehavior: getDefaultCacheBehavior(Origins.Items[0].Id, inputs.defaults),
|
DefaultCacheBehavior: getDefaultCacheBehavior(Origins.Items[0].Id, inputs.defaults),
|
||||||
|
|
@ -133,6 +134,21 @@ export const updateCloudFrontDistribution = async (
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const origins = updateDistributionRequest.DistributionConfig.Origins;
|
||||||
|
const existingOriginIds = origins.Items.map((origin) => origin.Id);
|
||||||
|
|
||||||
|
Origins.Items.forEach((inputOrigin) => {
|
||||||
|
const originIndex = existingOriginIds.indexOf(inputOrigin.Id);
|
||||||
|
|
||||||
|
if (originIndex > -1) {
|
||||||
|
// replace origin with new input configuration
|
||||||
|
origins.Items.splice(originIndex, 1, inputOrigin);
|
||||||
|
} else {
|
||||||
|
origins.Items.push(inputOrigin);
|
||||||
|
origins.Quantity += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (CacheBehaviors) {
|
if (CacheBehaviors) {
|
||||||
updateDistributionRequest.DistributionConfig.CacheBehaviors = CacheBehaviors;
|
updateDistributionRequest.DistributionConfig.CacheBehaviors = CacheBehaviors;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
packages/aws-cloudfront/types.d.ts
vendored
2
packages/aws-cloudfront/types.d.ts
vendored
|
|
@ -1,9 +1,11 @@
|
||||||
export type CloudFrontInputs = {
|
export type CloudFrontInputs = {
|
||||||
|
distributionId?: string;
|
||||||
region?: string;
|
region?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
origins: string[] | Origin[];
|
origins: string[] | Origin[];
|
||||||
defaults?: PathPatternConfig;
|
defaults?: PathPatternConfig;
|
||||||
|
priceClass?: 'PriceClass_All' | 'PriceClass_200' | 'PriceClass_100';
|
||||||
};
|
};
|
||||||
|
|
||||||
type PathPatternConfig = {
|
type PathPatternConfig = {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import { SubDomain } from '@next-deploy/aws-domain/types';
|
||||||
import AwsLambda from '@next-deploy/aws-lambda';
|
import AwsLambda from '@next-deploy/aws-lambda';
|
||||||
import { AwsLambdaInputs } from '@next-deploy/aws-lambda/types';
|
import { AwsLambdaInputs } from '@next-deploy/aws-lambda/types';
|
||||||
import { OriginRequestHandlerManifest as BuildManifest } from '@next-deploy/aws-lambda-builder/types';
|
import { OriginRequestHandlerManifest as BuildManifest } from '@next-deploy/aws-lambda-builder/types';
|
||||||
import { PathPatternConfig } from '@next-deploy/aws-cloudfront/types';
|
|
||||||
import { Origin } from '@next-deploy/aws-cloudfront/types';
|
import { Origin } from '@next-deploy/aws-cloudfront/types';
|
||||||
import { getDomains, load } from './utils';
|
import { getDomains, load } from './utils';
|
||||||
import { DeploymentResult, AwsComponentInputs, LambdaType } from '../types';
|
import { DeploymentResult, AwsComponentInputs, LambdaType } from '../types';
|
||||||
|
|
@ -136,17 +135,39 @@ class Aws extends Component {
|
||||||
|
|
||||||
const nextConfigPath = nextConfigDir ? resolve(nextConfigDir) : process.cwd();
|
const nextConfigPath = nextConfigDir ? resolve(nextConfigDir) : process.cwd();
|
||||||
const nextStaticPath = nextStaticDir ? resolve(nextStaticDir) : nextConfigPath;
|
const nextStaticPath = nextStaticDir ? resolve(nextStaticDir) : nextConfigPath;
|
||||||
const customCloudFrontConfig: Record<string, any> = cloudfrontInput || {};
|
const {
|
||||||
|
defaults: cloudFrontDefaultsInputs,
|
||||||
|
origins: cloudFrontOriginsInputs,
|
||||||
|
priceClass: cloudFrontPriceClassInputs,
|
||||||
|
...cloudFrontOtherInputs
|
||||||
|
} = cloudfrontInput || {};
|
||||||
|
const cloudFrontDefaults = cloudFrontDefaultsInputs || {};
|
||||||
const calculatedBucketRegion = bucketRegion || 'us-east-1';
|
const calculatedBucketRegion = bucketRegion || 'us-east-1';
|
||||||
const [
|
const stageBucket = await load<AwsS3>('@next-deploy/aws-s3', this, 'StageStateStorage');
|
||||||
bucket,
|
const stageStateBucketName =
|
||||||
stageBucket,
|
(typeof stage !== 'boolean' && stage?.bucketName) || 'next-deploy-environments';
|
||||||
cloudfront,
|
const isSyncStateVersioned = typeof stage !== 'boolean' && stage?.versioned;
|
||||||
requestEdgeLambda,
|
const stageName = stage?.name || 'local';
|
||||||
defaultBuildManifest,
|
const canSyncStageState = stageName !== 'local';
|
||||||
] = await Promise.all([
|
|
||||||
|
if (canSyncStageState) {
|
||||||
|
await stageBucket.default({
|
||||||
|
accelerated: true,
|
||||||
|
name: stageStateBucketName,
|
||||||
|
region: calculatedBucketRegion,
|
||||||
|
});
|
||||||
|
|
||||||
|
await AwsS3.syncStageStateDirectory({
|
||||||
|
name: stageName,
|
||||||
|
bucketName: stageStateBucketName,
|
||||||
|
versioned: isSyncStateVersioned,
|
||||||
|
nextConfigDir: nextConfigPath,
|
||||||
|
credentials: this.context.credentials.aws,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [bucket, cloudfront, requestEdgeLambda, defaultBuildManifest] = await Promise.all([
|
||||||
load<AwsS3>('@next-deploy/aws-s3', this, 'StaticStorage'),
|
load<AwsS3>('@next-deploy/aws-s3', this, 'StaticStorage'),
|
||||||
load<AwsS3>('@next-deploy/aws-s3', this, 'StageStateStorage'),
|
|
||||||
load<AwsCloudFront>('@next-deploy/aws-cloudfront', this),
|
load<AwsCloudFront>('@next-deploy/aws-cloudfront', this),
|
||||||
load<AwsLambda>('@next-deploy/aws-lambda', this, 'RequestEdgeLambda'),
|
load<AwsLambda>('@next-deploy/aws-lambda', this, 'RequestEdgeLambda'),
|
||||||
this.readRequestLambdaBuildManifest(nextConfigPath),
|
this.readRequestLambdaBuildManifest(nextConfigPath),
|
||||||
|
|
@ -157,24 +178,6 @@ class Aws extends Component {
|
||||||
region: calculatedBucketRegion,
|
region: calculatedBucketRegion,
|
||||||
});
|
});
|
||||||
const bucketUrl = `http://${bucketOutputs.name}.s3.${calculatedBucketRegion}.amazonaws.com`;
|
const bucketUrl = `http://${bucketOutputs.name}.s3.${calculatedBucketRegion}.amazonaws.com`;
|
||||||
const stageStateBucketName =
|
|
||||||
(typeof stage !== 'boolean' && stage?.bucketName) || 'next-deploy-environments';
|
|
||||||
|
|
||||||
if (stage) {
|
|
||||||
const stageStateBucketOutputs = await stageBucket.default({
|
|
||||||
accelerated: true,
|
|
||||||
name: stageStateBucketName,
|
|
||||||
region: calculatedBucketRegion,
|
|
||||||
});
|
|
||||||
|
|
||||||
await AwsS3.syncStageStateDirectory({
|
|
||||||
name: (typeof stage !== 'boolean' && stage?.name) || 'dev',
|
|
||||||
bucketName: stageStateBucketOutputs.name,
|
|
||||||
versioned: typeof stage !== 'boolean' && stage.versioned,
|
|
||||||
nextConfigDir: nextConfigPath,
|
|
||||||
credentials: this.context.credentials.aws,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await AwsS3.uploadStaticAssets({
|
await AwsS3.uploadStaticAssets({
|
||||||
bucketName: bucketOutputs.name,
|
bucketName: bucketOutputs.name,
|
||||||
|
|
@ -202,10 +205,9 @@ class Aws extends Component {
|
||||||
|
|
||||||
// parse origins from inputs
|
// parse origins from inputs
|
||||||
let inputOrigins: any = [];
|
let inputOrigins: any = [];
|
||||||
if (cloudfrontInput?.origins) {
|
if (cloudFrontOriginsInputs) {
|
||||||
const origins = cloudfrontInput.origins as string[];
|
const origins = cloudFrontOriginsInputs as string[];
|
||||||
inputOrigins = origins.map(expandRelativeUrls);
|
inputOrigins = origins.map(expandRelativeUrls);
|
||||||
delete cloudfrontInput.origins;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloudFrontOrigins = [
|
const cloudFrontOrigins = [
|
||||||
|
|
@ -262,7 +264,7 @@ class Aws extends Component {
|
||||||
memory: getLambdaInputValue('memory', 'requestLambda', 512) as number,
|
memory: getLambdaInputValue('memory', 'requestLambda', 512) as number,
|
||||||
timeout: getLambdaInputValue('timeout', 'requestLambda', 10) as number,
|
timeout: getLambdaInputValue('timeout', 'requestLambda', 10) as number,
|
||||||
runtime: getLambdaInputValue('runtime', 'requestLambda', 'nodejs12.x') as string,
|
runtime: getLambdaInputValue('runtime', 'requestLambda', 'nodejs12.x') as string,
|
||||||
name: getLambdaInputValue('name', 'requestLambda', undefined) as string | undefined,
|
name: getLambdaInputValue('name', 'requestLambda', undefined) as string,
|
||||||
description: getLambdaInputValue(
|
description: getLambdaInputValue(
|
||||||
'description',
|
'description',
|
||||||
'requestLambda',
|
'requestLambda',
|
||||||
|
|
@ -272,18 +274,12 @@ class Aws extends Component {
|
||||||
|
|
||||||
const requestEdgeLambdaOutputs = await requestEdgeLambda.default(defaultEdgeLambdaInput);
|
const requestEdgeLambdaOutputs = await requestEdgeLambda.default(defaultEdgeLambdaInput);
|
||||||
const requestEdgeLambdaPublishOutputs = await requestEdgeLambda.publishVersion();
|
const requestEdgeLambdaPublishOutputs = await requestEdgeLambda.publishVersion();
|
||||||
let defaultCloudfrontInputs = {} as PathPatternConfig;
|
|
||||||
|
|
||||||
if (cloudfrontInput && cloudfrontInput.defaults) {
|
|
||||||
defaultCloudfrontInputs = cloudfrontInput.defaults;
|
|
||||||
delete cloudfrontInput.defaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate that the custom config paths match generated paths in the manifest
|
// validate that the custom config paths match generated paths in the manifest
|
||||||
this.validatePathPatterns(Object.keys(customCloudFrontConfig), defaultBuildManifest);
|
this.validatePathPatterns(Object.keys(cloudFrontOtherInputs), defaultBuildManifest);
|
||||||
|
|
||||||
// add any custom cloudfront configuration - this includes overrides for _next, static and api
|
// add any custom cloudfront configuration - this includes overrides for _next, static and api
|
||||||
Object.entries(customCloudFrontConfig).map(([path, config]) => {
|
Object.entries(cloudFrontOtherInputs).map(([path, config]) => {
|
||||||
const edgeConfig = {
|
const edgeConfig = {
|
||||||
...(config['lambda@edge'] || {}),
|
...(config['lambda@edge'] || {}),
|
||||||
};
|
};
|
||||||
|
|
@ -319,16 +315,17 @@ class Aws extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultLambdaAtEdgeConfig = {
|
const defaultLambdaAtEdgeConfig = {
|
||||||
...(defaultCloudfrontInputs['lambda@edge'] || {}),
|
...(cloudFrontDefaults['lambda@edge'] || {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloudFrontOutputs = await cloudfront.default({
|
const cloudFrontOutputs = await cloudfront.default({
|
||||||
defaults: {
|
defaults: {
|
||||||
ttl: 0,
|
ttl: 0,
|
||||||
...defaultCloudfrontInputs,
|
...cloudFrontDefaults,
|
||||||
forward: {
|
forward: {
|
||||||
cookies: 'all',
|
cookies: 'all',
|
||||||
queryString: true,
|
queryString: true,
|
||||||
...defaultCloudfrontInputs.forward,
|
...cloudFrontDefaults.forward,
|
||||||
},
|
},
|
||||||
allowedHttpMethods: ['HEAD', 'DELETE', 'POST', 'GET', 'OPTIONS', 'PUT', 'PATCH'],
|
allowedHttpMethods: ['HEAD', 'DELETE', 'POST', 'GET', 'OPTIONS', 'PUT', 'PATCH'],
|
||||||
'lambda@edge': {
|
'lambda@edge': {
|
||||||
|
|
@ -338,6 +335,9 @@ class Aws extends Component {
|
||||||
compress: true,
|
compress: true,
|
||||||
},
|
},
|
||||||
origins: cloudFrontOrigins,
|
origins: cloudFrontOrigins,
|
||||||
|
...(cloudFrontPriceClassInputs && {
|
||||||
|
priceClass: cloudFrontPriceClassInputs,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
let appUrl = cloudFrontOutputs.url;
|
let appUrl = cloudFrontOutputs.url;
|
||||||
|
|
@ -358,16 +358,16 @@ class Aws extends Component {
|
||||||
[subdomain]: cloudFrontOutputs as SubDomain,
|
[subdomain]: cloudFrontOutputs as SubDomain,
|
||||||
},
|
},
|
||||||
domainType: domainType || 'both',
|
domainType: domainType || 'both',
|
||||||
defaultCloudfrontInputs,
|
defaultCloudfrontInputs: cloudFrontDefaults,
|
||||||
});
|
});
|
||||||
appUrl = domainOutputs.domains[0];
|
appUrl = domainOutputs.domains[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage) {
|
if (canSyncStageState) {
|
||||||
await AwsS3.syncStageStateDirectory({
|
await AwsS3.syncStageStateDirectory({
|
||||||
name: (typeof stage !== 'boolean' && stage?.name) || 'dev',
|
name: stageName,
|
||||||
bucketName: stageStateBucketName,
|
bucketName: stageStateBucketName,
|
||||||
versioned: typeof stage !== 'boolean' && stage.versioned,
|
versioned: isSyncStateVersioned,
|
||||||
nextConfigDir: nextConfigPath,
|
nextConfigDir: nextConfigPath,
|
||||||
credentials: this.context.credentials.aws,
|
credentials: this.context.credentials.aws,
|
||||||
syncTo: true,
|
syncTo: true,
|
||||||
|
|
|
||||||
3
packages/aws-component/types.d.ts
vendored
3
packages/aws-component/types.d.ts
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
import { PublicDirectoryCache, Stage } from '@next-deploy/aws-s3/types';
|
import { PublicDirectoryCache } from '@next-deploy/aws-s3/types';
|
||||||
import { CloudFrontInputs } from '@next-deploy/aws-cloudfront/types';
|
import { CloudFrontInputs } from '@next-deploy/aws-cloudfront/types';
|
||||||
import { DomainType } from '@next-deploy/aws-domain/types';
|
import { DomainType } from '@next-deploy/aws-domain/types';
|
||||||
|
|
||||||
|
|
@ -15,7 +15,6 @@ type AwsComponentInputs = BaseDeploymentOptions & {
|
||||||
policy?: string;
|
policy?: string;
|
||||||
domainType?: DomainType;
|
domainType?: DomainType;
|
||||||
cloudfront?: CloudFrontInputs;
|
cloudfront?: CloudFrontInputs;
|
||||||
stage?: boolean | Stage;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type LambdaType = 'requestLambda' | 'responseLambda';
|
type LambdaType = 'requestLambda' | 'responseLambda';
|
||||||
|
|
|
||||||
13
packages/aws-iam-role/package.json
Normal file
13
packages/aws-iam-role/package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "@next-deploy/aws-iam-role",
|
||||||
|
"version": "4.2.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "dist/component.js",
|
||||||
|
"types": "dist/component.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yarn clean && yarn compile",
|
||||||
|
"build:watch": "yarn compile -w",
|
||||||
|
"clean": "rm -rf ./dist",
|
||||||
|
"compile": "tsc -p tsconfig.build.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
150
packages/aws-iam-role/src/component.ts
Normal file
150
packages/aws-iam-role/src/component.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
import { equals, mergeDeepRight } from 'ramda';
|
||||||
|
import { IAM } from 'aws-sdk';
|
||||||
|
import { Component } from '@serverless/core';
|
||||||
|
|
||||||
|
import { Role, Policy } from '../types';
|
||||||
|
import {
|
||||||
|
createRole,
|
||||||
|
deleteRole,
|
||||||
|
getRole,
|
||||||
|
addRolePolicy,
|
||||||
|
removeRolePolicy,
|
||||||
|
updateAssumeRolePolicy,
|
||||||
|
inputsChanged,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
const defaults: Role = {
|
||||||
|
service: 'lambda.amazonaws.com',
|
||||||
|
policy: {
|
||||||
|
arn: 'arn:aws:iam::aws:policy/AdministratorAccess',
|
||||||
|
},
|
||||||
|
region: 'us-east-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
class IamRole extends Component {
|
||||||
|
async default(
|
||||||
|
inputs: Role = {}
|
||||||
|
): Promise<{
|
||||||
|
name: string;
|
||||||
|
arn: string;
|
||||||
|
service: string | string[];
|
||||||
|
policy: Policy;
|
||||||
|
}> {
|
||||||
|
inputs = mergeDeepRight(defaults, inputs) as Role;
|
||||||
|
const iam = new IAM({ region: inputs.region, credentials: this.context.credentials.aws });
|
||||||
|
|
||||||
|
this.context.status('Deploying');
|
||||||
|
|
||||||
|
inputs.name = this.state.name || this.context.resourceId();
|
||||||
|
|
||||||
|
this.context.debug(`Syncing role ${inputs.name} in region ${inputs.region}.`);
|
||||||
|
const prevRole = await getRole({ iam, name: inputs.name as string });
|
||||||
|
|
||||||
|
if (!prevRole) {
|
||||||
|
this.context.debug(`Creating role ${inputs.name}.`);
|
||||||
|
this.context.status('Creating');
|
||||||
|
inputs.arn = await createRole({
|
||||||
|
iam,
|
||||||
|
name: inputs.name as string,
|
||||||
|
service: inputs.service as string | string[],
|
||||||
|
policy: inputs.policy as Policy,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
inputs.arn = prevRole.arn;
|
||||||
|
|
||||||
|
if (inputsChanged(prevRole as Role, inputs as Role)) {
|
||||||
|
this.context.status(`Updating`);
|
||||||
|
if (prevRole.service !== inputs.service) {
|
||||||
|
this.context.debug(`Updating service for role ${inputs.name}.`);
|
||||||
|
await updateAssumeRolePolicy({
|
||||||
|
iam,
|
||||||
|
name: inputs.name as string,
|
||||||
|
service: inputs.service as string | string[],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!equals(prevRole.policy, inputs.policy)) {
|
||||||
|
this.context.debug(`Updating policy for role ${inputs.name}.`);
|
||||||
|
await removeRolePolicy({
|
||||||
|
iam,
|
||||||
|
name: inputs.name as string,
|
||||||
|
policy: inputs.policy as Policy,
|
||||||
|
});
|
||||||
|
await addRolePolicy({
|
||||||
|
iam,
|
||||||
|
name: inputs.name as string,
|
||||||
|
policy: inputs.policy as Policy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo we probably don't need this logic now that
|
||||||
|
// we auto generate unconfigurable names
|
||||||
|
if (this.state.name && this.state.name !== inputs.name) {
|
||||||
|
this.context.status(`Replacing`);
|
||||||
|
this.context.debug(`Deleting/Replacing role ${inputs.name}.`);
|
||||||
|
await deleteRole({ iam, name: this.state.name, policy: inputs.policy as Policy });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.name = inputs.name;
|
||||||
|
this.state.arn = inputs.arn;
|
||||||
|
this.state.service = inputs.service;
|
||||||
|
this.state.policy = inputs.policy;
|
||||||
|
this.state.region = inputs.region;
|
||||||
|
|
||||||
|
await this.save();
|
||||||
|
|
||||||
|
this.context.debug(`Saved state for role ${inputs.name}.`);
|
||||||
|
|
||||||
|
const outputs = {
|
||||||
|
name: inputs.name as string,
|
||||||
|
arn: inputs.arn as string,
|
||||||
|
service: inputs.service as string | string[],
|
||||||
|
policy: inputs.policy as Policy,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.context.debug(`Role ${inputs.name} was successfully deployed to region ${inputs.region}.`);
|
||||||
|
this.context.debug(`Deployed role arn is ${inputs.arn}.`);
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(): Promise<void | {
|
||||||
|
name: string;
|
||||||
|
arn: string;
|
||||||
|
service: string[];
|
||||||
|
policy: Policy;
|
||||||
|
}> {
|
||||||
|
this.context.status('Removing');
|
||||||
|
|
||||||
|
if (!this.state.name) {
|
||||||
|
this.context.debug('Aborting removal. Role name not found in state.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iam = new IAM({
|
||||||
|
region: this.state.region,
|
||||||
|
credentials: this.context.credentials.aws,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.context.debug(`Removing role ${this.state.name} from region ${this.state.region}.`);
|
||||||
|
await deleteRole({ iam, ...this.state });
|
||||||
|
this.context.debug(
|
||||||
|
`Role ${this.state.name} successfully removed from region ${this.state.region}.`
|
||||||
|
);
|
||||||
|
|
||||||
|
const outputs = {
|
||||||
|
name: this.state.name,
|
||||||
|
arn: this.state.arn,
|
||||||
|
service: this.state.service,
|
||||||
|
policy: this.state.policy,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
|
await this.save();
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IamRole;
|
||||||
193
packages/aws-iam-role/src/utils.ts
Normal file
193
packages/aws-iam-role/src/utils.ts
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
import { utils } from '@serverless/core';
|
||||||
|
import { IAM } from 'aws-sdk';
|
||||||
|
import { equals, isEmpty, has, not, pick, type } from 'ramda';
|
||||||
|
|
||||||
|
import { Policy, Role } from '../types';
|
||||||
|
|
||||||
|
export const addRolePolicy = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
policy,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
policy: Policy;
|
||||||
|
}): Promise<void> => {
|
||||||
|
if (has('arn', policy)) {
|
||||||
|
await iam
|
||||||
|
.attachRolePolicy({
|
||||||
|
RoleName: name,
|
||||||
|
PolicyArn: policy.arn,
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
} else if (!isEmpty(policy)) {
|
||||||
|
await iam
|
||||||
|
.putRolePolicy({
|
||||||
|
RoleName: name,
|
||||||
|
PolicyName: `${name}-policy`,
|
||||||
|
PolicyDocument: JSON.stringify(policy),
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.sleep(15000);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeRolePolicy = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
policy,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
policy: Policy;
|
||||||
|
}): Promise<void> => {
|
||||||
|
if (has('arn', policy)) {
|
||||||
|
await iam
|
||||||
|
.detachRolePolicy({
|
||||||
|
RoleName: name,
|
||||||
|
PolicyArn: policy.arn,
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
} else if (!isEmpty(policy)) {
|
||||||
|
await iam
|
||||||
|
.deleteRolePolicy({
|
||||||
|
RoleName: name,
|
||||||
|
PolicyName: `${name}-policy`,
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRole = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
service,
|
||||||
|
policy,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
service: string | string[];
|
||||||
|
policy: Policy;
|
||||||
|
}): Promise<string> => {
|
||||||
|
const assumeRolePolicyDocument = {
|
||||||
|
Version: '2012-10-17',
|
||||||
|
Statement: {
|
||||||
|
Effect: 'Allow',
|
||||||
|
Principal: {
|
||||||
|
Service: service,
|
||||||
|
},
|
||||||
|
Action: 'sts:AssumeRole',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const roleRes = await iam
|
||||||
|
.createRole({
|
||||||
|
RoleName: name,
|
||||||
|
Path: '/',
|
||||||
|
AssumeRolePolicyDocument: JSON.stringify(assumeRolePolicyDocument),
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
|
||||||
|
await addRolePolicy({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
policy,
|
||||||
|
});
|
||||||
|
|
||||||
|
return roleRes.Role.Arn;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteRole = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
policy,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
policy: Policy;
|
||||||
|
}): Promise<void> => {
|
||||||
|
try {
|
||||||
|
await removeRolePolicy({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
policy,
|
||||||
|
});
|
||||||
|
await iam
|
||||||
|
.deleteRole({
|
||||||
|
RoleName: name,
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message !== `Policy ${policy.arn} was not found.` && error.code !== 'NoSuchEntity') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRole = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
}): Promise<null | undefined | Partial<Role>> => {
|
||||||
|
try {
|
||||||
|
const res = await iam.getRole({ RoleName: name }).promise();
|
||||||
|
// todo add policy
|
||||||
|
return {
|
||||||
|
name: res.Role.RoleName,
|
||||||
|
arn: res.Role.Arn,
|
||||||
|
service: JSON.parse(decodeURIComponent(res.Role.AssumeRolePolicyDocument as string))
|
||||||
|
.Statement[0].Principal.Service,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
if (e.message.includes('cannot be found')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateAssumeRolePolicy = async ({
|
||||||
|
iam,
|
||||||
|
name,
|
||||||
|
service,
|
||||||
|
}: {
|
||||||
|
iam: IAM;
|
||||||
|
name: string;
|
||||||
|
service: string | string[];
|
||||||
|
}): Promise<void> => {
|
||||||
|
const assumeRolePolicyDocument = {
|
||||||
|
Version: '2012-10-17',
|
||||||
|
Statement: {
|
||||||
|
Effect: 'Allow',
|
||||||
|
Principal: {
|
||||||
|
Service: service,
|
||||||
|
},
|
||||||
|
Action: 'sts:AssumeRole',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await iam
|
||||||
|
.updateAssumeRolePolicy({
|
||||||
|
RoleName: name,
|
||||||
|
PolicyDocument: JSON.stringify(assumeRolePolicyDocument),
|
||||||
|
})
|
||||||
|
.promise();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inputsChanged = (prevRole: Role, role: Role): boolean => {
|
||||||
|
// todo add name and policy
|
||||||
|
const inputs = pick(['service'], role);
|
||||||
|
const prevInputs = pick(['service'], prevRole);
|
||||||
|
|
||||||
|
if (type(inputs.service) === 'Array') {
|
||||||
|
//@ts-ignore
|
||||||
|
inputs?.service?.sort();
|
||||||
|
}
|
||||||
|
if (type(prevInputs.service) === 'Array') {
|
||||||
|
//@ts-ignore
|
||||||
|
prevInputs?.service?.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return not(equals(inputs, prevInputs));
|
||||||
|
};
|
||||||
9
packages/aws-iam-role/tsconfig.build.json
Normal file
9
packages/aws-iam-role/tsconfig.build.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"sourceMap": false,
|
||||||
|
"removeComments": true,
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["./src/", "../../.d.ts"]
|
||||||
|
}
|
||||||
11
packages/aws-iam-role/types.d.ts
vendored
Normal file
11
packages/aws-iam-role/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
export type Role = {
|
||||||
|
name?: string;
|
||||||
|
region?: string;
|
||||||
|
policy?: Policy;
|
||||||
|
service?: string | string[];
|
||||||
|
arn?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Policy = {
|
||||||
|
arn: string;
|
||||||
|
};
|
||||||
|
|
@ -140,6 +140,8 @@ const handler = ({
|
||||||
req.method = request.method;
|
req.method = request.method;
|
||||||
req.rawHeaders = [];
|
req.rawHeaders = [];
|
||||||
req.headers = {};
|
req.headers = {};
|
||||||
|
// @ts-ignore
|
||||||
|
req.connection = {};
|
||||||
|
|
||||||
if (request.querystring) {
|
if (request.querystring) {
|
||||||
req.url = `${req.url}?${request.querystring}`;
|
req.url = `${req.url}?${request.querystring}`;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,6 @@
|
||||||
"compile": "tsc -p tsconfig.build.json"
|
"compile": "tsc -p tsconfig.build.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next-deploy/aws-s3": "link:../aws-s3"
|
"@next-deploy/aws-iam-role": "link:../aws-iam-role"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import path from 'path';
|
import { Lambda } from 'aws-sdk';
|
||||||
import aws from 'aws-sdk';
|
|
||||||
import { Component, utils } from '@serverless/core';
|
import { Component, utils } from '@serverless/core';
|
||||||
import { mergeDeepRight, pick } from 'ramda';
|
import { mergeDeepRight, pick } from 'ramda';
|
||||||
|
|
||||||
|
|
@ -11,7 +10,9 @@ import {
|
||||||
deleteLambda,
|
deleteLambda,
|
||||||
configChanged,
|
configChanged,
|
||||||
pack,
|
pack,
|
||||||
|
load,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
import AwsIamRole from '@next-deploy/aws-iam-role';
|
||||||
import { AwsLambdaInputs } from '../types';
|
import { AwsLambdaInputs } from '../types';
|
||||||
|
|
||||||
const outputsList = [
|
const outputsList = [
|
||||||
|
|
@ -27,7 +28,6 @@ const outputsList = [
|
||||||
'runtime',
|
'runtime',
|
||||||
'env',
|
'env',
|
||||||
'role',
|
'role',
|
||||||
'layer',
|
|
||||||
'arn',
|
'arn',
|
||||||
'region',
|
'region',
|
||||||
];
|
];
|
||||||
|
|
@ -60,85 +60,25 @@ class LambdaComponent extends Component {
|
||||||
`Starting deployment of lambda ${config.name} to the ${config.region} region.`
|
`Starting deployment of lambda ${config.name} to the ${config.region} region.`
|
||||||
);
|
);
|
||||||
|
|
||||||
const lambda = new aws.Lambda({
|
const lambda = new Lambda({
|
||||||
region: config.region,
|
region: config.region,
|
||||||
credentials: this.context.credentials.aws,
|
credentials: this.context.credentials.aws,
|
||||||
});
|
});
|
||||||
|
|
||||||
const awsIamRole = await this.load('@serverless/aws-iam-role');
|
const awsIamRole = await load<AwsIamRole>('@next-deploy/aws-iam-role', this);
|
||||||
|
const outputsAwsIamRole = await awsIamRole.default(config.role);
|
||||||
// If no role exists, create a default role
|
|
||||||
let outputsAwsIamRole;
|
|
||||||
if (!config.role) {
|
|
||||||
this.context.debug(`No role provided for lambda ${config.name}.`);
|
|
||||||
|
|
||||||
outputsAwsIamRole = await awsIamRole({
|
|
||||||
service: 'lambda.amazonaws.com',
|
|
||||||
name: config.name,
|
|
||||||
policy: {
|
|
||||||
arn: 'arn:aws:iam::aws:policy/AdministratorAccess',
|
|
||||||
},
|
|
||||||
region: config.region,
|
|
||||||
});
|
|
||||||
config.role = { arn: outputsAwsIamRole.arn };
|
config.role = { arn: outputsAwsIamRole.arn };
|
||||||
} else {
|
|
||||||
outputsAwsIamRole = await awsIamRole(config.role);
|
|
||||||
config.role = { arn: outputsAwsIamRole.arn };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
config.bucket &&
|
|
||||||
SUPPORTED_RUNTIMES.includes(config.runtime) &&
|
|
||||||
(await utils.dirExists(path.join(config.code, 'node_modules')))
|
|
||||||
) {
|
|
||||||
this.context.debug(`Bucket ${config.bucket} is provided for lambda ${config.name}.`);
|
|
||||||
|
|
||||||
const layer = await this.load('@serverless/aws-lambda-layer');
|
|
||||||
|
|
||||||
const layerInputs = {
|
|
||||||
description: `${config.name} Dependencies Layer`,
|
|
||||||
code: path.join(config.code, 'node_modules'),
|
|
||||||
runtimes: SUPPORTED_RUNTIMES,
|
|
||||||
prefix: 'nodejs/node_modules',
|
|
||||||
bucket: config.bucket,
|
|
||||||
region: config.region,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.context.status('Deploying Dependencies');
|
|
||||||
this.context.debug(`Packaging lambda code from ${config.code}.`);
|
|
||||||
this.context.debug(`Uploading dependencies as a layer for lambda ${config.name}.`);
|
|
||||||
|
|
||||||
const promises = [pack(config.code, config.shims, false), layer(layerInputs)];
|
|
||||||
const res = await Promise.all(promises);
|
|
||||||
config.zipPath = res[0];
|
|
||||||
config.layer = res[1];
|
|
||||||
} else {
|
|
||||||
this.context.status('Packaging');
|
this.context.status('Packaging');
|
||||||
this.context.debug(`Packaging lambda code from ${config.code}.`);
|
this.context.debug(`Packaging lambda code from ${config.code}.`);
|
||||||
config.zipPath = (await pack(config.code, config.shims)) as string;
|
config.zipPath = (await pack(config.code, config.shims)) as string;
|
||||||
}
|
|
||||||
|
|
||||||
config.hash = await utils.hashFile(config.zipPath as string);
|
config.hash = await utils.hashFile(config.zipPath as string);
|
||||||
|
|
||||||
let deploymentBucket;
|
|
||||||
if (config.bucket) {
|
|
||||||
deploymentBucket = await this.load('@next-deploy/aws-s3');
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevLambda = await getLambda({ lambda, ...config });
|
const prevLambda = await getLambda({ lambda, ...config });
|
||||||
|
|
||||||
if (!prevLambda) {
|
if (!prevLambda) {
|
||||||
if (config.bucket) {
|
this.context.status('Creating');
|
||||||
this.context.debug(`Uploading ${config.name} lambda package to bucket ${config.bucket}.`);
|
|
||||||
this.context.status(`Uploading`);
|
|
||||||
|
|
||||||
await deploymentBucket.upload({
|
|
||||||
name: config.bucket,
|
|
||||||
file: config.zipPath,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.status(`Creating`);
|
|
||||||
this.context.debug(`Creating lambda ${config.name} in the ${config.region} region.`);
|
this.context.debug(`Creating lambda ${config.name} in the ${config.region} region.`);
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
|
|
@ -149,16 +89,7 @@ class LambdaComponent extends Component {
|
||||||
config.arn = prevLambda.arn;
|
config.arn = prevLambda.arn;
|
||||||
|
|
||||||
if (configChanged(prevLambda, config)) {
|
if (configChanged(prevLambda, config)) {
|
||||||
if (config.bucket && prevLambda.hash !== config.hash) {
|
if (prevLambda.hash !== config.hash) {
|
||||||
this.context.status(`Uploading code`);
|
|
||||||
this.context.debug(`Uploading ${config.name} lambda code to bucket ${config.bucket}.`);
|
|
||||||
|
|
||||||
await deploymentBucket.upload({
|
|
||||||
name: config.bucket,
|
|
||||||
file: config.zipPath,
|
|
||||||
});
|
|
||||||
await updateLambdaCode({ lambda, ...config });
|
|
||||||
} else if (!config.bucket && prevLambda.hash !== config.hash) {
|
|
||||||
this.context.status(`Uploading code`);
|
this.context.status(`Uploading code`);
|
||||||
this.context.debug(`Uploading ${config.name} lambda code.`);
|
this.context.debug(`Uploading ${config.name} lambda code.`);
|
||||||
await updateLambdaCode({ lambda, ...config });
|
await updateLambdaCode({ lambda, ...config });
|
||||||
|
|
@ -193,7 +124,7 @@ class LambdaComponent extends Component {
|
||||||
async publishVersion(): Promise<{ version: string | undefined }> {
|
async publishVersion(): Promise<{ version: string | undefined }> {
|
||||||
const { name, region, hash } = this.state;
|
const { name, region, hash } = this.state;
|
||||||
|
|
||||||
const lambda = new aws.Lambda({
|
const lambda = new Lambda({
|
||||||
region: region as string,
|
region: region as string,
|
||||||
credentials: this.context.credentials.aws,
|
credentials: this.context.credentials.aws,
|
||||||
});
|
});
|
||||||
|
|
@ -218,16 +149,14 @@ class LambdaComponent extends Component {
|
||||||
|
|
||||||
const { name, region } = this.state;
|
const { name, region } = this.state;
|
||||||
|
|
||||||
const lambda = new aws.Lambda({
|
const lambda = new Lambda({
|
||||||
region: region as string,
|
region: region as string,
|
||||||
credentials: this.context.credentials.aws,
|
credentials: this.context.credentials.aws,
|
||||||
});
|
});
|
||||||
|
|
||||||
const awsIamRole = await this.load('@serverless/aws-iam-role');
|
const awsIamRole = await load<AwsIamRole>('@next-deploy/aws-iam-role', this);
|
||||||
const layer = await this.load('@serverless/aws-lambda-layer');
|
|
||||||
|
|
||||||
await awsIamRole.remove();
|
await awsIamRole.remove();
|
||||||
await layer.remove();
|
|
||||||
|
|
||||||
this.context.debug(`Removing lambda ${name} from the ${region} region.`);
|
this.context.debug(`Removing lambda ${name} from the ${region} region.`);
|
||||||
await deleteLambda({ lambda, name: name as string });
|
await deleteLambda({ lambda, name: name as string });
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,6 @@ export const createLambda = async ({
|
||||||
zipPath,
|
zipPath,
|
||||||
bucket,
|
bucket,
|
||||||
role,
|
role,
|
||||||
layer,
|
|
||||||
}: AwsLambdaInputs): Promise<{ arn?: string; hash?: string }> => {
|
}: AwsLambdaInputs): Promise<{ arn?: string; hash?: string }> => {
|
||||||
const params: Lambda.Types.CreateFunctionRequest = {
|
const params: Lambda.Types.CreateFunctionRequest = {
|
||||||
FunctionName: name,
|
FunctionName: name,
|
||||||
|
|
@ -102,10 +101,6 @@ export const createLambda = async ({
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (layer && layer.arn) {
|
|
||||||
params.Layers = [layer.arn];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
params.Code.S3Bucket = bucket;
|
params.Code.S3Bucket = bucket;
|
||||||
params.Code.S3Key = path.basename(zipPath);
|
params.Code.S3Key = path.basename(zipPath);
|
||||||
|
|
@ -128,7 +123,6 @@ export const updateLambdaConfig = async ({
|
||||||
env,
|
env,
|
||||||
description,
|
description,
|
||||||
role,
|
role,
|
||||||
layer,
|
|
||||||
}: AwsLambdaInputs): Promise<{ arn?: string; hash?: string }> => {
|
}: AwsLambdaInputs): Promise<{ arn?: string; hash?: string }> => {
|
||||||
const functionConfigParams: Lambda.Types.UpdateFunctionConfigurationRequest = {
|
const functionConfigParams: Lambda.Types.UpdateFunctionConfigurationRequest = {
|
||||||
FunctionName: name,
|
FunctionName: name,
|
||||||
|
|
@ -143,10 +137,6 @@ export const updateLambdaConfig = async ({
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (layer && layer.arn) {
|
|
||||||
functionConfigParams.Layers = [layer.arn];
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await (lambda as Lambda).updateFunctionConfiguration(functionConfigParams).promise();
|
const res = await (lambda as Lambda).updateFunctionConfiguration(functionConfigParams).promise();
|
||||||
|
|
||||||
return { arn: res.FunctionArn, hash: res.CodeSha256 };
|
return { arn: res.FunctionArn, hash: res.CodeSha256 };
|
||||||
|
|
@ -285,3 +275,19 @@ export const pack = async (code: string, shims = [], packDeps = true): Promise<u
|
||||||
|
|
||||||
return packDir(code, outputFilePath, shims, exclude);
|
return packDir(code, outputFilePath, shims, exclude);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: remove me, this is a duplicate of aws-component's load
|
||||||
|
export const load = async <T>(path: string, that: any, name?: string): Promise<T> => {
|
||||||
|
const EngineComponent = await import(path);
|
||||||
|
const component = new EngineComponent.default(
|
||||||
|
`${that.id}.${name || EngineComponent.default.name}`,
|
||||||
|
that.context.instance
|
||||||
|
);
|
||||||
|
await component.init();
|
||||||
|
|
||||||
|
component.context.log = () => ({});
|
||||||
|
component.context.status = () => ({});
|
||||||
|
component.context.output = () => ({});
|
||||||
|
|
||||||
|
return component;
|
||||||
|
};
|
||||||
|
|
|
||||||
12
packages/aws-lambda/types.d.ts
vendored
12
packages/aws-lambda/types.d.ts
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
import { Lambda } from 'aws-sdk';
|
import { Lambda } from 'aws-sdk';
|
||||||
|
import { Role } from '@next-deploy/aws-iam-role/types';
|
||||||
|
|
||||||
export type AwsLambdaInputs = {
|
export type AwsLambdaInputs = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -12,18 +13,9 @@ export type AwsLambdaInputs = {
|
||||||
runtime: string;
|
runtime: string;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
region: string;
|
region: string;
|
||||||
role: Resource;
|
role: Role;
|
||||||
arn?: string;
|
arn?: string;
|
||||||
zipPath: string;
|
zipPath: string;
|
||||||
hash?: string;
|
hash?: string;
|
||||||
layer?: Resource;
|
|
||||||
lambda?: Lambda;
|
lambda?: Lambda;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Resource = {
|
|
||||||
policy?: {
|
|
||||||
arn: string;
|
|
||||||
};
|
|
||||||
service?: string[];
|
|
||||||
arn?: string;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -23,18 +23,19 @@ const syncStageStateDirectory = async ({
|
||||||
const stateRootDirectory = path.join(nextConfigDir, STATE_ROOT);
|
const stateRootDirectory = path.join(nextConfigDir, STATE_ROOT);
|
||||||
|
|
||||||
if (syncTo) {
|
if (syncTo) {
|
||||||
const stateRootDirectoryFiles = await readDirectoryFiles(stateRootDirectory);
|
const stateRootDirectoryFiles = await readDirectoryFiles(path.join(stateRootDirectory, stage));
|
||||||
|
|
||||||
const buildStateRootDirectoryFilesUploads = stateRootDirectoryFiles
|
const buildStateRootDirectoryFilesUploads = stateRootDirectoryFiles
|
||||||
.filter(filterOutDirectories)
|
.filter(filterOutDirectories)
|
||||||
.map(async (fileItem) => {
|
.map(async (fileItem) =>
|
||||||
const s3Key = pathToPosix(path.relative(path.resolve(nextConfigDir), fileItem.path));
|
s3.uploadFile({
|
||||||
|
s3Key: pathToPosix(path.relative(path.resolve(nextConfigDir), fileItem.path)).replace(
|
||||||
return s3.uploadFile({
|
`${STATE_ROOT}/`,
|
||||||
s3Key: `${stage}/${s3Key}`,
|
''
|
||||||
|
),
|
||||||
filePath: fileItem.path,
|
filePath: fileItem.path,
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
|
|
||||||
return Promise.all([...buildStateRootDirectoryFilesUploads]);
|
return Promise.all([...buildStateRootDirectoryFilesUploads]);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -51,7 +52,7 @@ const syncStageStateDirectory = async ({
|
||||||
for (const file of bucketFiles.Contents || []) {
|
for (const file of bucketFiles.Contents || []) {
|
||||||
if (file.Key) {
|
if (file.Key) {
|
||||||
const fileData = await s3.downloadFile({ s3Key: file.Key });
|
const fileData = await s3.downloadFile({ s3Key: file.Key });
|
||||||
files.push({ name: file.Key.replace(`${stage}/`, ''), data: fileData });
|
files.push({ name: path.join(STATE_ROOT, file.Key), data: fileData });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
packages/aws-s3/types.d.ts
vendored
6
packages/aws-s3/types.d.ts
vendored
|
|
@ -26,12 +26,6 @@ type SyncStageStateDirectoryOptions = Stage & {
|
||||||
syncTo?: boolean;
|
syncTo?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Stage = {
|
|
||||||
bucketName: string;
|
|
||||||
name: string;
|
|
||||||
versioned?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type UploadStaticAssetsOptions = {
|
type UploadStaticAssetsOptions = {
|
||||||
bucketName: string;
|
bucketName: string;
|
||||||
nextConfigDir: string;
|
nextConfigDir: string;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { utils } from '@serverless/core';
|
||||||
import { ContextMetrics, ContextConfig } from '../types';
|
import { ContextMetrics, ContextConfig } from '../types';
|
||||||
|
|
||||||
const { red, green, blue, dim: grey } = chalk;
|
const { red, green, blue, dim: grey } = chalk;
|
||||||
|
const STATE_ROOT = '.next-deploy';
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
root: string;
|
root: string;
|
||||||
|
|
@ -21,26 +22,24 @@ class Context {
|
||||||
outputs: Record<string, unknown>;
|
outputs: Record<string, unknown>;
|
||||||
metrics: ContextMetrics;
|
metrics: ContextMetrics;
|
||||||
|
|
||||||
constructor(config: ContextConfig) {
|
constructor({ root, stateRoot, credentials, debug, entity, message }: ContextConfig) {
|
||||||
this.root = path.resolve(config.root) || process.cwd();
|
this.root = path.resolve(root) || process.cwd();
|
||||||
this.stateRoot = config.stateRoot
|
this.stateRoot = stateRoot ? path.resolve(stateRoot) : path.join(this.root, STATE_ROOT);
|
||||||
? path.resolve(config.stateRoot)
|
|
||||||
: path.join(this.root, '.next-deploy');
|
|
||||||
|
|
||||||
this.credentials = config.credentials || {};
|
this.credentials = credentials || {};
|
||||||
this.debugMode = config.debug || false;
|
this.debugMode = debug || false;
|
||||||
this.state = { id: utils.randomId() };
|
this.state = { id: utils.randomId() };
|
||||||
this.id = this.state.id as string;
|
this.id = this.state.id as string;
|
||||||
this.outputs = {};
|
this.outputs = {};
|
||||||
|
|
||||||
this.metrics = {
|
this.metrics = {
|
||||||
entity: config.entity || 'Components',
|
entity: entity || 'Component',
|
||||||
lastDebugTime: undefined,
|
lastDebugTime: undefined,
|
||||||
useTimer: true,
|
useTimer: true,
|
||||||
seconds: 0,
|
seconds: 0,
|
||||||
status: {
|
status: {
|
||||||
running: false,
|
running: false,
|
||||||
message: config.message || 'Running',
|
message: message || 'Running',
|
||||||
loadingDots: '',
|
loadingDots: '',
|
||||||
loadingDotCount: 0,
|
loadingDotCount: 0,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,15 @@ import Context from './context';
|
||||||
import { createBaseConfig } from './utils';
|
import { createBaseConfig } from './utils';
|
||||||
|
|
||||||
const deploy = async (deployConfigPath: string, methodName = 'default'): Promise<void> => {
|
const deploy = async (deployConfigPath: string, methodName = 'default'): Promise<void> => {
|
||||||
|
const options: BaseDeploymentOptions = await import(deployConfigPath);
|
||||||
const {
|
const {
|
||||||
debug = false,
|
debug = false,
|
||||||
engine = DEFAULT_ENGINE,
|
engine = DEFAULT_ENGINE,
|
||||||
onPreDeploy,
|
onPreDeploy,
|
||||||
onPostDeploy,
|
onPostDeploy,
|
||||||
onShutdown,
|
onShutdown,
|
||||||
...componentOptions
|
stage,
|
||||||
}: BaseDeploymentOptions = await import(deployConfigPath);
|
} = options;
|
||||||
const engineIndex = SUPPORTED_ENGINES.findIndex(({ type }) => type === engine);
|
const engineIndex = SUPPORTED_ENGINES.findIndex(({ type }) => type === engine);
|
||||||
const isInit = methodName === 'init';
|
const isInit = methodName === 'init';
|
||||||
|
|
||||||
|
|
@ -50,7 +51,7 @@ const deploy = async (deployConfigPath: string, methodName = 'default'): Promise
|
||||||
|
|
||||||
const context = new Context({
|
const context = new Context({
|
||||||
root: process.cwd(),
|
root: process.cwd(),
|
||||||
stateRoot: path.join(process.cwd(), STATE_ROOT),
|
stateRoot: path.join(process.cwd(), STATE_ROOT, stage?.name || 'local'),
|
||||||
debug,
|
debug,
|
||||||
entity: engine.toUpperCase(),
|
entity: engine.toUpperCase(),
|
||||||
message: method.action,
|
message: method.action,
|
||||||
|
|
@ -67,7 +68,7 @@ const deploy = async (deployConfigPath: string, methodName = 'default'): Promise
|
||||||
context.metrics.lastDebugTime = new Date().getTime();
|
context.metrics.lastDebugTime = new Date().getTime();
|
||||||
context.statusEngineStart();
|
context.statusEngineStart();
|
||||||
|
|
||||||
const outputs = await component[methodName](componentOptions);
|
const outputs = await component[methodName](options);
|
||||||
|
|
||||||
context.renderOutputs(outputs);
|
context.renderOutputs(outputs);
|
||||||
|
|
||||||
|
|
|
||||||
134
yarn.lock
134
yarn.lock
|
|
@ -2029,43 +2029,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" ">= 8"
|
"@types/node" ">= 8"
|
||||||
|
|
||||||
"@serverless/aws-iam-role@^1.0.0":
|
"@serverless/core@^1.1.2":
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@serverless/aws-iam-role/-/aws-iam-role-1.0.0.tgz#bab4ee9dd82f3f05b875daa9bb4b27f28038dc51"
|
|
||||||
integrity sha512-xEUF4upBki7O20QR51V/hRscBSqIsOun6ePtnJkiIEzMi6Fj51PbzySlj95+Ll5qBA5m8gDytu49BEClLfk5DQ==
|
|
||||||
dependencies:
|
|
||||||
"@serverless/core" "^1.0.0"
|
|
||||||
aws-sdk "^2.488.0"
|
|
||||||
ramda "^0.26.0"
|
|
||||||
|
|
||||||
"@serverless/aws-lambda-layer@^1.0.0":
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@serverless/aws-lambda-layer/-/aws-lambda-layer-1.0.0.tgz#be51ef02a003bd2450bd0c7db37363d75c8874db"
|
|
||||||
integrity sha512-kjh+ib257gPO9nCJUQ4ZEV54gAPd4JDWWcbGdN2aa1HVhPZf2V8qBI7bNQMknp+u2mRU3y1f7L/Qihb9bIDEOw==
|
|
||||||
dependencies:
|
|
||||||
"@serverless/aws-s3" "^1.0.0"
|
|
||||||
"@serverless/core" "^1.0.0"
|
|
||||||
archiver "^3.0.0"
|
|
||||||
aws-sdk "^2.387.0"
|
|
||||||
fs-extra "^7.0.1"
|
|
||||||
globby "^9.2.0"
|
|
||||||
ramda "^0.26.1"
|
|
||||||
|
|
||||||
"@serverless/aws-s3@^1.0.0":
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@serverless/aws-s3/-/aws-s3-1.0.1.tgz#79befe9d92a083e22c99af7d33f7d1215561e6fd"
|
|
||||||
integrity sha512-RDN/ykANnUuqGUWCrQdEC9H4YVkqDCg46/ZzP7H/c6GSbRBL6Supbne3GOOPHspjxGZMJJ3Q9f7xSKHE6chKnQ==
|
|
||||||
dependencies:
|
|
||||||
"@serverless/core" "^1.0.0"
|
|
||||||
archiver "^3.0.0"
|
|
||||||
aws-sdk "^2.488.0"
|
|
||||||
fs-extra "^7.0.0"
|
|
||||||
klaw-sync "^6.0.0"
|
|
||||||
mime-types "^2.1.22"
|
|
||||||
ramda "^0.26.1"
|
|
||||||
s3-stream-upload "^2.0.2"
|
|
||||||
|
|
||||||
"@serverless/core@^1.0.0", "@serverless/core@^1.1.2":
|
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@serverless/core/-/core-1.1.2.tgz#96a2ac428d81c0459474e77db6881ebdd820065d"
|
resolved "https://registry.yarnpkg.com/@serverless/core/-/core-1.1.2.tgz#96a2ac428d81c0459474e77db6881ebdd820065d"
|
||||||
integrity sha512-PY7gH+7aQ+MltcUD7SRDuQODJ9Sav9HhFJsgOiyf8IVo7XVD6FxZIsSnpMI6paSkptOB7n+0Jz03gNlEkKetQQ==
|
integrity sha512-PY7gH+7aQ+MltcUD7SRDuQODJ9Sav9HhFJsgOiyf8IVo7XVD6FxZIsSnpMI6paSkptOB7n+0Jz03gNlEkKetQQ==
|
||||||
|
|
@ -2578,7 +2542,7 @@ acorn@^6.4.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
|
||||||
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
|
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
|
||||||
|
|
||||||
acorn@^7.1.1, acorn@^7.2.0:
|
acorn@^7.1.1, acorn@^7.3.1:
|
||||||
version "7.3.1"
|
version "7.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
|
||||||
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==
|
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==
|
||||||
|
|
@ -2765,19 +2729,6 @@ archiver-utils@^2.1.0:
|
||||||
normalize-path "^3.0.0"
|
normalize-path "^3.0.0"
|
||||||
readable-stream "^2.0.0"
|
readable-stream "^2.0.0"
|
||||||
|
|
||||||
archiver@^3.0.0:
|
|
||||||
version "3.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/archiver/-/archiver-3.1.1.tgz#9db7819d4daf60aec10fe86b16cb9258ced66ea0"
|
|
||||||
integrity sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==
|
|
||||||
dependencies:
|
|
||||||
archiver-utils "^2.1.0"
|
|
||||||
async "^2.6.3"
|
|
||||||
buffer-crc32 "^0.2.1"
|
|
||||||
glob "^7.1.4"
|
|
||||||
readable-stream "^3.4.0"
|
|
||||||
tar-stream "^2.1.0"
|
|
||||||
zip-stream "^2.1.2"
|
|
||||||
|
|
||||||
archiver@^4.0.2:
|
archiver@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/archiver/-/archiver-4.0.2.tgz#43c72865eadb4ddaaa2fb74852527b6a450d927c"
|
resolved "https://registry.yarnpkg.com/archiver/-/archiver-4.0.2.tgz#43c72865eadb4ddaaa2fb74852527b6a450d927c"
|
||||||
|
|
@ -2934,7 +2885,7 @@ async-each@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
|
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
|
||||||
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
|
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
|
||||||
|
|
||||||
async@^2.6.1, async@^2.6.3:
|
async@^2.6.1:
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
|
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
|
||||||
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
|
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
|
||||||
|
|
@ -2966,7 +2917,7 @@ atob@^2.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||||
|
|
||||||
aws-sdk@^2.387.0, aws-sdk@^2.488.0, aws-sdk@^2.715.0:
|
aws-sdk@^2.715.0:
|
||||||
version "2.715.0"
|
version "2.715.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.715.0.tgz#b890892098e0a4d9e7189ed341267d4a9a6e856b"
|
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.715.0.tgz#b890892098e0a4d9e7189ed341267d4a9a6e856b"
|
||||||
integrity sha512-O6ytb66IXFCowp0Ng2bSPM6v/cZVOhjJWFTR1CG4ieG4IroAaVgB3YQKkfPKA0Cy9B/Ovlsm7B737iuroKsd0w==
|
integrity sha512-O6ytb66IXFCowp0Ng2bSPM6v/cZVOhjJWFTR1CG4ieG4IroAaVgB3YQKkfPKA0Cy9B/Ovlsm7B737iuroKsd0w==
|
||||||
|
|
@ -3454,9 +3405,9 @@ caniuse-api@^3.0.0:
|
||||||
lodash.uniq "^4.5.0"
|
lodash.uniq "^4.5.0"
|
||||||
|
|
||||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001093:
|
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001093:
|
||||||
version "1.0.30001102"
|
version "1.0.30001103"
|
||||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001102.tgz#3275e7a8d09548f955f665e532df88de0b63741a"
|
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001103.tgz#fe81536d075b97cd013d4988c9212418faa289a8"
|
||||||
integrity sha512-fOjqRmHjRXv1H1YD6QVLb96iKqnu17TjcLSaX64TwhGYed0P1E1CCWZ9OujbbK4Z/7zax7zAzvQidzdtjx8RcA==
|
integrity sha512-EJkTPrZrgy712tjZ7GQDye5A67SQOyNS6X9b6GS/e5QFu5Renv5qfkx3GHq1S+vObxKzbWWYuPO/7nt4kYW/gA==
|
||||||
|
|
||||||
caseless@~0.12.0:
|
caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
|
|
@ -3767,16 +3718,6 @@ compose-function@3.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
arity-n "^1.0.4"
|
arity-n "^1.0.4"
|
||||||
|
|
||||||
compress-commons@^2.1.1:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-2.1.1.tgz#9410d9a534cf8435e3fbbb7c6ce48de2dc2f0610"
|
|
||||||
integrity sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==
|
|
||||||
dependencies:
|
|
||||||
buffer-crc32 "^0.2.13"
|
|
||||||
crc32-stream "^3.0.1"
|
|
||||||
normalize-path "^3.0.0"
|
|
||||||
readable-stream "^2.3.6"
|
|
||||||
|
|
||||||
compress-commons@^3.0.0:
|
compress-commons@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-3.0.0.tgz#833944d84596e537224dd91cf92f5246823d4f1d"
|
resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-3.0.0.tgz#833944d84596e537224dd91cf92f5246823d4f1d"
|
||||||
|
|
@ -4567,9 +4508,9 @@ ecc-jsbn@~0.1.1:
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
electron-to-chromium@^1.3.413, electron-to-chromium@^1.3.488:
|
electron-to-chromium@^1.3.413, electron-to-chromium@^1.3.488:
|
||||||
version "1.3.499"
|
version "1.3.500"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.499.tgz#06949f19877dafa42915e57dfeb4c1cfb86a8649"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.500.tgz#ac082dd2279251b14f1a7f6af6fd16655cf3eb7b"
|
||||||
integrity sha512-y7FwtQm/8xuLMnYQfBQDYzCpNn+VkSnf4c3Km5TWMNXg7JA5RQBuxmcLaKdDVcIK0K5xGIa7TlxpRt4BdNxNoA==
|
integrity sha512-Zz8BZh4Ssb/rZBaicqpi+GOQ0uu3y+24+MxBLCk0UYt8EGoZRP4cYzYHHwXGZfrSbCU4VDjbWN+Tg+TPgOUX6Q==
|
||||||
|
|
||||||
elliptic@^6.0.0, elliptic@^6.5.2:
|
elliptic@^6.0.0, elliptic@^6.5.2:
|
||||||
version "6.5.3"
|
version "6.5.3"
|
||||||
|
|
@ -4777,22 +4718,22 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0:
|
||||||
esrecurse "^4.1.0"
|
esrecurse "^4.1.0"
|
||||||
estraverse "^4.1.1"
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
eslint-utils@^2.0.0:
|
eslint-utils@^2.0.0, eslint-utils@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
|
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
|
||||||
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
|
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^1.1.0"
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0:
|
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
||||||
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
||||||
|
|
||||||
eslint@^7.4.0:
|
eslint@^7.5.0:
|
||||||
version "7.4.0"
|
version "7.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.4.0.tgz#4e35a2697e6c1972f9d6ef2b690ad319f80f206f"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.5.0.tgz#9ecbfad62216d223b82ac9ffea7ef3444671d135"
|
||||||
integrity sha512-gU+lxhlPHu45H3JkEGgYhWhkR9wLHHEXC9FbWFnTlEkbKyZKWgWRLgf61E8zWmBuI6g5xKBph9ltg3NtZMVF8g==
|
integrity sha512-vlUP10xse9sWt9SGRtcr1LAC67BENcQMFeV+w5EvLEoFe3xJ8cF1Skd0msziRx/VMC+72B4DxreCE+OR12OA6Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.0.0"
|
"@babel/code-frame" "^7.0.0"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
|
|
@ -4802,9 +4743,9 @@ eslint@^7.4.0:
|
||||||
doctrine "^3.0.0"
|
doctrine "^3.0.0"
|
||||||
enquirer "^2.3.5"
|
enquirer "^2.3.5"
|
||||||
eslint-scope "^5.1.0"
|
eslint-scope "^5.1.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.1.0"
|
||||||
eslint-visitor-keys "^1.2.0"
|
eslint-visitor-keys "^1.3.0"
|
||||||
espree "^7.1.0"
|
espree "^7.2.0"
|
||||||
esquery "^1.2.0"
|
esquery "^1.2.0"
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
file-entry-cache "^5.0.1"
|
file-entry-cache "^5.0.1"
|
||||||
|
|
@ -4818,7 +4759,7 @@ eslint@^7.4.0:
|
||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
levn "^0.4.1"
|
||||||
lodash "^4.17.14"
|
lodash "^4.17.19"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
optionator "^0.9.1"
|
optionator "^0.9.1"
|
||||||
|
|
@ -4831,14 +4772,14 @@ eslint@^7.4.0:
|
||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
v8-compile-cache "^2.0.3"
|
v8-compile-cache "^2.0.3"
|
||||||
|
|
||||||
espree@^7.1.0:
|
espree@^7.2.0:
|
||||||
version "7.1.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-7.2.0.tgz#1c263d5b513dbad0ac30c4991b93ac354e948d69"
|
||||||
integrity sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==
|
integrity sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^7.2.0"
|
acorn "^7.3.1"
|
||||||
acorn-jsx "^5.2.0"
|
acorn-jsx "^5.2.0"
|
||||||
eslint-visitor-keys "^1.2.0"
|
eslint-visitor-keys "^1.3.0"
|
||||||
|
|
||||||
esprima@^4.0.0:
|
esprima@^4.0.0:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
|
|
@ -5254,7 +5195,7 @@ fs-constants@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||||
|
|
||||||
fs-extra@^7.0.0, fs-extra@^7.0.1:
|
fs-extra@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||||
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||||
|
|
@ -6546,9 +6487,9 @@ lint-staged@^10.2.11:
|
||||||
stringify-object "^3.3.0"
|
stringify-object "^3.3.0"
|
||||||
|
|
||||||
listr2@^2.1.0:
|
listr2@^2.1.0:
|
||||||
version "2.2.0"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.2.0.tgz#cb88631258abc578c7fb64e590fe5742f28e4aac"
|
resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.2.1.tgz#3a0abf78a7a9d9fb4121a541b524cb52e8dcbbba"
|
||||||
integrity sha512-Q8qbd7rgmEwDo1nSyHaWQeztfGsdL6rb4uh7BA+Q80AZiDET5rVntiU1+13mu2ZTDVaBVbvAD1Db11rnu3l9sg==
|
integrity sha512-WhuhT7xpVi2otpY/OzJJ8DQhf6da8MjGiEhMdA9oQquwtsSfzZt+YKlasUBer717Uocd0oPmbPeiTD7MvGzctw==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
cli-truncate "^2.1.0"
|
cli-truncate "^2.1.0"
|
||||||
|
|
@ -6991,7 +6932,7 @@ mime-db@1.44.0:
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
|
||||||
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
|
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
|
||||||
|
|
||||||
mime-types@^2.1.12, mime-types@^2.1.22, mime-types@^2.1.27, mime-types@~2.1.19:
|
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19:
|
||||||
version "2.1.27"
|
version "2.1.27"
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
|
||||||
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
|
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
|
||||||
|
|
@ -8632,7 +8573,7 @@ quick-lru@^4.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
|
||||||
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
|
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
|
||||||
|
|
||||||
ramda@^0.26.0, ramda@^0.26.1:
|
ramda@^0.26.1:
|
||||||
version "0.26.1"
|
version "0.26.1"
|
||||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||||
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
||||||
|
|
@ -9943,7 +9884,7 @@ tapable@^1.0.0, tapable@^1.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||||
|
|
||||||
tar-stream@^2.1.0, tar-stream@^2.1.2:
|
tar-stream@^2.1.2:
|
||||||
version "2.1.3"
|
version "2.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.3.tgz#1e2022559221b7866161660f118255e20fa79e41"
|
||||||
integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==
|
integrity sha512-Z9yri56Dih8IaK8gncVPx4Wqt86NDmQTSh49XLZgjWpGZL9GK9HKParS2scqHCC4w6X9Gh2jwaU45V47XTKwVA==
|
||||||
|
|
@ -10808,15 +10749,6 @@ yargs@^14.2.2:
|
||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
yargs-parser "^15.0.1"
|
yargs-parser "^15.0.1"
|
||||||
|
|
||||||
zip-stream@^2.1.2:
|
|
||||||
version "2.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b"
|
|
||||||
integrity sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==
|
|
||||||
dependencies:
|
|
||||||
archiver-utils "^2.1.0"
|
|
||||||
compress-commons "^2.1.1"
|
|
||||||
readable-stream "^3.4.0"
|
|
||||||
|
|
||||||
zip-stream@^3.0.1:
|
zip-stream@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-3.0.1.tgz#cb8db9d324a76c09f9b76b31a12a48638b0b9708"
|
resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-3.0.1.tgz#cb8db9d324a76c09f9b76b31a12a48638b0b9708"
|
||||||
|
|
|
||||||
Reference in a new issue