Merge pull request #1507 from TriliumNext/feature/windows_signing_v3

Windows Signing
This commit is contained in:
Elian Doran 2025-03-25 17:51:12 +02:00 committed by GitHub
commit 90ab31329e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 99 additions and 211 deletions

View File

@ -34,6 +34,7 @@ npm-debug.log
# exceptions # exceptions
!/bin/copy-dist.ts !/bin/copy-dist.ts
!/bin/electron-forge/sign-windows.cjs
# temporary exception to make copy-dist inside Docker build not fail # temporary exception to make copy-dist inside Docker build not fail
# TriliumNextTODO: make copy-dist *not* requiring to copy this file for builds other than electron-forge # TriliumNextTODO: make copy-dist *not* requiring to copy this file for builds other than electron-forge

View File

@ -8,8 +8,11 @@ inputs:
arch: arch:
description: "The architecture to build for: x64, arm64" description: "The architecture to build for: x64, arm64"
required: true required: true
extension: shell:
description: "Platform specific extensions to copy in the output: dmg, deb, rpm, exe, zip" description: "Which shell to use"
required: true
forge_platform:
description: "The --platform to pass to Electron Forge"
required: true required: true
runs: runs:
@ -38,21 +41,21 @@ runs:
- name: Verify certificates - name: Verify certificates
if: inputs.os == 'macos' if: inputs.os == 'macos'
shell: bash shell: ${{ inputs.shell }}
run: | run: |
echo "Available signing identities:" echo "Available signing identities:"
security find-identity -v -p codesigning build.keychain security find-identity -v -p codesigning build.keychain
- name: Set up Python and other macOS dependencies - name: Set up Python and other macOS dependencies
if: ${{ inputs.os == 'macos' }} if: ${{ inputs.os == 'macos' }}
shell: bash shell: ${{ inputs.shell }}
run: | run: |
brew install python-setuptools brew install python-setuptools
brew install create-dmg brew install create-dmg
- name: Install dependencies for RPM and Flatpak package building - name: Install dependencies for RPM and Flatpak package building
if: ${{ inputs.os == 'linux' }} if: ${{ inputs.os == 'linux' }}
shell: bash shell: ${{ inputs.shell }}
run: | run: |
sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
@ -62,39 +65,32 @@ runs:
# Build setup # Build setup
- name: Install dependencies - name: Install dependencies
shell: bash shell: ${{ inputs.shell }}
run: npm ci run: npm ci
- name: Update build info - name: Update build info
shell: bash shell: ${{ inputs.shell }}
run: npm run chore:update-build-info run: npm run chore:update-build-info
# Critical debugging configuration # Critical debugging configuration
- name: Run electron-forge build with enhanced logging - name: Run electron-forge build with enhanced logging
shell: bash shell: ${{ inputs.shell }}
env: env:
# Pass through required environment variables for signing and notarization # Pass through required environment variables for signing and notarization
APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }}
APPLE_ID: ${{ env.APPLE_ID }} APPLE_ID: ${{ env.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ env.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ env.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ env.WINDOWS_SIGN_EXECUTABLE }}
TRILIUM_ARTIFACT_NAME_HINT: TriliumNextNotes ${{ inputs.os }} ${{ inputs.arch }}
run: | run: |
# Map OS names to Electron Forge platform names
if [ "${{ inputs.os }}" = "macos" ]; then
PLATFORM="darwin"
elif [ "${{ inputs.os }}" = "windows" ]; then
PLATFORM="win32"
else
PLATFORM="${{ inputs.os }}"
fi
npm run electron-forge:make -- \ npm run electron-forge:make -- \
--arch=${{ inputs.arch }} \ --arch=${{ inputs.arch }} \
--platform=$PLATFORM --platform=${{ inputs.forge_platform }}
# Add DMG signing step # Add DMG signing step
- name: Sign DMG - name: Sign DMG
if: inputs.os == 'macos' if: inputs.os == 'macos'
shell: bash shell: ${{ inputs.shell }}
run: | run: |
echo "Signing DMG file..." echo "Signing DMG file..."
dmg_file=$(find ./dist -name "*.dmg" -print -quit) dmg_file=$(find ./dist -name "*.dmg" -print -quit)
@ -119,7 +115,7 @@ runs:
- name: Verify code signing - name: Verify code signing
if: inputs.os == 'macos' if: inputs.os == 'macos'
shell: bash shell: ${{ inputs.shell }}
run: | run: |
echo "Verifying code signing for all artifacts..." echo "Verifying code signing for all artifacts..."
@ -165,49 +161,3 @@ runs:
echo "Found ZIP: $zip_file" echo "Found ZIP: $zip_file"
echo "Note: ZIP files are not code signed, but their contents should be" echo "Note: ZIP files are not code signed, but their contents should be"
fi fi
- name: Prepare artifacts
shell: bash
run: |
mkdir -p upload
if [ "${{ inputs.os }}" = "macos" ]; then
# For macOS, we need to look in specific directories based on the maker
echo "Collecting macOS artifacts..."
# Look for DMG files recursively
echo "Looking for DMG files..."
dmg_file=$(find ./dist -name "*.dmg" -print -quit)
if [ -n "$dmg_file" ]; then
echo "Found DMG: $dmg_file"
cp "$dmg_file" "upload/TriliumNextNotes-${{ github.ref_name }}-macos-${{ inputs.arch }}.dmg"
else
echo "Warning: No DMG file found"
fi
# Look for ZIP files recursively
echo "Looking for ZIP files..."
zip_file=$(find ./dist -name "*.zip" -print -quit)
if [ -n "$zip_file" ]; then
echo "Found ZIP: $zip_file"
cp "$zip_file" "upload/TriliumNextNotes-${{ github.ref_name }}-macos-${{ inputs.arch }}.zip"
else
echo "Warning: No ZIP file found"
fi
else
# For other platforms, use the existing logic but with better error handling
echo "Collecting artifacts for ${{ inputs.os }}..."
for ext in ${{ inputs.extension }}; do
echo "Looking for .$ext files..."
file=$(find ./dist -name "*.$ext" -print -quit)
if [ -n "$file" ]; then
echo "Found $file for extension $ext"
cp "$file" "upload/TriliumNextNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }}.$ext"
else
echo "Warning: No file found with extension .$ext"
fi
done
fi
echo "Final contents of upload directory:"
ls -la upload/

View File

@ -1,93 +0,0 @@
name: Main
on:
push:
branches:
- "feature/update**"
- "feature/server_esm**"
paths-ignore:
- "docs/**"
- ".github/workflows/main-docker.yml"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
make-electron:
name: Make Electron
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
os:
- name: macos
image: macos-latest
extension: [dmg, zip]
- name: linux
image: ubuntu-latest
extension: [deb, rpm, zip, flatpak]
- name: windows
image: windows-latest
extension: [exe, zip]
runs-on: ${{ matrix.os.image }}
steps:
- uses: actions/checkout@v4
- name: Set up node & dependencies
uses: actions/setup-node@v4
with:
node-version: 20
- name: Run the build
uses: ./.github/actions/build-electron
with:
os: ${{ matrix.os.name }}
arch: ${{ matrix.arch }}
extension: ${{ matrix.os.extension }}
env:
APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
APPLE_INSTALLER_CERTIFICATE_BASE64: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_BASE64 }}
APPLE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_INSTALLER_CERTIFICATE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
# Clean up keychain after build
- name: Clean up keychain
if: matrix.os.name == 'macos' && always()
run: |
security delete-keychain build.keychain
- name: Publish artifacts
uses: actions/upload-artifact@v4
with:
name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}.zip
path: upload/*.zip
- name: Publish installer artifacts
uses: actions/upload-artifact@v4
with:
name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}.${{matrix.os.extension}}
path: upload/*.${{ matrix.os.extension }}
build_linux_server:
name: Build Linux Server
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
include:
- arch: x64
runs-on: ubuntu-latest
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- name: Run the build
uses: ./.github/actions/build-server
with:
arch: ${{ matrix.arch }}
- uses: actions/upload-artifact@v4
with:
name: TriliumNextNotes linux server ${{ matrix.arch }}
path: upload/TriliumNextNotes-linux-${{ matrix.arch }}-${{ github.ref_name }}.tar.xz

View File

@ -23,13 +23,16 @@ jobs:
os: os:
- name: macos - name: macos
image: macos-latest image: macos-latest
extension: [dmg, zip] shell: bash
forge_platform: darwin
- name: linux - name: linux
image: ubuntu-latest image: ubuntu-latest
extension: [deb, rpm, zip, flatpak] shell: bash
forge_platform: linux
- name: windows - name: windows
image: windows-latest image: win-signing
extension: [exe, zip] shell: cmd
forge_platform: win32
runs-on: ${{ matrix.os.image }} runs-on: ${{ matrix.os.image }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -38,7 +41,6 @@ jobs:
with: with:
node-version: 20 node-version: 20
- name: Install dependencies - name: Install dependencies
shell: bash
run: npm ci run: npm ci
- name: Update nightly version - name: Update nightly version
run: npm run chore:ci-update-nightly-version run: npm run chore:ci-update-nightly-version
@ -47,7 +49,8 @@ jobs:
with: with:
os: ${{ matrix.os.name }} os: ${{ matrix.os.name }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
extension: ${{ join(matrix.os.extension, ' ') }} shell: ${{ matrix.os.shell }}
forge_platform: ${{ matrix.os.forge_platform }}
env: env:
APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
@ -56,6 +59,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
- name: Publish release - name: Publish release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
@ -69,35 +73,9 @@ jobs:
tag_name: nightly tag_name: nightly
name: Nightly Build name: Nightly Build
nightly-server: - name: Publish artifacts
name: Deploy server nightly uses: actions/upload-artifact@v4
strategy: if: ${{ github.event_name == 'pull_request' }}
fail-fast: false
matrix:
arch: [x64, arm64]
include:
- arch: x64
runs-on: ubuntu-latest
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }}
steps:
- uses: actions/checkout@v4
- name: Run the build
uses: ./.github/actions/build-server
with: with:
os: linux name: TriliumNextNotes ${{ matrix.os.name }} ${{ matrix.arch }}
arch: ${{ matrix.arch }} path: upload
- name: Publish release
uses: softprops/action-gh-release@v2
if: ${{ github.event_name != 'pull_request' }}
with:
make_latest: false
prerelease: true
draft: false
fail_on_unmatched_files: true
files: upload/*.*
tag_name: nightly
name: Nightly Build

View File

@ -20,13 +20,16 @@ jobs:
os: os:
- name: macos - name: macos
image: macos-latest image: macos-latest
extension: [dmg, zip] shell: bash
forge_platform: darwin
- name: linux - name: linux
image: ubuntu-latest image: ubuntu-latest
extension: [deb, rpm, zip, flatpak] shell: bash
forge_platform: linux
- name: windows - name: windows
image: windows-latest image: win-signing
extension: [exe, zip] shell: cmd
forge_platform: win32
runs-on: ${{ matrix.os.image }} runs-on: ${{ matrix.os.image }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -39,7 +42,8 @@ jobs:
with: with:
os: ${{ matrix.os.name }} os: ${{ matrix.os.name }}
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
extension: ${{ join(matrix.os.extension, ' ') }} shell: ${{ matrix.os.shell }}
forge_platform: ${{ matrix.os.forge_platform }}
env: env:
APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }} APPLE_APP_CERTIFICATE_BASE64: ${{ secrets.APPLE_APP_CERTIFICATE_BASE64 }}
APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }} APPLE_APP_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_APP_CERTIFICATE_PASSWORD }}
@ -48,6 +52,7 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
WINDOWS_SIGN_EXECUTABLE: ${{ vars.WINDOWS_SIGN_EXECUTABLE }}
- name: Publish release - name: Publish release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2

View File

@ -21,6 +21,7 @@ function copyNodeModuleFileOrFolder(source: string) {
try { try {
const assetsToCopy = new Set([ const assetsToCopy = new Set([
"./node_modules",
"./images", "./images",
"./libraries", "./libraries",
"./translations", "./translations",
@ -33,6 +34,7 @@ try {
"./forge.config.cjs", "./forge.config.cjs",
"./bin/tpl/", "./bin/tpl/",
"./bin/electron-forge/desktop.ejs", "./bin/electron-forge/desktop.ejs",
"./bin/electron-forge/sign-windows.cjs",
"./src/views/", "./src/views/",
"./src/etapi/etapi.openapi.yaml", "./src/etapi/etapi.openapi.yaml",
"./src/routes/api/openapi.json", "./src/routes/api/openapi.json",

View File

@ -0,0 +1,14 @@
const child_process = require("child_process");
module.exports = function (filePath) {
const { WINDOWS_SIGN_EXECUTABLE } = process.env;
if (!WINDOWS_SIGN_EXECUTABLE) {
console.warn("[Sign] Skip signing due to missing environment variable.");
return;
}
const command = `${WINDOWS_SIGN_EXECUTABLE} --executable "${filePath}"`;
console.log(`[Sign] ${command}`);
child_process.execSync(command);
}

View File

@ -2,13 +2,17 @@ const path = require("path");
const fs = require("fs-extra"); const fs = require("fs-extra");
const APP_NAME = "TriliumNext Notes"; const APP_NAME = "TriliumNext Notes";
const BIN_PATH = path.normalize("./bin/electron-forge");
const extraResourcesForPlatform = getExtraResourcesForPlatform(); const extraResourcesForPlatform = getExtraResourcesForPlatform();
const baseLinuxMakerConfigOptions = { const baseLinuxMakerConfigOptions = {
icon: "./images/app-icons/png/128x128.png", icon: "./images/app-icons/png/128x128.png",
desktopTemplate: path.resolve("./bin/electron-forge/desktop.ejs"), desktopTemplate: path.resolve(path.join(BIN_PATH, "desktop.ejs")),
categories: ["Office", "Utility"] categories: ["Office", "Utility"]
}; };
const windowsSignConfiguration = process.env.WINDOWS_SIGN_EXECUTABLE ? {
hookModulePath: path.join(BIN_PATH, "sign-windows.cjs")
} : undefined;
module.exports = { module.exports = {
// we run electron-forge inside the ./build folder, // we run electron-forge inside the ./build folder,
@ -26,6 +30,7 @@ module.exports = {
appleIdPassword: process.env.APPLE_ID_PASSWORD, appleIdPassword: process.env.APPLE_ID_PASSWORD,
teamId: process.env.APPLE_TEAM_ID teamId: process.env.APPLE_TEAM_ID
}, },
windowsSign: windowsSignConfiguration,
extraResource: [ extraResource: [
// All resources should stay in Resources directory for macOS // All resources should stay in Resources directory for macOS
...(process.platform === "darwin" ? [] : extraResourcesForPlatform), ...(process.platform === "darwin" ? [] : extraResourcesForPlatform),
@ -105,7 +110,8 @@ module.exports = {
config: { config: {
iconUrl: "https://raw.githubusercontent.com/TriliumNext/Notes/develop/images/app-icons/icon.ico", iconUrl: "https://raw.githubusercontent.com/TriliumNext/Notes/develop/images/app-icons/icon.ico",
setupIcon: "./images/app-icons/win/setup.ico", setupIcon: "./images/app-icons/win/setup.ico",
loadingGif: "./images/app-icons/win/setup-banner.gif" loadingGif: "./images/app-icons/win/setup-banner.gif",
windowsSign: windowsSignConfiguration
} }
}, },
{ {
@ -129,7 +135,33 @@ module.exports = {
name: "@electron-forge/plugin-auto-unpack-natives", name: "@electron-forge/plugin-auto-unpack-natives",
config: {} config: {}
} }
] ],
hooks: {
postMake(_, makeResults) {
const outputDir = path.join(__dirname, "..", "upload");
fs.mkdirp(outputDir);
for (const makeResult of makeResults) {
for (const artifactPath of makeResult.artifacts) {
// Ignore certain artifacts.
let fileName = path.basename(artifactPath);
const extension = path.extname(fileName);
if (fileName === "RELEASES" || extension === ".nupkg") {
continue;
}
// Override the extension for the CI.
const { TRILIUM_ARTIFACT_NAME_HINT } = process.env;
if (TRILIUM_ARTIFACT_NAME_HINT) {
fileName = TRILIUM_ARTIFACT_NAME_HINT + extension;
}
const outputPath = path.join(outputDir, fileName);
console.log(`[Artifact] ${artifactPath} -> ${outputPath}`);
fs.copyFile(artifactPath, outputPath);
}
}
}
}
}; };
function getExtraResourcesForPlatform() { function getExtraResourcesForPlatform() {

View File

@ -38,9 +38,9 @@
"electron:switch": "electron-rebuild", "electron:switch": "electron-rebuild",
"docs:edit": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_ENV=dev TRILIUM_PORT=37741 electron ./electron-docs-main.ts .", "docs:edit": "cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_ENV=dev TRILIUM_PORT=37741 electron ./electron-docs-main.ts .",
"docs:edit-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_PORT=37741 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-docs-main.ts .\"", "docs:edit-nix": "electron-rebuild --version 33.3.1 && cross-env NODE_OPTIONS=\"--import tsx\" TRILIUM_DATA_DIR=./data-docs TRILIUM_PORT=37741 TRILIUM_ENV=dev nix-shell -p electron_33 --run \"electron ./electron-docs-main.ts .\"",
"electron-forge:prepare": "npm run build:prepare-dist && cp -r node_modules ./build", "electron-forge:prepare": "npm run build:prepare-dist",
"electron-forge:start": "npm run electron-forge:prepare && cd ./build && electron-forge start", "electron-forge:start": "npm run electron-forge:prepare && cd ./build && electron-forge start",
"electron-forge:make": "npm run electron-forge:prepare && cd ./build && electron-forge make", "electron-forge:make": "npm run electron-forge:prepare && cross-env DEBUG=electron-windows-installer:* electron-forge make ./build",
"electron-forge:package": "npm run electron-forge:prepare && cd ./build && electron-forge package", "electron-forge:package": "npm run electron-forge:prepare && cd ./build && electron-forge package",
"docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts", "docs:build-backend": "rimraf ./docs/backend_api && typedoc ./docs/backend_api src/becca/entities/*.ts src/services/backend_script_api.ts src/services/sql.ts",
"docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js", "docs:build-frontend": "rimraf ./docs/frontend_api && jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/basic_widget.js src/public/app/widgets/note_context_aware_widget.js src/public/app/widgets/right_panel_widget.js",

View File

@ -76,6 +76,10 @@ async function backupNow(name: string) {
return await syncMutexService.doExclusively(async () => { return await syncMutexService.doExclusively(async () => {
const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`; const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`;
if (!fs.existsSync(dataDir.BACKUP_DIR)) {
fs.mkdirSync(dataDir.BACKUP_DIR, 0o700);
}
await sql.copyDatabase(backupFile); await sql.copyDatabase(backupFile);
log.info(`Created backup at ${backupFile}`); log.info(`Created backup at ${backupFile}`);
@ -83,11 +87,6 @@ async function backupNow(name: string) {
return backupFile; return backupFile;
}); });
} }
if (!fs.existsSync(dataDir.BACKUP_DIR)) {
fs.mkdirSync(dataDir.BACKUP_DIR, 0o700);
}
export default { export default {
getExistingBackups, getExistingBackups,
backupNow, backupNow,