# core/steps/build.step.ts

> 104 lines of code and 19 definitions.

Tree: Bane's Lab Deploy
Language: typescript
Layer: application
Canonical: https://banes-lab.com/anatomy/deploy#file-deploy-core-steps-build-step-ts
Source text: https://banes-lab.com/source/deploy/core/steps/build.step.ts.txt

Listed in [core/steps](https://banes-lab.com/api/source/deploy/core/steps.md), after [core/steps/backup.step.ts](https://banes-lab.com/source/deploy/core/steps/backup.step.ts.md) and before [core/steps/certificate.step.ts](https://banes-lab.com/source/deploy/core/steps/certificate.step.ts.md).

## Definitions

- `objectPath` (lexical_declaration, line 23)
- `verifiedFetch` (lexical_declaration, line 31, exported)
- `staleModules` (lexical_declaration, line 35, exported)
- `compileModules` (lexical_declaration, line 56, exported)
- `swapModules` (lexical_declaration, line 81, exported)
- `restoreModules` (lexical_declaration, line 97, exported)
- `settleModules` (lexical_declaration, line 110, exported)
- `markerPath` (lexical_declaration, line 27)
- `unpacked` (lexical_declaration, line 45)
- `COMMAND_JOIN` (lexical_declaration, line 17)
- `markerOf` (lexical_declaration, line 19, exported)
- `checked` (lexical_declaration, line 36, exported)
- `archive` (lexical_declaration, line 46)
- `builtPath` (lexical_declaration, line 52, exported)
- `dynamic` (lexical_declaration, line 62, exported)
- `[object, marker]` (lexical_declaration, line 83, exported)
- `result` (lexical_declaration, line 91, exported)
- `commands` (lexical_declaration, line 101, exported)
- `paths` (lexical_declaration, line 114, exported)

## Contained in

- [core/steps](https://banes-lab.com/anatomy/deploy/folder-deploy-core-steps.md)

## Used by

- [core/pipelines/setup.pipeline.ts](https://banes-lab.com/source/deploy/core/pipelines/setup.pipeline.ts.md)
- [core/steps/setup.step.ts](https://banes-lab.com/source/deploy/core/steps/setup.step.ts.md)

## Linked from

- [core/pipelines](https://banes-lab.com/anatomy/deploy/folder-deploy-core-pipelines.md)
- [core/steps](https://banes-lab.com/anatomy/deploy/folder-deploy-core-steps.md)

## Source

```typescript
import {
    BUILD_MARKER_SUFFIX,
    BUILD_PACKAGES,
    MODULES_FOLDER,
    NGINX_SOURCE,
    PACKAGE_INDEX_COMMAND,
    PACKAGE_INSTALL_COMMAND,
    PACKAGE_LOCK_WAIT,
    PREVIOUS_SUFFIX,
    SETUP_STAGING,
    SOURCE_MODULES,
} from "#configuration/constants/nginx.constants";
import type { Journal, Shell, SourceArchive, SourceModule } from "#types/deployment.types";
import { MODULE_BUILDING, MODULE_BUILD_FAILED } from "#configuration/strings/nginx.strings";
import { PATH_SEPARATOR } from "#configuration/constants/deployment.constants";

const COMMAND_JOIN = " && ";

export const markerOf = function markerOf(module: SourceModule): string {
    return `${NGINX_SOURCE.upstream} ${module.version}`;
};

const objectPath = function objectPath(module: SourceModule): string {
    return MODULES_FOLDER + PATH_SEPARATOR + module.object;
};

const markerPath = function markerPath(module: SourceModule): string {
    return objectPath(module) + BUILD_MARKER_SUFFIX;
};

export const verifiedFetch = function verifiedFetch(url: string, path: string, sha256: string): string {
    return `curl -fsSL -o ${path} "${url}" && echo "${sha256}  ${path}" | sha256sum -c --quiet -`;
};

export const staleModules = async function staleModules(shell: Shell): Promise<readonly SourceModule[]> {
    const checked = await Promise.all(
        SOURCE_MODULES.map(async (module) => {
            const result = await shell.run(`test -f ${objectPath(module)} && cat ${markerPath(module)}`);
            return result.code === 0 && result.stdout.trim() === markerOf(module) ? null : module;
        }),
    );
    return checked.filter((module): module is SourceModule => module !== null);
};

const unpacked = function unpacked(source: SourceArchive): string {
    const archive = `${SETUP_STAGING}/${source.folder}.tar.gz`;
    return [verifiedFetch(source.archive, archive, source.sha256), `tar -xzf ${archive} -C ${SETUP_STAGING}`].join(
        COMMAND_JOIN,
    );
};

export const builtPath = function builtPath(module: SourceModule): string {
    return `${SETUP_STAGING}/${NGINX_SOURCE.folder}/objs/${module.object}`;
};

export const compileModules = async function compileModules(
    shell: Shell,
    journal: Journal,
    modules: readonly SourceModule[],
): Promise<void> {
    journal.log(MODULE_BUILDING + modules.map((module) => module.object).join(", "));
    const dynamic = modules.map((module) => `--add-dynamic-module=../${module.folder}`).join(" ");
    const result = await shell.run(
        [
            `mkdir -p ${SETUP_STAGING}`,
            PACKAGE_LOCK_WAIT,
            PACKAGE_INDEX_COMMAND,
            `${PACKAGE_INSTALL_COMMAND} ${BUILD_PACKAGES.join(" ")}`,
            unpacked(NGINX_SOURCE),
            ...modules.map(unpacked),
            `cd ${SETUP_STAGING}/${NGINX_SOURCE.folder}`,
            `./configure --with-compat ${dynamic} > /dev/null`,
            "make modules > /dev/null",
        ].join(COMMAND_JOIN),
    );
    if (result.code !== 0) {
        throw new Error(MODULE_BUILD_FAILED + result.stderr);
    }
};

export const swapModules = async function swapModules(shell: Shell, modules: readonly SourceModule[]): Promise<void> {
    const commands = modules.map((module) => {
        const [object, marker] = [objectPath(module), markerPath(module)];
        return [
            `if [ -f ${object} ]; then sudo cp -p ${object} ${object}${PREVIOUS_SUFFIX}; fi`,
            `if [ -f ${marker} ]; then sudo cp -p ${marker} ${marker}${PREVIOUS_SUFFIX}; fi`,
            `sudo install -m 644 ${builtPath(module)} ${object}`,
            `echo '${markerOf(module)}' | sudo tee ${marker} > /dev/null`,
        ].join(COMMAND_JOIN);
    });
    const result = await shell.run(commands.join(COMMAND_JOIN));
    if (result.code !== 0) {
        throw new Error(MODULE_BUILD_FAILED + result.stderr);
    }
};

export const restoreModules = async function restoreModules(
    shell: Shell,
    modules: readonly SourceModule[],
): Promise<void> {
    const commands = modules.flatMap((module) =>
        [objectPath(module), markerPath(module)].map(
            (path) =>
                `if [ -f ${path}${PREVIOUS_SUFFIX} ]; then sudo mv ${path}${PREVIOUS_SUFFIX} ${path}; else sudo rm -f ${path}; fi`,
        ),
    );
    await shell.run(commands.join("; "));
};

export const settleModules = async function settleModules(
    shell: Shell,
    modules: readonly SourceModule[],
): Promise<void> {
    const paths = modules.flatMap((module) => [objectPath(module), markerPath(module)]);
    await shell.run(`sudo rm -f ${paths.map((path) => path + PREVIOUS_SUFFIX).join(" ")}`);
};
```
