1
1
import { Command } from 'commander'
2
- import { mkdirSync , rmSync , symlinkSync , writeFileSync } from 'fs'
3
- import { tmpdir } from 'os'
4
2
import path from 'path'
5
- import { simpleGit } from 'simple-git'
6
3
import packageJson from '../package.json'
7
- import { skipCiFlag } from './consts'
8
- import { bumpCalculator , bumpMapping , BumpType , findInFile , isValidTag , replaceVersionInCommonFiles , zipFolder , zipMultipleFoldersOrFiles } from './utils'
4
+ import { deployHandler } from './cli/deploy'
5
+ import { guessHandler } from './cli/guess'
6
+ import { packHandler } from './cli/pack'
7
+ import { shipitHandler } from './cli/shipit'
8
+ import { wrapProcess } from './utils'
9
9
10
10
const commandCwd = process . cwd ( )
11
- const nextServerConfigRegex = / (?< = c o n f : ) ( .* ) (? = , ) /
12
- const scriptDir = path . dirname ( __filename )
13
-
14
11
const program = new Command ( )
15
12
16
13
program
@@ -35,7 +32,7 @@ program
35
32
. option (
36
33
'--handlerPath' ,
37
34
'Path to custom handler to be used to handle ApiGw events. By default this is provided for you.' ,
38
- path . resolve ( scriptDir , './server-handler.js' ) ,
35
+ path . resolve ( path . dirname ( __filename ) , './server-handler.js' ) ,
39
36
)
40
37
. option (
41
38
'--outputFolder' ,
@@ -44,103 +41,8 @@ program
44
41
)
45
42
. action ( async ( options ) => {
46
43
const { standaloneFolder, publicFolder, handlerPath, outputFolder } = options
47
-
48
- // @TODO : Validate that output folder exists.
49
- // @TODO : Validate server.js exists and we can match data.
50
- // @TODO : Validate that public folder is using `assets` subfolder.
51
-
52
- // Dependencies layer configuration
53
- const nodeModulesFolderPath = path . resolve ( standaloneFolder , './node_modules' )
54
- const depsLambdaFolder = 'nodejs/node_modules'
55
- const lambdaNodeModulesPath = path . resolve ( '/opt' , depsLambdaFolder )
56
- const dependenciesOutputPath = path . resolve ( outputFolder , 'dependenciesLayer.zip' )
57
-
58
- // Code layer configuration
59
- const generatedNextServerPath = path . resolve ( standaloneFolder , './server.js' )
60
- const codeOutputPath = path . resolve ( outputFolder , 'code.zip' )
61
-
62
- // Assets bundle configuration
63
- const buildIdPath = path . resolve ( commandCwd , './.next/BUILD_ID' )
64
- const generatedStaticContentPath = path . resolve ( commandCwd , '.next/static' )
65
- const generatedStaticRemapping = '_next/static'
66
- const assetsOutputPath = path . resolve ( outputFolder , 'assetsLayer.zip' )
67
-
68
- // Clean output directory before continuing
69
- rmSync ( outputFolder , { force : true , recursive : true } )
70
- mkdirSync ( outputFolder )
71
-
72
- // Zip dependencies from standalone output in a layer-compatible format.
73
- await zipFolder ( {
74
- outputName : dependenciesOutputPath ,
75
- folderPath : nodeModulesFolderPath ,
76
- dir : depsLambdaFolder ,
77
- } )
78
-
79
- // Zip staticly generated assets and public folder.
80
- await zipMultipleFoldersOrFiles ( {
81
- outputName : assetsOutputPath ,
82
- inputDefinition : [
83
- {
84
- isFile : true ,
85
- name : 'BUILD_ID' ,
86
- path : buildIdPath ,
87
- } ,
88
- {
89
- path : publicFolder ,
90
- } ,
91
- {
92
- path : generatedStaticContentPath ,
93
- dir : generatedStaticRemapping ,
94
- } ,
95
- ] ,
96
- } )
97
-
98
- // Create a symlink for node_modules so they point to the separately packaged layer.
99
- // We need to create symlink because we are not using NodejsFunction in CDK as bundling is custom.
100
- const tmpFolder = tmpdir ( )
101
-
102
- const symlinkPath = path . resolve ( tmpFolder , `./node_modules_${ Math . random ( ) } ` )
103
- symlinkSync ( lambdaNodeModulesPath , symlinkPath )
104
-
105
- const nextConfig = findInFile ( generatedNextServerPath , nextServerConfigRegex )
106
- const configPath = path . resolve ( tmpFolder , `./config.json_${ Math . random ( ) } ` )
107
- writeFileSync ( configPath , nextConfig , 'utf-8' )
108
-
109
- // Zip codebase including symlinked node_modules and handler.
110
- await zipMultipleFoldersOrFiles ( {
111
- outputName : codeOutputPath ,
112
- inputDefinition : [
113
- {
114
- isGlob : true ,
115
- cwd : standaloneFolder ,
116
- path : '**/*' ,
117
- ignore : [ '**/node_modules/**' , '*.zip' ] ,
118
- } ,
119
- {
120
- // Ensure hidden files are included.
121
- isGlob : true ,
122
- cwd : standaloneFolder ,
123
- path : '.*/**/*' ,
124
- } ,
125
- {
126
- isFile : true ,
127
- path : handlerPath ,
128
- name : 'handler.js' ,
129
- } ,
130
- {
131
- isFile : true ,
132
- path : symlinkPath ,
133
- name : 'node_modules' ,
134
- } ,
135
- {
136
- isFile : true ,
137
- path : configPath ,
138
- name : 'config.json' ,
139
- } ,
140
- ] ,
141
- } )
142
-
143
- console . log ( 'Your NextJS project was succefully prepared for Lambda.' )
44
+ console . log ( 'Our config is: ' , options )
45
+ wrapProcess ( packHandler ( { commandCwd, handlerPath, outputFolder, publicFolder, standaloneFolder } ) )
144
46
} )
145
47
146
48
program
@@ -151,20 +53,8 @@ program
151
53
. option ( '-t, --tagPrefix <prefix>' , 'Prefix version with string of your choice' , 'v' )
152
54
. action ( async ( commitMessage , latestVersion , options ) => {
153
55
const { tagPrefix } = options
154
-
155
- if ( ! isValidTag ( latestVersion , tagPrefix ) ) {
156
- throw new Error ( `Invalid version found - ${ latestVersion } !` )
157
- }
158
-
159
- const match = bumpMapping . find ( ( { test } ) => commitMessage . match ( test ) )
160
- if ( ! match ) {
161
- throw new Error ( 'No mapping for for suplied commit message.' )
162
- }
163
-
164
- const nextTag = bumpCalculator ( latestVersion . replace ( tagPrefix , '' ) , match ?. bump )
165
- const nextTagWithPrefix = tagPrefix + nextTag
166
-
167
- console . log ( nextTagWithPrefix )
56
+ console . log ( 'Our config is: ' , options )
57
+ wrapProcess ( guessHandler ( { commitMessage, latestVersion, tagPrefix } ) )
168
58
} )
169
59
170
60
program
@@ -179,102 +69,20 @@ program
179
69
. option ( '--gitEmail <email>' , 'User email to be used for commits.' , '[email protected] ' )
180
70
. action ( async ( options ) => {
181
71
const { tagPrefix, failOnMissingCommit, releaseBranchPrefix, forceBump, gitUser, gitEmail } = options
182
-
183
72
console . log ( 'Our config is: ' , options )
184
-
185
- const git = simpleGit ( )
186
-
187
- git . addConfig ( 'user.name' , gitUser )
188
- git . addConfig ( 'user.email' , gitEmail )
189
-
190
- const tags = await git . tags ( )
191
- const log = await git . log ( )
192
- const branch = await git . branch ( )
193
- const [ remote ] = await git . getRemotes ( )
194
-
195
- const latestCommit = log . latest ?. hash
196
- const latestTag = tags . latest ?? '0.0.0'
197
- const currentTag = latestTag . replace ( tagPrefix , '' )
198
-
199
- console . log ( 'Current version: ' , latestTag )
200
-
201
- if ( ! isValidTag ( latestTag , tagPrefix ) ) {
202
- throw new Error ( `Invalid tag found - ${ latestTag } !` )
203
- }
204
-
205
- if ( ! latestCommit ) {
206
- throw new Error ( 'Latest commit was not found!' )
207
- }
208
-
209
- const commits = await git . log ( {
210
- from : tags . latest ,
211
- to : latestCommit ,
212
- } )
213
-
214
- if ( commits . total < 1 && failOnMissingCommit ) {
215
- throw new Error ( 'No new commits since last tag.' )
216
- }
217
-
218
- const bumps : BumpType [ ] = [ ]
219
-
220
- commits . all . forEach ( ( { message, body } ) => {
221
- const match = bumpMapping . find ( ( { test, scanBody } ) => ( scanBody ? body : message ) . match ( test ) )
222
- if ( ! match ) {
223
- console . warn ( `Invalid commit, cannot match bump - ${ message } !` )
224
- } else {
225
- bumps . push ( match ?. bump )
226
- }
227
- } )
228
-
229
- console . log ( 'Bumps: ' , bumps )
230
-
231
- // Bump minor in case nothing is found.
232
- if ( bumps . length < 1 && forceBump ) {
233
- console . log ( 'Forcing patch bump!' )
234
- bumps . push ( BumpType . Patch )
235
- }
236
-
237
- const nextTag = bumps . reduce ( ( acc , curr ) => bumpCalculator ( acc , curr ) , currentTag )
238
- const nextTagWithPrefix = tagPrefix + nextTag
239
- const releaseBranch = `${ releaseBranchPrefix } ${ nextTagWithPrefix } `
240
- console . log ( `Next version is - ${ nextTagWithPrefix } !` )
241
-
242
- if ( currentTag === nextTag ) {
243
- throw new Error ( 'Failed to bump version!' )
244
- }
245
-
246
- const replacementResults = replaceVersionInCommonFiles ( currentTag , nextTag )
247
- console . log ( `Replaced version in files.` , replacementResults )
248
-
249
- // Commit changed files (versions) and create a release commit with skip ci flag.
250
- await git
251
- //
252
- . add ( './*' )
253
- . raw ( 'commit' , '--message' , `Release: ${ nextTagWithPrefix } ${ skipCiFlag } ` )
254
- // Create tag and push it to master.
255
- . addTag ( nextTagWithPrefix )
256
-
257
- git . push ( remote . name , branch . current )
258
- git . pushTags ( )
259
-
260
- // As current branch commit includes skip ci flag, we want to ommit this flag for release branch so pipeline can run (omitting infinite loop).
261
- // So we are overwriting last commit message and pushing to release branch.
262
- await git
263
- //
264
- . raw ( 'commit' , '--message' , `Release: ${ nextTagWithPrefix } ` , '--amend' )
265
- . push ( remote . name , `${ branch . current } :${ releaseBranch } ` )
266
-
267
- // @Note : CI/CD should not be listening for tags in master, it should listen to release branch.
268
- // @TODO : Include commits and commit bodies in release commit so Jira can pick it up.
269
-
270
- console . log ( `Successfuly tagged and created new branch - ${ releaseBranch } ` )
73
+ wrapProcess ( shipitHandler ( { tagPrefix, gitEmail, gitUser, failOnMissingCommit, forceBump, releaseBranchPrefix } ) )
271
74
} )
272
75
273
76
program
274
77
. command ( 'deploy' )
275
78
. description ( 'Deploy Next application via CDK' )
79
+ . option ( '--stackName <name>' , 'Name of the stack to be deployed.' , 'StandaloneNextjsStack-Temporary' )
80
+ . option ( '--tsconfigPath <path>' , 'Absolute path to config.' , path . resolve ( __dirname , '../tsconfig.json' ) )
81
+ . option ( '--appPath <path>' , 'Absolute path to app.' , path . resolve ( __dirname , '../cdk/app.ts' ) )
276
82
. action ( async ( options ) => {
277
- // @TODO : Add support for CLI CDK deployments via provided CDK example.
83
+ const { stackName, appPath, tsconfigPath } = options
84
+ console . log ( 'Our config is: ' , options )
85
+ wrapProcess ( deployHandler ( { stackName, appPath, tsconfigPath } ) )
278
86
} )
279
87
280
88
program . parse ( process . argv )
0 commit comments