# core/steps/backup.step.ts

> 91 lines of code and 14 definitions.

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

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

## Definitions

- `restoreSite` (lexical_declaration, line 86, exported)
- `backupDirectory` (lexical_declaration, line 34)
- `pruneArchives` (lexical_declaration, line 73, exported)
- `staged` (lexical_declaration, line 40)
- `archiveSite` (lexical_declaration, line 44, exported)
- `name` (lexical_declaration, line 46, exported)
- `result` (lexical_declaration, line 48, exported)
- `local` (lexical_declaration, line 59, exported)
- `download` (lexical_declaration, line 60, exported)
- `isArchive` (lexical_declaration, line 69)
- `directory` (lexical_declaration, line 75, exported)
- `archives` (lexical_declaration, line 76, exported)
- `remote` (lexical_declaration, line 88, exported)
- `upload` (lexical_declaration, line 91, exported)

## Contained in

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

## Used by

- [runtime/entrypoints/deployment.entrypoint.ts](https://banes-lab.com/source/deploy/runtime/entrypoints/deployment.entrypoint.ts.md)

## Linked from

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

## Source

```typescript
import {
    ARCHIVE_FAILED,
    ARCHIVE_UNIT,
    BACKED_UP,
    BACKING_UP,
    BACKUP_REMOVED,
    BACKUP_SAVED,
    DOWNLOADING_BACKUP,
    DOWNLOAD_LABEL,
    EXTRACTING,
    PRUNED,
    PRUNING,
    REMOTE_BACKUP_REMOVED,
    REUPLOADING,
    REUPLOAD_LABEL,
    ROLLED_BACK,
    ROLLING_BACK,
} from "#configuration/strings/deployment.strings";
import {
    ARCHIVE_SUFFIX,
    BACKUP_PREFIX,
    BACKUP_RETENTION,
    PATH_SEPARATOR,
    REMOTE_SITE,
    REMOTE_STAGING,
} from "#configuration/constants/deployment.constants";
import type { Journal, Shell } from "#types/deployment.types";
import { mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
import { absolutePath } from "@ssot/paths";
import { createByteProgress } from "#core/reporters/journal.reporter";
import { join } from "node:path";
import { stamp } from "#core/converters/text.converter";

const backupDirectory = function backupDirectory(): string {
    const directory = absolutePath("app.backups");
    mkdirSync(directory, { recursive: true });
    return directory;
};

const staged = function staged(name: string): string {
    return REMOTE_STAGING + PATH_SEPARATOR + name;
};

export const archiveSite = async function archiveSite(shell: Shell, journal: Journal): Promise<string> {
    journal.log(BACKING_UP);
    const name = BACKUP_PREFIX + stamp() + ARCHIVE_SUFFIX;
    const remote = staged(name);
    const result = await shell.run(
        `mkdir -p ${REMOTE_SITE} ${REMOTE_STAGING} && tar -czf ${remote} -C ${REMOTE_SITE} .`,
    );
    if (result.code !== 0) {
        throw new Error(ARCHIVE_FAILED + result.stderr);
    }
    if (result.stderr.length > 0) {
        journal.log(result.stderr);
    }
    journal.mark(BACKED_UP);
    journal.log(DOWNLOADING_BACKUP);
    const local = join(backupDirectory(), name);
    const download = createByteProgress(journal, DOWNLOAD_LABEL, ARCHIVE_UNIT);
    await shell.download(local, remote, download.step);
    download.finish();
    journal.mark(BACKUP_SAVED + local);
    await shell.run(`rm -f ${remote}`);
    journal.log(REMOTE_BACKUP_REMOVED);
    return name;
};

const isArchive = function isArchive(name: string): boolean {
    return name.startsWith(BACKUP_PREFIX) && name.endsWith(ARCHIVE_SUFFIX);
};

export const pruneArchives = function pruneArchives(journal: Journal): void {
    journal.log(PRUNING + String(BACKUP_RETENTION));
    const directory = backupDirectory();
    const archives = readdirSync(directory)
        .filter(isArchive)
        .toSorted((a, b) => statSync(join(directory, b)).mtimeMs - statSync(join(directory, a)).mtimeMs);
    for (const stale of archives.slice(BACKUP_RETENTION)) {
        unlinkSync(join(directory, stale));
        journal.log(BACKUP_REMOVED + stale);
    }
    journal.mark(PRUNED);
};

export const restoreSite = async function restoreSite(shell: Shell, journal: Journal, name: string): Promise<void> {
    journal.log(ROLLING_BACK);
    const remote = staged(name);
    journal.log(REUPLOADING + name);
    await shell.run(`mkdir -p ${REMOTE_STAGING}`);
    const upload = createByteProgress(journal, REUPLOAD_LABEL, ARCHIVE_UNIT);
    await shell.upload(join(backupDirectory(), name), remote, upload.step);
    upload.finish();
    journal.log(EXTRACTING);
    await shell.run(`mkdir -p ${REMOTE_SITE} && cd ${REMOTE_SITE} && tar -xzf ${remote} && rm -f ${remote}`);
    journal.log(ROLLED_BACK);
};
```
