Skip to content

Commit

Permalink
feat: init
Browse files Browse the repository at this point in the history
  • Loading branch information
brettstack committed Mar 25, 2020
0 parents commit 4963dd4
Show file tree
Hide file tree
Showing 6 changed files with 5,065 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# serverless-amplify-plugin

A <a href="https://serverless.com/" target="_blank">Serverless Framework</a> plugin that provides simplified syntax for creating <a href="https://aws.amazon.com/amplify/console/" target="_blank">AWS Amplify Console</a> applications. Amplify console provides hosting and continuous deployment of static websites.

## Usage

```shell
npm i -D serverless-amplify-plugin
```

```yaml
# serverless.yaml
plugins:
- serverless-amplify-plugin

custom:
amplify:
repository: https://github.com/USER/REPO # required
accessTokenSecretName: AmplifyGithub # optional
accessTokenSecretKey: accessToken # optional
branch: master # optional
domainName: example.com # optional;
buildSpec: |- # optional
version: 0.1
frontend:
...
```
### 🔒 Securing your GitHub Personal Access Token Secret
It's important **not** to paste your GitHub Personal Access Token directly into the `accessToken` property. At a minimum, you should use `${{env:GITHUB_PERSONAL_ACCESS_TOKEN}}` along with the <a href="serverless-dotenv-plugin" target="_blank">serverless-dotenv-plugin</a>, however, this will still be visible in the CloudFormation template and logs.

The recommended way is to store your secret in <a href="https://aws.amazon.com/secrets-manager/" target="_blank">AWS Secrets Manager</a>. You can do this via the AWS Console or by running this command (ensure your profile and region are correct):

```shell
aws secretsmanager create-secret --name AmplifyGithub --secret-string '[{"personalAccessToken":"82dcc67482dcc67482dcc67482dcc67482dcc67482dcc674"}]'
```

## Options

### repository (required)

The GitHub repository URL (`https://github.com/USER/REPO`) of the project for which you want to set up Continuous Deployment and hosting.

### accessTokenSecretName (optional)

Shorthand equivalent of `accessToken: '{{resolve:secretsmanager:AmplifyGithub:SecretString:personalAccessToken}}'` where:
```yaml
accessTokenSecretName: AmplifyGithub
accessTokenSecretKey: personalAccessToken
```
**Default**: AmplifyGithub
### accessTokenSecretKey (optional)
**Default:** accessToken
### 🔒 accessToken (required*)
A <a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line" target="_blank">GitHub Personal Access Token</a> with `repo` permissions. Amplify Console sets up a <a href="https://developer.github.com/webhooks/" target="_blank">GitHub Webhook</a> so that it can be notified of new commits to build and deploy any changes.

🔒 This is a secret! It's recommended to store this in <a href="https://aws.amazon.com/secrets-manager/" target="_blank">AWS Secrets Manager</a>.

### branch (optional)

Amplify Console is notified of changes to this branch.

**Default:** master

### domainName (optional)

When specified, Amplify Console sets up a custom domain name for your application. You will need to perform additional <a href="https://docs.aws.amazon.com/amplify/latest/userguide/howto-third-party-domains.html" target="_blank">steps to verify the domain with your DNS provider and approve the SSL Certificate</a>.

**Default:** None. When deployed, your website is accessible via a subdomain `https://{branchName}.{appId}.amplifyapp.com`.

### buildSpec (optional)

Amplify Console executes a build according to these instructions. See <a href="https://docs.aws.amazon.com/amplify/latest/userguide/build-settings.html" target="_blank">Configuring Build Settings</a> for more information.

**Default:** A standard build spec that runs `npm ci` and `npm run build` and expects build artifacts to be stored in `dist/`:

```yaml
version: 0.1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- node_modules/**/*
```

## Limitations and future considerations

This plugin is currently only written with a basic single branch setup in mind. In the future we'd like to add support for all of Amplify Console's features including:

1. Multiple branches
2. Multiple domains
3. PR Previews
4. Environment Variables
5. Email Notifications
6. Access Control
7. Rewrites and redirects
128 changes: 128 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
const { pascalCase } = require('pascal-case')

class ServerlessAmplifyPlugin {
constructor(serverless, options) {
this.serverless = serverless
this.options = options
// const credentials = serverless.providers.aws.getCredentials()
// this.amplifySdk = new serverless.providers.aws.sdk.Amplify(credentials)
this.hooks = {
'before:package:finalize': () => this.addAmplify(),
// TODO: Get correct hook to run after CloudFormation success
'after:deploy:finalize': () => this.startFirstJob(),
}
this.variableResolvers = {
amplify: {
resolver: this.amplifyVariableResolver,
isDisabledAtPrepopulation: true,
serviceName: 'serverless-amplify..'
}
}
}

amplifyVariableResolver(src) {
return src.slice('amplify:'.length)
}

addAmplify() {
const { service } = this.serverless
const { custom, provider, serviceObject } = service
const { amplify } = custom
const { defaultBuildSpecOverrides = {} } = amplify
const {
baseDirectory = 'dist'
} = defaultBuildSpecOverrides
const {
repository,
accessTokenSecretName = 'AmplifyGithub',
accessTokenSecretKey = 'accessToken',
accessToken = `{{resolve:secretsmanager:${accessTokenSecretName}:SecretString:${accessTokenSecretKey}}}`,
branch = 'master',
domainName,
enableAutoBuild = true,
redirectNakedToWww = false,
name = serviceObject.name,
stage = 'PRODUCTION',
buildSpec = `version: 0.1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- npm run build
artifacts:
baseDirectory: ${baseDirectory}
files:
- '**/*'
cache:
paths:
- node_modules/**/*`,
} = amplify
const { Resources, Outputs } = provider.compiledCloudFormationTemplate
const namePascalCase = pascalCase(name)
Resources[`${namePascalCase}AmplifyApp`] = {
Type: 'AWS::Amplify::App',
Properties: {
Name: name,
Repository: repository,
AccessToken: accessToken,
BuildSpec: buildSpec
}
}

Resources[`${namePascalCase}AmplifyBranch`] = {
Type: 'AWS::Amplify::Branch',
Properties: {
AppId: { 'Fn::GetAtt': [`${namePascalCase}AmplifyApp`, 'AppId'] },
BranchName: branch,
EnableAutoBuild: enableAutoBuild,
Stage: stage
}
}

if (domainName) {
if (redirectNakedToWww) {
Resources[`${namePascalCase}AmplifyApp`].Properties.CustomRules = {
Source: `https://${domainName}`,
Target: `https://www.${domainName}`,
Status: "302"
}
}

Resources[`${namePascalCase}AmplifyDomain`] = {
Type: 'AWS::Amplify::Domain',
Properties: {
DomainName: domainName,
AppId: { 'Fn::GetAtt': [`${namePascalCase}AmplifyApp`, 'AppId'] },
SubDomainSettings: [
{
Prefix: '',
BranchName: { 'Fn::GetAtt': [`${namePascalCase}AmplifyBranch`, 'BranchName'] }
}
]
}
}

Outputs[`${namePascalCase}AmplifyBranchUrl`] = {
"Value": {
"Fn::Sub": `\${${namePascalCase}AmplifyBranch.BranchName}.\${${namePascalCase}AmplifyDomain.DomainName}`
}
}
}

Outputs[[`${namePascalCase}AmplifyDefaultDomain`]] = {
"Value": {
"Fn::Sub": `\${${namePascalCase}AmplifyBranch.BranchName}.\${${namePascalCase}AmplifyApp.DefaultDomain}`
}
}
}

startFirstJob() {
// this.amplifySdk.startJob
// See https://github.com/schwamster/serverless-certificate-creator/blob/051a6e93a19b197a1a144eccbf401c85ef88f103/index.js for more examples
}
}

module.exports = ServerlessAmplifyPlugin
Loading

0 comments on commit 4963dd4

Please sign in to comment.