@jcoreio/toolchain-aws-lambda 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/pack-lambda +5 -0
- package/bin/resolveBin.cjs +8 -0
- package/package.json +33 -0
- package/plugins/getConfigFiles.cjs +56 -0
- package/plugins/scripts.cjs +19 -0
- package/plugins/semanticReleasePlugins.cjs +8 -0
- package/scripts/deploy.cjs +191 -0
package/bin/pack-lambda
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jcoreio/toolchain-aws-lambda",
|
|
3
|
+
"version": "4.3.0",
|
|
4
|
+
"description": "AWS Lambda function build toolchain",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/jcoreio/toolchains.git",
|
|
8
|
+
"directory": "packages/aws-lambda"
|
|
9
|
+
},
|
|
10
|
+
"author": "Andy Edwards",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jcoreio/toolchains/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/jcoreio/toolchains/tree/main/packages/aws-lambda",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@aws-sdk/client-s3": "^3.575.0",
|
|
18
|
+
"@aws-sdk/client-sts": "^3.575.0",
|
|
19
|
+
"@jcoreio/cloudformation-tools": "^5.1.1",
|
|
20
|
+
"@jcoreio/pack-lambda": "^2.0.0",
|
|
21
|
+
"@semantic-release/exec": "^6.0.3",
|
|
22
|
+
"dedent-js": "^1.0.1",
|
|
23
|
+
"dotenv": "^16.4.5",
|
|
24
|
+
"resolve-bin": "^1.0.0",
|
|
25
|
+
"zod": "^3.21.4",
|
|
26
|
+
"@jcoreio/toolchain": "4.3.0"
|
|
27
|
+
},
|
|
28
|
+
"toolchainManaged": {
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/aws-lambda": "^8.10.137"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const dedent = require('dedent-js')
|
|
2
|
+
const { toolchainPackages } = require('../util/findUps.cjs')
|
|
3
|
+
|
|
4
|
+
module.exports = [
|
|
5
|
+
async function getConfigFiles() {
|
|
6
|
+
const isTS = toolchainPackages.includes('@jcoreio/toolchain-typescript')
|
|
7
|
+
const files = {
|
|
8
|
+
[`src/index.${isTS ? 'ts' : 'js'}`]: dedent`
|
|
9
|
+
// eslint-disable-next-line @jcoreio/implicit-dependencies/no-implicit
|
|
10
|
+
import type { Handler } from 'aws-lambda'
|
|
11
|
+
|
|
12
|
+
export const handler: Handler = async (event, context) => {
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
`,
|
|
16
|
+
[`scripts/cloudFormationTemplate.${isTS ? 'ts' : 'js'}`]: dedent`
|
|
17
|
+
// export const StackName = 'StackName'
|
|
18
|
+
|
|
19
|
+
export const Template = {
|
|
20
|
+
AWSTemplateFormatVersion: '2010-09-09',
|
|
21
|
+
// Description: 'Template Descrption',
|
|
22
|
+
Transform: 'AWS::Serverless-2016-10-31',
|
|
23
|
+
Parameters: {
|
|
24
|
+
|
|
25
|
+
},
|
|
26
|
+
Resources: {
|
|
27
|
+
LambdaFunction: {
|
|
28
|
+
Type: 'AWS::Serverless::Function',
|
|
29
|
+
Properties: {
|
|
30
|
+
MemorySize: 128,
|
|
31
|
+
Timeout: 60,
|
|
32
|
+
// CodeUri: {
|
|
33
|
+
// Bucket: 'BucketName',
|
|
34
|
+
// },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
Outputs: {
|
|
39
|
+
LambdaFunction: { Value: { Ref: 'LambdaFunction' } },
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Parameters = {
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Capabilities = ['CAPABILITY_IAM']
|
|
48
|
+
|
|
49
|
+
export const Tags = {
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
`,
|
|
53
|
+
}
|
|
54
|
+
return files
|
|
55
|
+
},
|
|
56
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const execa = require('@jcoreio/toolchain/util/execa.cjs')
|
|
2
|
+
const fs = require('@jcoreio/toolchain/util/projectFs.cjs')
|
|
3
|
+
const deploy = require('../scripts/deploy.cjs')
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
{
|
|
7
|
+
predeploy: {
|
|
8
|
+
description: 'prepare to deploy lambda function',
|
|
9
|
+
run: async (args = []) => {
|
|
10
|
+
await execa('tc', ['build'])
|
|
11
|
+
await fs.ensureSymlink('node_modules', 'dist/node_modules', 'dir')
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
deploy: {
|
|
15
|
+
description: 'deploy lambda function to AWS',
|
|
16
|
+
run: deploy,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
const {
|
|
2
|
+
projectDir,
|
|
3
|
+
toolchainPackages,
|
|
4
|
+
} = require('@jcoreio/toolchain/util/findUps.cjs')
|
|
5
|
+
const fs = require('@jcoreio/toolchain/util/projectFs.cjs')
|
|
6
|
+
const { uploadToS3 } = require('@jcoreio/pack-lambda')
|
|
7
|
+
const path = require('path')
|
|
8
|
+
const { inspect } = require('util')
|
|
9
|
+
const { S3Client, CreateBucketCommand } = require('@aws-sdk/client-s3')
|
|
10
|
+
const { STSClient, GetCallerIdentityCommand } = require('@aws-sdk/client-sts')
|
|
11
|
+
const { deployCloudFormationStack } = require('@jcoreio/cloudformation-tools')
|
|
12
|
+
const z = require('zod').default
|
|
13
|
+
|
|
14
|
+
const TemplateModuleSchema = z.strictObject({
|
|
15
|
+
StackName: z.string().optional(),
|
|
16
|
+
Parameters: z.record(z.any()).optional(),
|
|
17
|
+
Template: z
|
|
18
|
+
.object({
|
|
19
|
+
Parameters: z
|
|
20
|
+
.record(
|
|
21
|
+
z
|
|
22
|
+
.object({
|
|
23
|
+
Type: z.string(),
|
|
24
|
+
Description: z.string().optional(),
|
|
25
|
+
Default: z.any().optional(),
|
|
26
|
+
})
|
|
27
|
+
.passthrough()
|
|
28
|
+
)
|
|
29
|
+
.optional(),
|
|
30
|
+
Resources: z.record(z.object({ Type: z.string() }).passthrough()),
|
|
31
|
+
})
|
|
32
|
+
.passthrough(),
|
|
33
|
+
Capabilities: z
|
|
34
|
+
.array(
|
|
35
|
+
z.enum([
|
|
36
|
+
'CAPABILITY_IAM',
|
|
37
|
+
'CAPABILITY_NAMED_IAM',
|
|
38
|
+
'CAPABILITY_AUTO_EXPAND',
|
|
39
|
+
])
|
|
40
|
+
)
|
|
41
|
+
.optional(),
|
|
42
|
+
Tags: z
|
|
43
|
+
.union([
|
|
44
|
+
z.record(z.string()),
|
|
45
|
+
z.array(z.object({ Tag: z.string(), Value: z.string() })),
|
|
46
|
+
])
|
|
47
|
+
.optional(0),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
module.exports = async function deploy() {
|
|
51
|
+
const packageJsonFile = path.join(projectDir, 'dist', 'package.json')
|
|
52
|
+
const packageJson = await fs.readJson(packageJsonFile)
|
|
53
|
+
require('dotenv').config()
|
|
54
|
+
|
|
55
|
+
if (toolchainPackages.includes('@jcoreio/toolchain-esnext')) {
|
|
56
|
+
// eslint-disable-next-line @jcoreio/implicit-dependencies/no-implicit
|
|
57
|
+
require(require.resolve(
|
|
58
|
+
'@jcoreio/toolchain-esnext/util/babelRegister.cjs',
|
|
59
|
+
{ paths: [projectDir] }
|
|
60
|
+
))
|
|
61
|
+
}
|
|
62
|
+
const templatePath = path.join(
|
|
63
|
+
projectDir,
|
|
64
|
+
'scripts',
|
|
65
|
+
'cloudFormationTemplate'
|
|
66
|
+
)
|
|
67
|
+
const templateModule = TemplateModuleSchema.parse(require(templatePath))
|
|
68
|
+
const { Template, Parameters, Capabilities, Tags } = templateModule
|
|
69
|
+
let { StackName } = templateModule
|
|
70
|
+
|
|
71
|
+
if (!Template.Description) Template.Description = packageJson.Description
|
|
72
|
+
|
|
73
|
+
const [lambda, ...excessLambdas] = Object.entries(
|
|
74
|
+
Template.Resources || {}
|
|
75
|
+
).filter(
|
|
76
|
+
([key, value]) =>
|
|
77
|
+
value.Type === 'AWS::Serverless::Function' ||
|
|
78
|
+
value.Type === 'AWS::Lambda::Function'
|
|
79
|
+
)
|
|
80
|
+
if (!lambda) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Missing lambda function in Template export in ${path.relative(
|
|
83
|
+
process.cwd(),
|
|
84
|
+
templatePath
|
|
85
|
+
)}`
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
if (excessLambdas.length) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`${path.relative(
|
|
91
|
+
process.cwd(),
|
|
92
|
+
templatePath
|
|
93
|
+
)} contains more than one lambda function, this is not supported`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const [lambdaResourceName, lambdaResource] = lambda
|
|
98
|
+
|
|
99
|
+
const { Properties } = lambdaResource
|
|
100
|
+
if (!Properties) {
|
|
101
|
+
throw new Error(`Missing Properties on ${lambdaResourceName} resource`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!Properties.Handler) {
|
|
105
|
+
if (!packageJson.main) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`missing "Handler" in lambda function properties or "main" in ${path.relative(
|
|
108
|
+
process.cwd(),
|
|
109
|
+
packageJsonFile
|
|
110
|
+
)}`
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
Properties.Handler = packageJson.main.replace(/\.[^.]+$/, '.handler')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!Properties.CodeUri) Properties.CodeUri = {}
|
|
117
|
+
const { CodeUri } = Properties
|
|
118
|
+
|
|
119
|
+
if (!CodeUri.Bucket) {
|
|
120
|
+
const sts = new STSClient()
|
|
121
|
+
const { Account } = await sts.send(new GetCallerIdentityCommand({}))
|
|
122
|
+
// eslint-disable-next-line no-console
|
|
123
|
+
console.error(`Defaulting Lambda CodeUri.Bucket to AWS Account: ${Account}`)
|
|
124
|
+
CodeUri.Bucket = Account
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let Bucket = CodeUri.Bucket
|
|
128
|
+
if (CodeUri.Bucket instanceof Object) {
|
|
129
|
+
if (CodeUri.Bucket.Ref) {
|
|
130
|
+
Bucket =
|
|
131
|
+
Parameters[CodeUri.Bucket.Ref] ||
|
|
132
|
+
(Template.Parameters[CodeUri.Bucket.Ref] || {}).Default
|
|
133
|
+
}
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Lambda CodeUri.Bucket format not supported: ${inspect(Bucket)}`
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
let Key = CodeUri.Key
|
|
139
|
+
|
|
140
|
+
const s3 = new S3Client()
|
|
141
|
+
try {
|
|
142
|
+
await s3.send(new CreateBucketCommand({ Bucket }))
|
|
143
|
+
// eslint-disable-next-line no-console
|
|
144
|
+
console.error(`Created S3 Bucket: ${Bucket}`)
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if (!error.stack.includes('BucketAlreadyOwnedByYou')) {
|
|
147
|
+
// eslint-disable-next-line no-console
|
|
148
|
+
console.error(error)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
;({ Key } = await uploadToS3({
|
|
153
|
+
packageDir: path.resolve(projectDir, 'dist'),
|
|
154
|
+
Bucket,
|
|
155
|
+
Key,
|
|
156
|
+
}))
|
|
157
|
+
CodeUri.Key = Key
|
|
158
|
+
|
|
159
|
+
if (!Properties.Runtime) {
|
|
160
|
+
Properties.Runtime = `nodejs20.x`
|
|
161
|
+
// eslint-disable-next-line no-console
|
|
162
|
+
console.error(`Defaulting Lambda Runtime to ${Properties.Runtime}`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!Properties.FunctionName) {
|
|
166
|
+
Properties.FunctionName = packageJson.name
|
|
167
|
+
.replace(/[^-a-z0-9]+/g, '-')
|
|
168
|
+
.replace(/^-+|-+$/g, '')
|
|
169
|
+
// eslint-disable-next-line no-console
|
|
170
|
+
console.error(
|
|
171
|
+
`Defaulting Lambda FunctionName to ${Properties.FunctionName}`
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!StackName) {
|
|
176
|
+
StackName = Properties.FunctionName.replace(/[^-a-z0-9]+/g, '-').replace(
|
|
177
|
+
/^-+|-+$/g,
|
|
178
|
+
''
|
|
179
|
+
)
|
|
180
|
+
// eslint-disable-next-line no-console
|
|
181
|
+
console.error(`Defaulting StackName to ${StackName}`)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await deployCloudFormationStack({
|
|
185
|
+
StackName,
|
|
186
|
+
Template,
|
|
187
|
+
Parameters,
|
|
188
|
+
Capabilities,
|
|
189
|
+
Tags,
|
|
190
|
+
})
|
|
191
|
+
}
|