Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: export function to bundle edge functions #4271

Merged
merged 12 commits into from
May 4, 2022
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
2 changes: 2 additions & 0 deletions packages/build/src/core/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { warnOnMissingSideFiles } from './missing_side_file.js'
import { normalizeFlags } from './normalize_flags.js'
import { getSeverity } from './severity.js'

export { runCoreSteps } from '../steps/run_core_steps.js'

/**
* Main entry point of Netlify Build.
* Runs a builds and returns whether it succeeded or not.
Expand Down
199 changes: 199 additions & 0 deletions packages/build/src/steps/run_core_steps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/* eslint-disable max-lines */
import { getConfigOpts, loadConfig } from '../core/config.js'
import { getConstants } from '../core/constants.js'
import { normalizeFlags } from '../core/normalize_flags.js'
import { getSeverity } from '../core/severity.js'
import { handleBuildError } from '../error/handle.js'
import { getErrorInfo } from '../error/info.js'
import { startErrorMonitor } from '../error/monitor/start.js'
import { getBufferLogs } from '../log/logger.js'
import { logBuildStart } from '../log/messages/core.js'
import { reportStatuses } from '../status/report.js'

import { getSteps } from './get.js'
import { runSteps } from './run_steps.js'

/**
* Runs specific core steps for a build and returns whether it succeeded or not.
*
* @param {string[]} [buildSteps] - a string array of build steps to run
* @param {object} [flags] - build configuration CLI flags
* @param {string} [flags.config] - Path to the configuration file
* @param {string} [flags.cwd] - Current directory. Used to retrieve the configuration file
* @param {string} [flags.repositoryRoot] - Git repository root directory. Used to retrieve the configuration file.
* @param {string} [flags.apiHost] - Netlify API endpoint
* @param {string} [flags.token] - Netlify API token for authentication
* @param {string} [flags.siteId] - Netlify Site ID
* @param {string} [flags.deployId] - Netlify Deploy ID
* @param {string} [flags.context] - Build context
* @param {string} [flags.branch] - Repository branch
* @param {boolean} [flags.dry=false] - Run in dry mode, i.e. printing steps without executing them
* @param {string} [flags.nodePath] - Path to the Node.js binary to use in the build command and plugins
* @param {boolean} [flags.buffer=false] - Buffer output instead of printing it
*
* @returns {object} buildResult
* @returns {boolean} buildResult.success - Whether build succeeded or failed
* @returns {number} buildResult.severityCode - Build success/failure status among:
* 0 (success), 1 (build cancelled), 2 (user error), 3 (plugin error), 4 (system error). Can be used as exit code.
* @returns {string[]} buildResult.logs - When using the `buffer` option, all log messages
*/
export const runCoreSteps = async (buildSteps, flags = {}) => {
const { errorMonitor, mode, logs, debug, ...flagsA } = startBuild(flags)
const errorParams = { errorMonitor, mode, logs, debug }

try {
const { netlifyConfig: netlifyConfigA, configMutations } = await executeBuildStep({
...flagsA,
errorMonitor,
mode,
logs,
debug,
errorParams,
buildSteps,
})
const { success, severityCode } = getSeverity('success')

return { success, severityCode, netlifyConfig: netlifyConfigA, configMutations, logs }
} catch (error) {
const { severity } = await handleBuildError(error, errorParams)
const { success, severityCode } = getSeverity(severity)

return { success, severityCode, logs }
}
}

const startBuild = function (flags) {
const logs = getBufferLogs(flags)

logBuildStart(logs)

const { bugsnagKey, ...flagsA } = normalizeFlags(flags, logs)
const errorMonitor = startErrorMonitor({ flags: flagsA, logs, bugsnagKey })

return { ...flagsA, errorMonitor, logs }
}

const getBuildSteps = function (buildSteps) {
const allSteps = getSteps([]).steps.filter(({ coreStepId }) => buildSteps.includes(coreStepId))

return allSteps
}

const executeBuildStep = async function ({
config,
defaultConfig,
cachedConfig,
debug,
nodePath,
functionsDistDir,
edgeFunctionsDistDir,
errorMonitor,
mode,
logs,
errorParams,
featureFlags,
buildSteps,
repositoryRoot,
}) {
const configOpts = getConfigOpts({
config,
defaultConfig,
featureFlags,
mode,
repositoryRoot,
})
const {
netlifyConfig,
buildDir,
siteInfo,
childEnv,
userNodeVersion,
repositoryRoot: repositoryRootA,
} = await loadConfig({
configOpts,
cachedConfig,
debug,
logs,
nodePath,
timers: [],
})
const constants = await getConstants({
buildDir,
functionsDistDir,
edgeFunctionsDistDir,
netlifyConfig,
siteInfo,
mode,
})

// eslint-disable-next-line fp/no-mutating-assign
Object.assign(errorParams, { netlifyConfig, siteInfo, childEnv, userNodeVersion })

try {
const { netlifyConfig: netlifyConfigA, configMutations } = await runBuildStep({
netlifyConfig,
buildDir,
nodePath,
logs,
debug,
constants,
featureFlags,
childEnv,
buildSteps,
repositoryRoot: repositoryRootA,
})

return {
netlifyConfig: netlifyConfigA,
configMutations,
}
} catch (error) {
const [{ statuses }] = getErrorInfo(error)

await reportStatuses({
statuses,
childEnv,
mode,
netlifyConfig,
errorMonitor,
logs,
debug,
})

throw error
}
}

const runBuildStep = async function ({
netlifyConfig,
buildDir,
nodePath,
constants,
logs,
debug,
featureFlags,
childEnv,
buildSteps,
repositoryRoot,
}) {
try {
const { netlifyConfig: netlifyConfigA, configMutations } = await runSteps({
steps: getBuildSteps(buildSteps),
buildDir,
nodePath,
constants,
netlifyConfig,
logs,
debug,
timers: [],
featureFlags,
childEnv,
repositoryRoot,
})

return { netlifyConfig: netlifyConfigA, configMutations }
} catch (error) {
console.error(error)
}
}
/* eslint-enable max-lines */
52 changes: 52 additions & 0 deletions packages/build/tests/edge_functions/snapshots/tests.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,55 @@ Generated by [AVA](https://avajs.dev).
────────────────────────────────────────────────────────────────␊
(Netlify Build completed in 1ms)`

## bundles Edge Functions via runCoreSteps function

> Snapshot 1

`␊
────────────────────────────────────────────────────────────────␊
Netlify Build ␊
────────────────────────────────────────────────────────────────␊
> Version␊
@netlify/build 1.0.0␊
> Flags␊
buildSteps:␊
- edge_functions_bundling␊
debug: true␊
repositoryRoot: packages/build/tests/edge_functions/fixtures/functions_user␊
testOpts:␊
pluginsListUrl: test␊
silentLingeringProcesses: true␊
useRunCoreSteps: true␊
> Current directory␊
packages/build/tests/edge_functions/fixtures/functions_user␊
> Config file␊
packages/build/tests/edge_functions/fixtures/functions_user/netlify.toml␊
> Resolved config␊
build:␊
edge_functions: packages/build/tests/edge_functions/fixtures/functions_user/netlify/edge-functions␊
publish: packages/build/tests/edge_functions/fixtures/functions_user␊
publishOrigin: default␊
> Context␊
production␊
────────────────────────────────────────────────────────────────␊
1. Edge Functions bundling ␊
────────────────────────────────────────────────────────────────␊
Packaging Edge Functions from netlify/edge-functions directory:␊
- function-1␊
(Edge Functions bundling completed in 1ms)␊
────────────────────────────────────────────────────────────────␊
Netlify Build Complete ␊
────────────────────────────────────────────────────────────────␊
(Netlify Build completed in 1ms)`
Binary file modified packages/build/tests/edge_functions/snapshots/tests.js.snap
Binary file not shown.
7 changes: 7 additions & 0 deletions packages/build/tests/edge_functions/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,10 @@ test.serial('builds Edge Functions from both the user and the internal directori
await runFixture(t, fixtureName, { flags: { debug: false, mode: 'buildbot' } })
await assertManifest(t, fixtureName)
})

test('bundles Edge Functions via runCoreSteps function', async (t) => {
const fixtureName = 'functions_user'

await runFixture(t, fixtureName, { flags: { buildSteps: ['edge_functions_bundling'], useRunCoreSteps: true } })
await assertManifest(t, fixtureName)
})
59 changes: 56 additions & 3 deletions packages/build/tests/helpers/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export const runFixtureCommon = async function (
mainFunc,
binaryPath,
useBinary = false,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
} = {},
) {
const forceColor = isPrint() ? { FORCE_COLOR: '1' } : {}
Expand All @@ -51,6 +54,9 @@ export const runFixtureCommon = async function (
fixtureName,
copyRoot,
copyRootDir,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
})

doTestAction({ t, returnValue, normalizeOption, snapshot })
Expand Down Expand Up @@ -101,9 +107,22 @@ const runCommand = async function ({
copyRoot,
copyRoot: { branch } = {},
copyRootDir,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
}) {
if (copyRoot === undefined) {
return execCommand({ mainFunc, binaryPath, useBinary, mainFlags, commandEnv, cwd })
return execCommand({
mainFunc,
binaryPath,
useBinary,
mainFlags,
commandEnv,
cwd,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
})
}

try {
Expand All @@ -113,17 +132,41 @@ const runCommand = async function ({
await execaCommand(`git checkout -b ${branch}`, { cwd: copyRootDir })
}

return await execCommand({ mainFunc, binaryPath, useBinary, mainFlags, commandEnv, cwd })
return await execCommand({
mainFunc,
binaryPath,
useBinary,
mainFlags,
commandEnv,
cwd,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
})
} finally {
await removeDir(copyRootDir)
}
}

const execCommand = function ({ mainFunc, binaryPath, useBinary, mainFlags, commandEnv, cwd }) {
const execCommand = function ({
mainFunc,
binaryPath,
useBinary,
mainFlags,
commandEnv,
cwd,
runCoreStepsFunc,
buildSteps,
useRunCoreSteps,
}) {
if (useBinary) {
return execCliCommand({ binaryPath, mainFlags, commandEnv, cwd })
}

if (useRunCoreSteps) {
return execRunCoreStepsFunc({ runCoreStepsFunc, mainFlags, buildSteps })
}

return execMainFunc({ mainFunc, mainFlags, commandEnv })
}

Expand Down Expand Up @@ -174,6 +217,16 @@ const execMainFunc = async function ({ mainFunc, mainFlags, commandEnv }) {
}
}

const execRunCoreStepsFunc = async function ({ runCoreStepsFunc, mainFlags, buildSteps }) {
try {
const returnValue = await runCoreStepsFunc(buildSteps, { ...mainFlags })
return { returnValue }
} catch (error) {
const returnValue = error.message
return { returnValue }
}
}

// The `PRINT` environment variable can be set to `1` to run the test in print
// mode. Print mode is a debugging mode which shows the test output but does
// not create nor compare its snapshot.
Expand Down
Loading