netlify-cli 17.2.2 → 17.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/npm-shrinkwrap.json +3 -2
- package/package.json +2 -1
- package/src/commands/build/build.mjs +1 -0
- package/src/commands/deploy/deploy.mjs +146 -84
- package/src/commands/integration/deploy.mjs +1 -0
- package/src/lib/build.mjs +53 -21
- package/src/lib/functions/server.mjs +0 -4
- package/src/utils/deploy/deploy-site.mjs +35 -26
- package/src/utils/deploy/hash-config.mjs +26 -0
- package/src/utils/deploy/upload-files.mjs +3 -2
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
|
-
"version": "17.
|
|
3
|
+
"version": "17.3.0",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "netlify-cli",
|
|
9
|
-
"version": "17.
|
|
9
|
+
"version": "17.3.0",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"through2-map": "3.0.0",
|
|
113
113
|
"to-readable-stream": "3.0.0",
|
|
114
114
|
"toml": "3.0.0",
|
|
115
|
+
"tomlify-j0.4": "^3.0.0",
|
|
115
116
|
"ulid": "2.3.0",
|
|
116
117
|
"unixify": "1.0.0",
|
|
117
118
|
"update-notifier": "6.0.2",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "17.
|
|
4
|
+
"version": "17.3.0",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -144,6 +144,7 @@
|
|
|
144
144
|
"through2-map": "3.0.0",
|
|
145
145
|
"to-readable-stream": "3.0.0",
|
|
146
146
|
"toml": "3.0.0",
|
|
147
|
+
"tomlify-j0.4": "^3.0.0",
|
|
147
148
|
"ulid": "2.3.0",
|
|
148
149
|
"unixify": "1.0.0",
|
|
149
150
|
"update-notifier": "6.0.2",
|
|
@@ -4,11 +4,12 @@ import { basename, resolve } from 'path'
|
|
|
4
4
|
import { env } from 'process'
|
|
5
5
|
|
|
6
6
|
import { runCoreSteps } from '@netlify/build'
|
|
7
|
-
import { restoreConfig, updateConfig } from '@netlify/config'
|
|
8
7
|
import { Option } from 'commander'
|
|
9
8
|
import inquirer from 'inquirer'
|
|
10
9
|
import isEmpty from 'lodash/isEmpty.js'
|
|
11
10
|
import isObject from 'lodash/isObject.js'
|
|
11
|
+
import { parseAllHeaders } from 'netlify-headers-parser'
|
|
12
|
+
import { parseAllRedirects } from 'netlify-redirect-parser'
|
|
12
13
|
import prettyjson from 'prettyjson'
|
|
13
14
|
|
|
14
15
|
import { cancelDeploy } from '../../lib/api.mjs'
|
|
@@ -220,7 +221,10 @@ const getDeployFilesFilter = ({ deployFolder, site }) => {
|
|
|
220
221
|
(skipNodeModules && base === 'node_modules') ||
|
|
221
222
|
(base.startsWith('.') && base !== '.well-known') ||
|
|
222
223
|
base.startsWith('__MACOSX') ||
|
|
223
|
-
base.includes('/.')
|
|
224
|
+
base.includes('/.') ||
|
|
225
|
+
// headers and redirects are bundled in the config
|
|
226
|
+
base === '_redirects' ||
|
|
227
|
+
base === '_headers'
|
|
224
228
|
|
|
225
229
|
return !skipFile
|
|
226
230
|
}
|
|
@@ -322,7 +326,7 @@ const runDeploy = async ({
|
|
|
322
326
|
alias,
|
|
323
327
|
api,
|
|
324
328
|
command,
|
|
325
|
-
|
|
329
|
+
config,
|
|
326
330
|
deployFolder,
|
|
327
331
|
deployTimeout,
|
|
328
332
|
deployToProduction,
|
|
@@ -358,9 +362,29 @@ const runDeploy = async ({
|
|
|
358
362
|
const functionDirectories = [internalFunctionsFolder, functionsFolder].filter(Boolean)
|
|
359
363
|
const manifestPath = skipFunctionsCache ? null : await getFunctionsManifestPath({ base: site.root, packagePath })
|
|
360
364
|
|
|
365
|
+
const redirectsPath = `${deployFolder}/_redirects`
|
|
366
|
+
const headersPath = `${deployFolder}/_headers`
|
|
367
|
+
|
|
368
|
+
const { redirects } = await parseAllRedirects({
|
|
369
|
+
configRedirects: config.redirects,
|
|
370
|
+
redirectsFiles: [redirectsPath],
|
|
371
|
+
minimal: true,
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
config.redirects = redirects
|
|
375
|
+
|
|
376
|
+
const { headers } = await parseAllHeaders({
|
|
377
|
+
configHeaders: config.headers,
|
|
378
|
+
// @ts-ignore
|
|
379
|
+
headersFiles: [headersPath],
|
|
380
|
+
minimal: true,
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
config.headers = headers
|
|
384
|
+
|
|
361
385
|
// @ts-ignore
|
|
362
386
|
results = await deploySite(api, siteId, deployFolder, {
|
|
363
|
-
|
|
387
|
+
config,
|
|
364
388
|
fnDir: functionDirectories,
|
|
365
389
|
functionsConfig,
|
|
366
390
|
statusCb: silent ? () => {} : deployProgressCb(),
|
|
@@ -407,10 +431,12 @@ const runDeploy = async ({
|
|
|
407
431
|
* @param {object} config
|
|
408
432
|
* @param {*} config.cachedConfig
|
|
409
433
|
* @param {string} [config.packagePath]
|
|
434
|
+
* @param {*} config.deployHandler
|
|
435
|
+
* @param {string} config.currentDir
|
|
410
436
|
* @param {import('commander').OptionValues} config.options The options of the command
|
|
411
437
|
* @returns
|
|
412
438
|
*/
|
|
413
|
-
const handleBuild = async ({ cachedConfig, options, packagePath }) => {
|
|
439
|
+
const handleBuild = async ({ cachedConfig, currentDir, deployHandler, options, packagePath }) => {
|
|
414
440
|
if (!options.build) {
|
|
415
441
|
return {}
|
|
416
442
|
}
|
|
@@ -420,6 +446,8 @@ const handleBuild = async ({ cachedConfig, options, packagePath }) => {
|
|
|
420
446
|
packagePath,
|
|
421
447
|
token,
|
|
422
448
|
options,
|
|
449
|
+
currentDir,
|
|
450
|
+
deployHandler,
|
|
423
451
|
})
|
|
424
452
|
const { configMutations, exitCode, newConfig } = await runBuild(resolvedOptions)
|
|
425
453
|
if (exitCode !== 0) {
|
|
@@ -529,67 +557,18 @@ const printResults = ({ deployToProduction, isIntegrationDeploy, json, results,
|
|
|
529
557
|
}
|
|
530
558
|
}
|
|
531
559
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
560
|
+
const prepAndRunDeploy = async ({
|
|
561
|
+
api,
|
|
562
|
+
command,
|
|
563
|
+
config,
|
|
564
|
+
deployToProduction,
|
|
565
|
+
options,
|
|
566
|
+
site,
|
|
567
|
+
siteData,
|
|
568
|
+
siteId,
|
|
569
|
+
workingDir,
|
|
570
|
+
}) => {
|
|
540
571
|
const alias = options.alias || options.branch
|
|
541
|
-
|
|
542
|
-
command.setAnalyticsPayload({ open: options.open, prod: options.prod, json: options.json, alias: Boolean(alias) })
|
|
543
|
-
|
|
544
|
-
if (options.branch) {
|
|
545
|
-
warn('--branch flag has been renamed to --alias and will be removed in future versions')
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (options.context && !options.build) {
|
|
549
|
-
return error('--context flag is only available when using the --build flag')
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
await command.authenticate(options.auth)
|
|
553
|
-
|
|
554
|
-
let siteId = site.id || options.site
|
|
555
|
-
|
|
556
|
-
let siteData = {}
|
|
557
|
-
if (siteId && !isEmpty(siteInfo)) {
|
|
558
|
-
siteData = siteInfo
|
|
559
|
-
siteId = siteData.id
|
|
560
|
-
} else {
|
|
561
|
-
log("This folder isn't linked to a site yet")
|
|
562
|
-
const NEW_SITE = '+ Create & configure a new site'
|
|
563
|
-
const EXISTING_SITE = 'Link this directory to an existing site'
|
|
564
|
-
|
|
565
|
-
const initializeOpts = [EXISTING_SITE, NEW_SITE]
|
|
566
|
-
|
|
567
|
-
const { initChoice } = await inquirer.prompt([
|
|
568
|
-
{
|
|
569
|
-
type: 'list',
|
|
570
|
-
name: 'initChoice',
|
|
571
|
-
message: 'What would you like to do?',
|
|
572
|
-
choices: initializeOpts,
|
|
573
|
-
},
|
|
574
|
-
])
|
|
575
|
-
// create site or search for one
|
|
576
|
-
if (initChoice === NEW_SITE) {
|
|
577
|
-
siteData = await sitesCreate({}, command)
|
|
578
|
-
site.id = siteData.id
|
|
579
|
-
siteId = site.id
|
|
580
|
-
} else if (initChoice === EXISTING_SITE) {
|
|
581
|
-
siteData = await link({}, command)
|
|
582
|
-
site.id = siteData.id
|
|
583
|
-
siteId = site.id
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
const deployToProduction = options.prod || (options.prodIfUnlocked && !siteData.published_deploy.locked)
|
|
588
|
-
|
|
589
|
-
if (options.trigger) {
|
|
590
|
-
return triggerDeploy({ api, options, siteData, siteId })
|
|
591
|
-
}
|
|
592
|
-
|
|
593
572
|
const isUsingEnvelope = siteData && siteData.use_envelope
|
|
594
573
|
// if a context is passed besides dev, we need to pull env vars from that specific context
|
|
595
574
|
if (isUsingEnvelope && options.context && options.context !== 'dev') {
|
|
@@ -601,16 +580,10 @@ export const deploy = async (options, command) => {
|
|
|
601
580
|
})
|
|
602
581
|
}
|
|
603
582
|
|
|
604
|
-
const { configMutations = [], newConfig } = await handleBuild({
|
|
605
|
-
packagePath: command.workspacePackage,
|
|
606
|
-
cachedConfig: command.netlify.cachedConfig,
|
|
607
|
-
options,
|
|
608
|
-
})
|
|
609
|
-
const config = newConfig || command.netlify.config
|
|
610
|
-
|
|
611
583
|
const deployFolder = await getDeployFolder({ command, options, config, site, siteData })
|
|
612
584
|
const functionsFolder = getFunctionsFolder({ workingDir, options, config, site, siteData })
|
|
613
585
|
const { configPath } = site
|
|
586
|
+
|
|
614
587
|
const edgeFunctionsConfig = command.netlify.config.edge_functions
|
|
615
588
|
|
|
616
589
|
// build flag wasn't used and edge functions exist
|
|
@@ -648,20 +621,11 @@ export const deploy = async (options, command) => {
|
|
|
648
621
|
siteEnv,
|
|
649
622
|
})
|
|
650
623
|
|
|
651
|
-
const redirectsPath = `${deployFolder}/_redirects`
|
|
652
|
-
// @ts-ignore
|
|
653
|
-
await updateConfig(configMutations, {
|
|
654
|
-
buildDir: deployFolder,
|
|
655
|
-
configPath,
|
|
656
|
-
redirectsPath,
|
|
657
|
-
context: command.netlify.cachedConfig.context,
|
|
658
|
-
branch: command.netlify.cachedConfig.branch,
|
|
659
|
-
})
|
|
660
624
|
const results = await runDeploy({
|
|
661
625
|
alias,
|
|
662
626
|
api,
|
|
663
627
|
command,
|
|
664
|
-
|
|
628
|
+
config,
|
|
665
629
|
deployFolder,
|
|
666
630
|
deployTimeout: options.timeout * SEC_TO_MILLISEC || DEFAULT_DEPLOY_TIMEOUT,
|
|
667
631
|
deployToProduction,
|
|
@@ -677,9 +641,107 @@ export const deploy = async (options, command) => {
|
|
|
677
641
|
title: options.message,
|
|
678
642
|
})
|
|
679
643
|
|
|
680
|
-
|
|
681
|
-
|
|
644
|
+
return results
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* The deploy command
|
|
649
|
+
* @param {import('commander').OptionValues} options
|
|
650
|
+
* @param {import('../base-command.mjs').default} command
|
|
651
|
+
*/
|
|
652
|
+
export const deploy = async (options, command) => {
|
|
653
|
+
const { workingDir } = command
|
|
654
|
+
const { api, site, siteInfo } = command.netlify
|
|
655
|
+
const alias = options.alias || options.branch
|
|
656
|
+
|
|
657
|
+
command.setAnalyticsPayload({ open: options.open, prod: options.prod, json: options.json, alias: Boolean(alias) })
|
|
658
|
+
|
|
659
|
+
if (options.branch) {
|
|
660
|
+
warn('--branch flag has been renamed to --alias and will be removed in future versions')
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (options.context && !options.build) {
|
|
664
|
+
return error('--context flag is only available when using the --build flag')
|
|
665
|
+
}
|
|
682
666
|
|
|
667
|
+
await command.authenticate(options.auth)
|
|
668
|
+
|
|
669
|
+
let siteId = site.id || options.site
|
|
670
|
+
|
|
671
|
+
let siteData = {}
|
|
672
|
+
if (siteId && !isEmpty(siteInfo)) {
|
|
673
|
+
siteData = siteInfo
|
|
674
|
+
siteId = siteData.id
|
|
675
|
+
} else {
|
|
676
|
+
log("This folder isn't linked to a site yet")
|
|
677
|
+
const NEW_SITE = '+ Create & configure a new site'
|
|
678
|
+
const EXISTING_SITE = 'Link this directory to an existing site'
|
|
679
|
+
|
|
680
|
+
const initializeOpts = [EXISTING_SITE, NEW_SITE]
|
|
681
|
+
|
|
682
|
+
const { initChoice } = await inquirer.prompt([
|
|
683
|
+
{
|
|
684
|
+
type: 'list',
|
|
685
|
+
name: 'initChoice',
|
|
686
|
+
message: 'What would you like to do?',
|
|
687
|
+
choices: initializeOpts,
|
|
688
|
+
},
|
|
689
|
+
])
|
|
690
|
+
// create site or search for one
|
|
691
|
+
if (initChoice === NEW_SITE) {
|
|
692
|
+
siteData = await sitesCreate({}, command)
|
|
693
|
+
site.id = siteData.id
|
|
694
|
+
siteId = site.id
|
|
695
|
+
} else if (initChoice === EXISTING_SITE) {
|
|
696
|
+
siteData = await link({}, command)
|
|
697
|
+
site.id = siteData.id
|
|
698
|
+
siteId = site.id
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (options.trigger) {
|
|
703
|
+
return triggerDeploy({ api, options, siteData, siteId })
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const deployToProduction = options.prod || (options.prodIfUnlocked && !siteData.published_deploy.locked)
|
|
707
|
+
|
|
708
|
+
let results = {}
|
|
709
|
+
|
|
710
|
+
if (options.build) {
|
|
711
|
+
await handleBuild({
|
|
712
|
+
packagePath: command.workspacePackage,
|
|
713
|
+
cachedConfig: command.netlify.cachedConfig,
|
|
714
|
+
currentDir: command.workingDir,
|
|
715
|
+
options,
|
|
716
|
+
deployHandler: async ({ netlifyConfig }) => {
|
|
717
|
+
results = await prepAndRunDeploy({
|
|
718
|
+
command,
|
|
719
|
+
options,
|
|
720
|
+
workingDir,
|
|
721
|
+
api,
|
|
722
|
+
site,
|
|
723
|
+
config: netlifyConfig,
|
|
724
|
+
siteData,
|
|
725
|
+
siteId,
|
|
726
|
+
deployToProduction,
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
return {}
|
|
730
|
+
},
|
|
731
|
+
})
|
|
732
|
+
} else {
|
|
733
|
+
results = await prepAndRunDeploy({
|
|
734
|
+
command,
|
|
735
|
+
options,
|
|
736
|
+
workingDir,
|
|
737
|
+
api,
|
|
738
|
+
site,
|
|
739
|
+
config: command.netlify.config,
|
|
740
|
+
siteData,
|
|
741
|
+
siteId,
|
|
742
|
+
deployToProduction,
|
|
743
|
+
})
|
|
744
|
+
}
|
|
683
745
|
const isIntegrationDeploy = command.name() === 'integration:deploy'
|
|
684
746
|
|
|
685
747
|
printResults({
|
package/src/lib/build.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import fs from 'fs'
|
|
2
3
|
import process from 'process'
|
|
3
4
|
|
|
4
5
|
import build from '@netlify/build'
|
|
6
|
+
import tomlify from 'tomlify-j0.4'
|
|
5
7
|
|
|
6
8
|
import { isFeatureFlagEnabled } from '../utils/feature-flags.mjs'
|
|
7
9
|
|
|
@@ -22,36 +24,66 @@ import { featureFlags as edgeFunctionsFeatureFlags } from './edge-functions/cons
|
|
|
22
24
|
* @param {object} config
|
|
23
25
|
* @param {*} config.cachedConfig
|
|
24
26
|
* @param {string} [config.packagePath]
|
|
27
|
+
* @param {string} config.currentDir
|
|
25
28
|
* @param {string} config.token
|
|
26
29
|
* @param {import('commander').OptionValues} config.options
|
|
30
|
+
* @param {*} config.deployHandler
|
|
27
31
|
* @returns {BuildConfig}
|
|
28
32
|
*/
|
|
29
33
|
export const getBuildOptions = ({
|
|
30
34
|
cachedConfig,
|
|
35
|
+
currentDir,
|
|
36
|
+
deployHandler,
|
|
31
37
|
options: { context, cwd, debug, dry, json, offline, silent },
|
|
32
38
|
packagePath,
|
|
33
39
|
token,
|
|
34
|
-
}) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
}) => {
|
|
41
|
+
const eventHandlers = {
|
|
42
|
+
onEnd: {
|
|
43
|
+
handler: ({ netlifyConfig }) => {
|
|
44
|
+
const string = tomlify.toToml(netlifyConfig)
|
|
45
|
+
|
|
46
|
+
if (!fs.existsSync(`${currentDir}/.netlify`)) {
|
|
47
|
+
fs.mkdirSync(`${currentDir}/.netlify`, { recursive: true })
|
|
48
|
+
}
|
|
49
|
+
fs.writeFileSync(`${currentDir}/.netlify/netlify.toml`, string)
|
|
50
|
+
|
|
51
|
+
return {}
|
|
52
|
+
},
|
|
53
|
+
description: 'Save updated config',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (deployHandler) {
|
|
58
|
+
eventHandlers.onPostBuild = {
|
|
59
|
+
handler: deployHandler,
|
|
60
|
+
description: 'Deploy Site',
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
cachedConfig,
|
|
66
|
+
siteId: cachedConfig.siteInfo.id,
|
|
67
|
+
packagePath,
|
|
68
|
+
token,
|
|
69
|
+
dry,
|
|
70
|
+
debug,
|
|
71
|
+
context,
|
|
72
|
+
mode: 'cli',
|
|
73
|
+
telemetry: false,
|
|
74
|
+
// buffer = true will not stream output
|
|
75
|
+
buffer: json || silent,
|
|
76
|
+
offline,
|
|
77
|
+
cwd,
|
|
78
|
+
featureFlags: {
|
|
79
|
+
...edgeFunctionsFeatureFlags,
|
|
80
|
+
...getFeatureFlagsFromSiteInfo(cachedConfig.siteInfo),
|
|
81
|
+
functionsBundlingManifest: true,
|
|
82
|
+
},
|
|
83
|
+
eventHandlers,
|
|
84
|
+
edgeFunctionsBootstrapURL: getBootstrapURL(),
|
|
85
|
+
}
|
|
86
|
+
}
|
|
55
87
|
|
|
56
88
|
/**
|
|
57
89
|
* @param {*} siteInfo
|
|
@@ -233,10 +233,6 @@ const getFunctionsServer = (options) => {
|
|
|
233
233
|
}),
|
|
234
234
|
)
|
|
235
235
|
|
|
236
|
-
app.get('/favicon.ico', function onRequest(_req, res) {
|
|
237
|
-
res.status(204).end()
|
|
238
|
-
})
|
|
239
|
-
|
|
240
236
|
app.all(`${functionsPrefix}*`, functionHandler)
|
|
241
237
|
app.all(`${buildersPrefix}*`, functionHandler)
|
|
242
238
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
DEFAULT_MAX_RETRY,
|
|
14
14
|
DEFAULT_SYNC_LIMIT,
|
|
15
15
|
} from './constants.mjs'
|
|
16
|
+
import { hashConfig } from './hash-config.mjs'
|
|
16
17
|
import hashFiles from './hash-files.mjs'
|
|
17
18
|
import hashFns from './hash-fns.mjs'
|
|
18
19
|
import uploadFiles from './upload-files.mjs'
|
|
@@ -27,7 +28,7 @@ export const deploySite = async (
|
|
|
27
28
|
branch,
|
|
28
29
|
concurrentHash = DEFAULT_CONCURRENT_HASH,
|
|
29
30
|
concurrentUpload = DEFAULT_CONCURRENT_UPLOAD,
|
|
30
|
-
|
|
31
|
+
config,
|
|
31
32
|
deployId,
|
|
32
33
|
deployTimeout = DEFAULT_DEPLOY_TIMEOUT,
|
|
33
34
|
draft = false,
|
|
@@ -55,31 +56,39 @@ export const deploySite = async (
|
|
|
55
56
|
})
|
|
56
57
|
|
|
57
58
|
const edgeFunctionsDistPath = await getDistPathIfExists(workingDir)
|
|
58
|
-
const [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
59
|
+
const [
|
|
60
|
+
{ files: staticFiles, filesShaMap: staticShaMap },
|
|
61
|
+
{ fnConfig, fnShaMap, functionSchedules, functions, functionsWithNativeModules },
|
|
62
|
+
configFile,
|
|
63
|
+
] = await Promise.all([
|
|
64
|
+
hashFiles({
|
|
65
|
+
assetType,
|
|
66
|
+
concurrentHash,
|
|
67
|
+
directories: [dir, edgeFunctionsDistPath].filter(Boolean),
|
|
68
|
+
filter,
|
|
69
|
+
hashAlgorithm,
|
|
70
|
+
normalizer: deployFileNormalizer.bind(null, workingDir),
|
|
71
|
+
statusCb,
|
|
72
|
+
}),
|
|
73
|
+
hashFns(fnDir, {
|
|
74
|
+
functionsConfig,
|
|
75
|
+
tmpDir,
|
|
76
|
+
concurrentHash,
|
|
77
|
+
hashAlgorithm,
|
|
78
|
+
statusCb,
|
|
79
|
+
assetType,
|
|
80
|
+
workingDir,
|
|
81
|
+
manifestPath,
|
|
82
|
+
skipFunctionsCache,
|
|
83
|
+
siteEnv,
|
|
84
|
+
rootDir: siteRoot,
|
|
85
|
+
}),
|
|
86
|
+
hashConfig({ config }),
|
|
87
|
+
])
|
|
88
|
+
|
|
89
|
+
const files = { ...staticFiles, [configFile.normalizedPath]: configFile.hash }
|
|
90
|
+
const filesShaMap = { ...staticShaMap, [configFile.hash]: [configFile] }
|
|
91
|
+
|
|
83
92
|
const edgeFunctionsCount = Object.keys(files).filter(isEdgeFunctionFile).length
|
|
84
93
|
const filesCount = Object.keys(files).length - edgeFunctionsCount
|
|
85
94
|
const functionsCount = Object.keys(functions).length
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import hasha from 'hasha'
|
|
2
|
+
import tomlify from 'tomlify-j0.4'
|
|
3
|
+
|
|
4
|
+
export const hashConfig = ({ config }) => {
|
|
5
|
+
if (!config) throw new Error('Missing config option')
|
|
6
|
+
const configString = serializeToml(config)
|
|
7
|
+
|
|
8
|
+
const hash = hasha(configString, { algorithm: 'sha1' })
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
assetType: 'file',
|
|
12
|
+
body: configString,
|
|
13
|
+
hash,
|
|
14
|
+
normalizedPath: 'netlify.toml',
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const serializeToml = function (object) {
|
|
19
|
+
return tomlify.toToml(object, { space: 2, replace: replaceTomlValue })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// `tomlify-j0.4` serializes integers as floats, e.g. `200.0`.
|
|
23
|
+
// This is a problem with `redirects[*].status`.
|
|
24
|
+
const replaceTomlValue = function (key, value) {
|
|
25
|
+
return Number.isInteger(value) ? String(value) : false
|
|
26
|
+
}
|
|
@@ -14,8 +14,9 @@ const uploadFiles = async (api, deployId, uploadList, { concurrentUpload, maxRet
|
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
const uploadFile = async (fileObj, index) => {
|
|
17
|
-
const { assetType, filepath, invocationMode, normalizedPath, runtime } = fileObj
|
|
18
|
-
|
|
17
|
+
const { assetType, body, filepath, invocationMode, normalizedPath, runtime } = fileObj
|
|
18
|
+
|
|
19
|
+
const readStreamCtor = () => body ?? fs.createReadStream(filepath)
|
|
19
20
|
|
|
20
21
|
statusCb({
|
|
21
22
|
type: 'upload',
|