@@ -2,10 +2,12 @@ const {existsSync} = require('fs')
2
2
const { appendFile, readFile, writeFile} = require ( 'fs' ) . promises
3
3
const { join} = require ( 'path' )
4
4
const { homedir} = require ( 'os' )
5
+ const { buildAndDeployNetlifyFunctions, rewriteRedirects} = require ( './nfn' )
5
6
6
7
const nimConfig = join ( homedir ( ) , '.nimbella' )
7
- let isProject = false
8
- let deployWeb = false
8
+ let isProject = false // True if deploying a Nimbella project
9
+ let deployWeb = false // True if deploying a Nimbella project with a web folder and proxying the domain
10
+ let hasFunctions = false // True if deploying Netlify functions
9
11
10
12
// Disable auto updates of nim.
11
13
process . env . NIM_DISABLE_AUTOUPDATE = '1'
@@ -39,44 +41,102 @@ async function deployProject(run, includeWeb) {
39
41
// matching rule to the _redirects file. The target is the Nimbella namespace/:splat.
40
42
//
41
43
// If deploying the web assets as well, then proxy the entire domain instead.
42
- async function processRedirects ( run , inputs ) {
44
+ async function addRedirect ( inputs , { namespace , apihost } ) {
43
45
const redirectRules = [ ]
44
- const { stdout : namespace } = await run . command ( `nim auth current` )
45
- const apihost = await getApiHost ( run )
46
46
47
47
if ( deployWeb ) {
48
48
redirectRules . push ( `/* https://${ namespace } -${ apihost } /:splat 200!` )
49
- } else {
50
- let { path : redirectPath } = inputs
51
- redirectPath = redirectPath . endsWith ( '/' )
52
- ? redirectPath
53
- : redirectPath + '/'
54
- if ( redirectPath ) {
55
- redirectRules . push (
56
- `${ redirectPath } * https://${ apihost } /api/v1/web/${ namespace } /:splat 200!`
57
- )
58
- }
49
+ } else if ( inputs . path ) {
50
+ const redirectPath = inputs . path . endsWith ( '/' )
51
+ ? inputs . path
52
+ : inputs . path + '/'
53
+ const pkg = isProject ? '' : 'default/'
54
+ redirectRules . push (
55
+ `${ redirectPath } * https://${ apihost } /api/v1/web/${ namespace } /${ pkg } :splat 200!`
56
+ )
59
57
}
60
58
61
59
return redirectRules
62
60
}
63
61
62
+ /**
63
+ * Checks inputs for validity.
64
+ * Issues warning if deprecated input properties are used.
65
+ * @param {object } inputs
66
+ * @returns true iff input parameters are valid
67
+ */
68
+ function checkInputsAndNotifyOfDeprecation ( inputs ) {
69
+ const warn = ( prop ) => {
70
+ const chalk = require ( 'chalk' )
71
+ console . warn (
72
+ chalk . yellow ( `${ prop } is deprecated.` ) ,
73
+ 'Migrate to Nimbella project.yml.'
74
+ )
75
+ }
76
+
77
+ const error = ( prop , units ) => {
78
+ const chalk = require ( 'chalk' )
79
+ console . warn ( chalk . yellow ( `${ prop } must be a number in ${ units } .` ) )
80
+ }
81
+
82
+ let valid = true
83
+ if ( inputs ) {
84
+ if ( inputs . functions ) warn ( '[inputs.functions]' )
85
+ if ( inputs . timeout ) {
86
+ warn ( '[inputs.timeout]' )
87
+ if ( Number . isNaN ( Number ( inputs . timeout ) ) ) {
88
+ error ( '[inputs.timeout]' , 'milliseconds in [100-10000])' )
89
+ valid = false
90
+ }
91
+ }
92
+
93
+ if ( inputs . memory ) {
94
+ warn ( '[inputs.memory]' )
95
+ if ( Number . isNaN ( Number ( inputs . memory ) ) ) {
96
+ error ( '[inputs.memory]' , 'megabytes in [128-512])' )
97
+ valid = false
98
+ }
99
+ }
100
+ }
101
+
102
+ return valid
103
+ }
104
+
105
+ async function constructDotEnvFile ( inputs ) {
106
+ if ( inputs . envs && inputs . envs . length > 0 ) {
107
+ console . log ( 'Forwarding environment variables:' )
108
+ let envVars = ''
109
+ inputs . envs . forEach ( ( env ) => {
110
+ console . log ( `\t- ${ env } ` )
111
+ envVars += `\n${ env } = ${ process . env [ env ] } `
112
+ } )
113
+ await appendFile ( '.env' , envVars )
114
+ }
115
+ }
116
+
64
117
module . exports = {
65
118
// Execute before build starts.
66
119
onPreBuild : async ( { utils, inputs} ) => {
120
+ const valid = checkInputsAndNotifyOfDeprecation ( inputs )
121
+ if ( ! valid ) {
122
+ utils . build . failBuild ( 'Invalid input parameters.' )
123
+ }
124
+
67
125
if (
68
126
! process . env . NIMBELLA_LOGIN_TOKEN &&
69
127
! ( await utils . cache . has ( nimConfig ) )
70
128
) {
71
129
utils . build . failBuild (
72
- 'Nimbella login token is not available. Please run `netlify addons:create nimbella` at the base of your local project directory linked to your Netlify site.'
130
+ [
131
+ 'Nimbella login token is not available.' ,
132
+ `Add NIMBELLA_LOGIN_TOKEN to your build environment.',
133
+ 'You may also run 'netlify addons:create nimbella' in your project directory linked to this Netlify site.`
134
+ ] . join ( '\n' )
73
135
)
74
136
}
75
137
76
138
await utils . cache . restore ( nimConfig )
77
-
78
139
const loggedIn = existsSync ( nimConfig )
79
- // Login if not logged in before.
80
140
if ( loggedIn ) {
81
141
try {
82
142
const { stdout} = await utils . run . command ( 'nim auth current' , {
@@ -122,28 +182,28 @@ module.exports = {
122
182
}
123
183
124
184
isProject = existsSync ( 'packages' ) || ( deployWeb && existsSync ( 'web' ) )
185
+ hasFunctions = inputs . functions && existsSync ( inputs . functions )
186
+
187
+ if ( hasFunctions && isProject ) {
188
+ utils . build . failBuild (
189
+ 'Detected both a Nimbella project and a functions directory. Use one or the other.'
190
+ )
191
+ }
125
192
} ,
126
193
// Build and deploy the Nimbella project
127
194
onBuild : async ( { utils, inputs} ) => {
128
195
if ( process . env . CONTEXT === 'production' ) {
129
196
if ( isProject ) {
130
- if ( inputs . env && inputs . env . length > 0 ) {
131
- console . log ( 'Forwarding environment variables:' )
132
- let envVars = ''
133
- inputs . env . forEach ( ( env ) => {
134
- console . log ( `\t- ${ env } ` )
135
- envVars += `\n${ env } = ${ process . env [ env ] } `
136
- } )
137
- await appendFile ( '.env' , envVars )
138
- }
139
-
140
197
try {
198
+ await constructDotEnvFile ( inputs )
141
199
await deployProject ( utils . run , deployWeb )
142
200
} catch ( error ) {
143
201
utils . build . failBuild ( 'Failed to build and deploy the project' , {
144
202
error
145
203
} )
146
204
}
205
+ } else if ( hasFunctions ) {
206
+ return buildAndDeployNetlifyFunctions ( { utils, inputs} )
147
207
} else {
148
208
console . log (
149
209
`Skipping the build and deployment: Nimbella project not detected.`
@@ -157,11 +217,17 @@ module.exports = {
157
217
} ,
158
218
// Execute after build is done.
159
219
onPostBuild : async ( { constants, utils, inputs} ) => {
160
- if ( process . env . CONTEXT === 'production' && isProject ) {
220
+ if ( process . env . CONTEXT === 'production' && ( isProject || hasFunctions ) ) {
161
221
const redirectsFile = join ( constants . PUBLISH_DIR , '_redirects' )
162
- const redirectRules = await processRedirects ( utils . run , inputs )
163
-
164
- if ( redirectRules . length > 0 ) {
222
+ const { stdout : namespace } = await utils . run . command ( `nim auth current` )
223
+ const apihost = await getApiHost ( utils . run )
224
+ const creds = { namespace, apihost}
225
+ const fnRewrites = hasFunctions
226
+ ? await rewriteRedirects ( constants , creds )
227
+ : [ ]
228
+ const redirectRules = await addRedirect ( inputs , creds )
229
+
230
+ if ( fnRewrites . length > 0 || redirectRules . length > 0 ) {
165
231
let content = ''
166
232
if ( existsSync ( redirectsFile ) ) {
167
233
content = await readFile ( redirectsFile )
@@ -171,8 +237,12 @@ module.exports = {
171
237
}
172
238
173
239
// The rewrites take precedence
174
- await writeFile ( redirectsFile , redirectRules . join ( '\n' ) + '\n' )
240
+ fnRewrites . push ( ...redirectRules )
241
+ await writeFile ( redirectsFile , fnRewrites . join ( '\n' ) + '\n' )
175
242
await appendFile ( redirectsFile , content )
243
+
244
+ const rf = await readFile ( redirectsFile )
245
+ console . log ( `Redirects:\n${ rf } ` )
176
246
}
177
247
}
178
248
}
0 commit comments