@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.
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
4
+
5
+ exec $(node "$basedir/resolveBin.cjs" pack-lambda) "$@"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.stdout.write(
4
+ require('resolve-bin').sync(
5
+ process.argv[2],
6
+ process.argv[3] ? { executable: process.argv[3] } : {}
7
+ )
8
+ )
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,8 @@
1
+ module.exports = [
2
+ () => [
3
+ require.resolve('@semantic-release/exec'),
4
+ {
5
+ publishCmd: 'npm version ${nextRelease.version} && tc deploy',
6
+ },
7
+ ],
8
+ ]
@@ -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
+ }