Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/commands/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,7 @@ const getDeployFilesFilter = ({ deployFolder, site }: { deployFolder: string; si
// when site.root !== deployFolder the behaviour matches our buildbot
const skipNodeModules = site.root === deployFolder

return (filename: string | null | undefined) => {
if (filename == null) {
return false
}
return (filename: string) => {
if (filename === deployFolder) {
return true
}
Expand Down
3 changes: 0 additions & 3 deletions src/utils/deploy/deploy-site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ const buildStatsString = (possibleParts: (string | false | undefined)[]) => {
}

export interface DeploySiteOptions {
assetType?: 'file' | undefined
branch?: string
concurrentHash?: number
concurrentUpload?: number
Expand Down Expand Up @@ -83,7 +82,6 @@ export const deploySite = async (
siteId: string,
dir: string,
{
assetType,
branch,
concurrentHash = DEFAULT_CONCURRENT_HASH,
concurrentUpload = DEFAULT_CONCURRENT_UPLOAD,
Expand Down Expand Up @@ -127,7 +125,6 @@ export const deploySite = async (
{ edgeFunctions, edgeFnShaMap },
] = await Promise.all([
hashFiles({
assetType,
concurrentHash,
directories: [dir, edgeFunctionsDistPath, deployConfigPath, dbMigrationsDistPath].filter((d): d is string =>
Boolean(d),
Expand Down
3 changes: 1 addition & 2 deletions src/utils/deploy/hash-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import tomlify from 'tomlify-j0.4'
import type { DeployConfig } from './types.js'
import type { InlineUploadFile } from './upload-files.js'

export const hashConfig = ({ config }: { config: DeployConfig | undefined }): InlineUploadFile & { hash: string } => {
if (!config) throw new Error('Missing config option')
export const hashConfig = ({ config }: { config: DeployConfig }): InlineUploadFile & { hash: string } => {
const configString = serializeToml(config)

const hash = createHash('sha1').update(configString).digest('hex')
Expand Down
8 changes: 2 additions & 6 deletions src/utils/deploy/hash-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,24 @@ import { fileFilterCtor, fileNormalizerCtor, hasherCtor, manifestCollectorCtor }
import type { StatusCallback } from './status-cb.js'

const hashFiles = async ({
assetType = 'file',
concurrentHash,
directories,
filter,
hashAlgorithm = 'sha1',
normalizer,
statusCb,
}: {
assetType?: 'file' | undefined
concurrentHash: number
directories: string[]
filter: ((filename: string) => boolean) | undefined
filter: (filename: string) => boolean
hashAlgorithm?: string | undefined
normalizer?: (file: File) => File
statusCb: StatusCallback
}): Promise<{ files: Record<string, string>; filesShaMap: Record<string, File[]> }> => {
if (!filter) throw new Error('Missing filter function option')

const fileStream = walker(directories, { filter })
const fileFilter = fileFilterCtor()
const hasher = hasherCtor({ concurrentHash, hashAlgorithm })
const fileNormalizer = fileNormalizerCtor({ assetType, normalizer })
const fileNormalizer = fileNormalizerCtor({ normalizer })

// Written to by manifestCollector
// normalizedPath: hash (wanted by deploy API)
Expand Down
84 changes: 33 additions & 51 deletions src/utils/deploy/hash-fns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,20 @@ const trafficRulesConfig = (trafficRules?: TrafficRules) => {
}
}

interface FunctionConfigPayload {
display_name: FunctionResult['displayName']
excluded_routes: FunctionResult['excludedRoutes']
generator: FunctionResult['generator']
memory: FunctionResult['memory']
region: FunctionResult['region']
routes: FunctionResult['routes']
build_data: FunctionResult['buildData']
priority: FunctionResult['priority']
traffic_rules: ReturnType<typeof trafficRulesConfig>
vcpu: FunctionResult['vcpu']
}
const toFunctionConfigPayload = (func: FunctionResult) => ({
display_name: func.displayName,
excluded_routes: func.excludedRoutes,
generator: func.generator,
memory: func.memory,
region: func.region,
routes: func.routes,
build_data: func.buildData,
priority: func.priority,
traffic_rules: trafficRulesConfig(func.trafficRules),
vcpu: func.vcpu,
})

type FunctionConfigPayload = ReturnType<typeof toFunctionConfigPayload>

const hashFns = async (
command: BaseCommand,
Expand Down Expand Up @@ -202,13 +204,12 @@ const hashFns = async (
serverManifestPath?: string | undefined
skipFunctionsCache?: boolean | undefined
statusCb: StatusCallback
tmpDir?: string | undefined
tmpDir: string
},
): Promise<{
functionSchedules?: { name: string; cron: string }[] | undefined
functions: Record<string, string>
functionsWithNativeModules: FunctionResult[]
shaMap?: Record<string, never> | undefined
fnShaMap?: Record<string, HashedFunctionFile[]> | undefined
fnConfig?: Record<string, FunctionConfigPayload> | undefined
server?: { sha: string; region?: string } | undefined
Expand All @@ -217,11 +218,7 @@ const hashFns = async (
// Exit early if there is nothing to bundle. A site can have a server without
// any functions directory.
if (directories.length === 0 && !serverEnabled) {
return { functions: {}, functionsWithNativeModules: [], shaMap: {} }
}

if (!tmpDir) {
throw new Error('Missing tmpDir directory for zipping files')
return { functions: {}, functionsWithNativeModules: [] }
}

const [functionZips, serverBundle] = await Promise.all([
Expand Down Expand Up @@ -282,38 +279,23 @@ const hashFns = async (
trafficRules,
}),
)
const fnConfig = functionZips
.filter((func) =>
Boolean(
func.displayName ||
func.generator ||
func.routes ||
func.buildData ||
func.priority ||
func.trafficRules ||
func.region ||
func.memory ||
func.vcpu,
),
)
.reduce<Record<string, FunctionConfigPayload>>(
(funcs, curr) => ({
...funcs,
[curr.name]: {
display_name: curr.displayName,
excluded_routes: curr.excludedRoutes,
generator: curr.generator,
memory: curr.memory,
region: curr.region,
routes: curr.routes,
build_data: curr.buildData,
priority: curr.priority,
traffic_rules: trafficRulesConfig(curr.trafficRules),
vcpu: curr.vcpu,
},
}),
{},
)
const fnConfig = Object.fromEntries(
functionZips
.filter((func) =>
Boolean(
func.displayName ||
func.generator ||
func.routes ||
func.buildData ||
func.priority ||
func.trafficRules ||
func.region ||
func.memory ||
func.vcpu,
),
)
.map((func) => [func.name, toFunctionConfigPayload(func)]),
)
const functionSchedules = functionZips
.map(({ name, schedule }) => schedule && { name, cron: schedule })
.filter((schedule): schedule is { name: string; cron: string } => schedule !== '' && schedule !== undefined)
Expand Down
10 changes: 2 additions & 8 deletions src/utils/deploy/hasher-segments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,11 @@ export const hasherCtor = ({ concurrentHash, hashAlgorithm }: { concurrentHash?:
}

// Inject normalized file names into normalizedPath and assetType
export const fileNormalizerCtor = ({
assetType,
normalizer: normalizeFunction,
}: {
assetType: 'file'
normalizer?: (file: File) => File
}) => {
export const fileNormalizerCtor = ({ normalizer: normalizeFunction }: { normalizer?: (file: File) => File }) => {
return new Transform({
objectMode: true,
transform(fileObj: HashedFile, _, callback) {
const normalizedFile: File = { ...fileObj, assetType, normalizedPath: normalizePath(fileObj.relname) }
const normalizedFile: File = { ...fileObj, assetType: 'file', normalizedPath: normalizePath(fileObj.relname) }

const result = normalizeFunction !== undefined ? normalizeFunction(normalizedFile) : normalizedFile

Expand Down
18 changes: 9 additions & 9 deletions src/utils/deploy/upload-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ type UploadDeployServerParams = WithRetryCount<Parameters<UploadApi['uploadDeplo
interface UploadFileBase {
filepath: string
normalizedPath: string
body?: undefined
}

export interface StaticUploadFile extends UploadFileBase {
assetType: 'file'
body?: undefined
}

// A generated file (e.g. `netlify.toml`) whose contents are held in memory rather than on disk
Expand Down Expand Up @@ -94,10 +94,7 @@ const uploadFiles = async (
})

const uploadFile = async (fileObj: UploadFile, index: number) => {
const { body, filepath, normalizedPath } = fileObj

// FIXME(@netlify/api): octet-stream `body` is typed as `ReadStream` only, but an inline string body works too
const readStreamCtor = () => (body ?? fs.createReadStream(filepath)) as fs.ReadStream
const { normalizedPath } = fileObj

statusCb({
type: 'upload',
Expand All @@ -107,10 +104,13 @@ const uploadFiles = async (

switch (fileObj.assetType) {
case 'file': {
const { body, filepath } = fileObj
return await retryUpload(
() =>
api.uploadDeployFile({
body: readStreamCtor,
// FIXME(@netlify/api): `types.d.ts` hardcodes every `application/octet-stream` body as `ReadStream | (() => ReadStream)`,
// ignoring the spec's `string` type; a string body works at runtime
body: () => (body ?? fs.createReadStream(filepath)) as fs.ReadStream,
deployId,
path: encodeURI(normalizedPath),
}),
Expand All @@ -122,7 +122,7 @@ const uploadFiles = async (

return await retryUpload((retryCount) => {
const params: UploadDeployFunctionParams = {
body: readStreamCtor,
body: () => fs.createReadStream(fileObj.filepath),
deployId,
invocationMode,
timeout,
Expand All @@ -140,7 +140,7 @@ const uploadFiles = async (
case 'edge-function': {
return await retryUpload((retryCount) => {
const params: UploadDeployEdgeFunctionParams = {
body: readStreamCtor,
body: () => fs.createReadStream(fileObj.filepath),
deployId,
codeSha: normalizedPath,
}
Expand All @@ -155,7 +155,7 @@ const uploadFiles = async (
case 'server': {
return await retryUpload((retryCount) => {
const params: UploadDeployServerParams = {
body: readStreamCtor,
body: () => fs.createReadStream(fileObj.filepath),
deployId,
codeSha: fileObj.hash,
}
Expand Down
Loading