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
29 changes: 0 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,32 +200,3 @@ The internal queries that are ran before the queries in ```initSQLString``` are
Default: process.arch

Description: The MySQL binary architecture to execute. MySQL does not offer server builds for Windows on ARM, so to get this package working on Windows on ARM, set the arch option to "x64" and Windows will emulate MySQL.

***
### :warning: Internal Options :warning:

The following options are only meant for internal use (such as for testing this package or the internals for running this package via the CLI). Their behaviour may change or they may get removed between major/minor/patch versions and they are not to be considered stable. The options below will not follow Semantic Versioning so it is advised to not use them.

- `_DO_NOT_USE_deleteDBAfterStopped: boolean`

Default: true

Description: Changes whether or not the database will be deleted after it has been stopped. If set to `true`, the database WILL be deleted after it has been stopped.

- `_DO_NOT_USE_dbPath: string`

Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory and UUID with a UUIDv4 without seperating dashes).

Description: The folder to store database-related data in

- `_DO_NOT_USE_binaryDirectoryPath: string`

Default: `TMPDIR/mysqlmsn/binaries` (replacing TMPDIR with the OS temp directory)

Description: The folder to store the MySQL binaries when they are downloaded from the CDN.

- `_DO_NOT_USE_cli: boolean`

Default: ```false``` if the package is not being executed via the CLI and ```true``` if it is.

Description: If set to ```true```, this enables certain CLI-only functionality. For example, when ran by the CLI, logging a message to the console when a shutdown signal (like CTRL + C) has been received and the MySQL database is shutting down and getting deleted.
5 changes: 2 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import { ServerOptions } from "../types";

function main() {
const definedOptions = process.argv.filter((option) => option.startsWith('--'))
const options: ServerOptions = {
_DO_NOT_USE_cli: true
}
const options: ServerOptions = {}
process.env.mysqlmsn_internal_DO_NOT_USE_cli = 'true'
for (const opt of definedOptions) {
if (!DEFAULT_OPTIONS_KEYS.includes(opt.replace('--', ''))) {
console.error(`Option ${opt} is not a valid option.`)
Expand Down
39 changes: 12 additions & 27 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@ export const DEFAULT_OPTIONS_GENERATOR: () => InternalServerOptions = () => ({
xPort: 0,
downloadRetries: 10,
initSQLString: '',
arch: process.arch,
_DO_NOT_USE_deleteDBAfterStopped: true,
//mysqlmsn = MySQL Memory Server Node.js
_DO_NOT_USE_dbPath: normalizePath(`${tmpdir()}/mysqlmsn/dbs/${randomUUID().replace(/-/g, '')}`),
_DO_NOT_USE_binaryDirectoryPath: `${tmpdir()}/mysqlmsn/binaries`,
_DO_NOT_USE_cli: false
arch: process.arch
});

export const DEFAULT_OPTIONS_KEYS = Object.freeze(Object.keys(DEFAULT_OPTIONS_GENERATOR()))
Expand All @@ -36,7 +31,17 @@ export const LOG_LEVELS = {
'ERROR': 2
} as const;

export const INTERNAL_OPTIONS = ['_DO_NOT_USE_deleteDBAfterStopped', '_DO_NOT_USE_dbPath', '_DO_NOT_USE_binaryDirectoryPath', '_DO_NOT_USE_cli'] as const;
const internalOptions = {
deleteDBAfterStopped: 'true',
//mysqlmsn = MySQL Memory Server Node.js
dbPath: normalizePath(`${tmpdir()}/mysqlmsn/dbs/${randomUUID().replace(/-/g, '')}`),
binaryDirectoryPath: `${tmpdir()}/mysqlmsn/binaries`,
cli: 'false'
}

export function getInternalEnvVariable(envVar: keyof typeof internalOptions): string {
return process.env['mysqlmsn_internal_DO_NOT_USE_' + envVar] || internalOptions[envVar]
}

const allowedArches = ['x64', 'arm64', undefined]
export const OPTION_TYPE_CHECKS: OptionTypeChecks = {
Expand Down Expand Up @@ -109,25 +114,5 @@ export const OPTION_TYPE_CHECKS: OptionTypeChecks = {
check: (opt: any) => allowedArches.includes(opt),
errorMessage: `Option arch must be either of the following: ${allowedArches.join(', ')}`,
definedType: 'string'
},
_DO_NOT_USE_deleteDBAfterStopped: {
check: (opt: any) => opt === undefined || typeof opt === 'boolean',
errorMessage: 'Option _DO_NOT_USE_deleteDBAfterStopped must be either undefined or a boolean.',
definedType: 'boolean'
},
_DO_NOT_USE_dbPath: {
check: (opt: any) => opt === undefined || typeof opt === 'string',
errorMessage: 'Option _DO_NOT_USE_dbPath must be either undefined or a string.',
definedType: 'string'
},
_DO_NOT_USE_binaryDirectoryPath: {
check: (opt: any) => opt === undefined || typeof opt === 'string',
errorMessage: 'Option _DO_NOT_USE_binaryDirectoryPath must be either undefined or a string.',
definedType: 'string'
},
_DO_NOT_USE_cli: {
check: (opt: any) => opt === undefined || typeof opt === 'boolean',
errorMessage: 'Option _DO_NOT_USE_cli must be either undefined or a boolean.',
definedType: 'boolean'
}
} as const;
10 changes: 2 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,16 @@ import Logger from './libraries/Logger'
import * as os from 'node:os'
import Executor from "./libraries/Executor"
import { satisfies, lt, coerce } from "semver"
import { BinaryInfo, InternalServerOptions, ServerOptions } from '../types'
import { BinaryInfo, ServerOptions } from '../types'
import getBinaryURL from './libraries/Version'
import MySQLVersions from './versions.json'
import { downloadBinary } from './libraries/Downloader'
import { MIN_SUPPORTED_MYSQL, DEFAULT_OPTIONS_KEYS, OPTION_TYPE_CHECKS, INTERNAL_OPTIONS, DEFAULT_OPTIONS_GENERATOR } from './constants'
import { MIN_SUPPORTED_MYSQL, DEFAULT_OPTIONS_KEYS, OPTION_TYPE_CHECKS, DEFAULT_OPTIONS_GENERATOR } from './constants'

export async function createDB(opts?: ServerOptions) {
const suppliedOpts = opts || {};
const suppliedOptsKeys = Object.keys(suppliedOpts);

for (const opt of INTERNAL_OPTIONS) {
if (suppliedOptsKeys.includes(opt)) {
console.warn(`[ mysql-memory-server - Options WARN ]: Creating MySQL database with option ${opt}. This is considered unstable and should not be used externally. Please consider removing this option.`)
}
}

const options = DEFAULT_OPTIONS_GENERATOR();

for (const opt of suppliedOptsKeys) {
Expand Down
3 changes: 2 additions & 1 deletion src/libraries/Downloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { randomUUID } from 'crypto';
import { execFile } from 'child_process';
import { BinaryInfo, InternalServerOptions } from '../../types';
import { lockFile, waitForLock } from './FileLock';
import { getInternalEnvVariable } from '../constants';

function getZipData(entry: AdmZip.IZipEntry): Promise<Buffer> {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -181,7 +182,7 @@ function extractBinary(url: string, archiveLocation: string, extractedLocation:
export function downloadBinary(binaryInfo: BinaryInfo, options: InternalServerOptions, logger: Logger): Promise<string> {
return new Promise(async (resolve, reject) => {
const {url, version} = binaryInfo;
const dirpath = options._DO_NOT_USE_binaryDirectoryPath
const dirpath = getInternalEnvVariable('binaryDirectoryPath')
logger.log('Binary path:', dirpath)
await fsPromises.mkdir(dirpath, {recursive: true})

Expand Down
19 changes: 10 additions & 9 deletions src/libraries/Executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {normalize as normalizePath, resolve as resolvePath} from 'path'
import { lockFile, waitForLock } from "./FileLock";
import { onExit } from "signal-exit";
import { randomUUID } from "crypto";
import { getInternalEnvVariable } from "../constants";

class Executor {
logger: Logger;
Expand Down Expand Up @@ -117,7 +118,7 @@ class Executor {
if (portIssue || xPortIssue) {
this.logger.log('Error log when exiting for port in use error:', errorLog)
try {
await this.#deleteDatabaseDirectory(options._DO_NOT_USE_dbPath)
await this.#deleteDatabaseDirectory(getInternalEnvVariable('dbPath'))
} catch (e) {
this.logger.error(e)
return reject(`MySQL failed to listen on a certain port. To restart MySQL with a different port, the database directory needed to be deleted. An error occurred while deleting the database directory. Aborting. The error was: ${e}`)
Expand All @@ -126,7 +127,7 @@ class Executor {
}

try {
if (options._DO_NOT_USE_deleteDBAfterStopped) {
if (getInternalEnvVariable('deleteDBAfterStopped') === 'true') {
await this.#deleteDatabaseDirectory(dbPath)
}
} catch (e) {
Expand Down Expand Up @@ -421,7 +422,7 @@ class Executor {

this.logger.log('Writing init file')

await fsPromises.writeFile(`${options._DO_NOT_USE_dbPath}/init.sql`, initText, {encoding: 'utf8'})
await fsPromises.writeFile(`${getInternalEnvVariable('dbPath')}/init.sql`, initText, {encoding: 'utf8'})

this.logger.log('Finished writing init file')
}
Expand All @@ -430,15 +431,15 @@ class Executor {
this.version = installedMySQLBinary.version
this.versionInstalledOnSystem = installedMySQLBinary.installedOnSystem
this.removeExitHandler = onExit(() => {
if (options._DO_NOT_USE_cli) {
if (getInternalEnvVariable('cli') === 'true') {
console.log('\nShutting down the ephemeral MySQL database and cleaning all related files...')
}

this.DBDestroySignal.abort()

if (options._DO_NOT_USE_deleteDBAfterStopped) {
if (getInternalEnvVariable('deleteDBAfterStopped') === 'true') {
try {
fs.rmSync(options._DO_NOT_USE_dbPath, {recursive: true, maxRetries: 50, force: true})
fs.rmSync(getInternalEnvVariable('dbPath'), {recursive: true, maxRetries: 50, force: true})
} catch (e) {
this.logger.error('An error occurred while deleting database directory path:', e)
}
Expand All @@ -453,14 +454,14 @@ class Executor {
}
}

if (options._DO_NOT_USE_cli) {
if (getInternalEnvVariable('cli') === 'true') {
console.log('Shutdown and cleanup is complete.')
}
})

let retries = 0;

const datadir = normalizePath(`${options._DO_NOT_USE_dbPath}/data`)
const datadir = normalizePath(`${getInternalEnvVariable('dbPath')}/data`)

do {
await this.#setupDataDirectories(options, installedMySQLBinary.path, datadir, true);
Expand All @@ -472,7 +473,7 @@ class Executor {

try {
this.logger.log('Starting MySQL process')
const resolved = await this.#startMySQLProcess(options, port, mySQLXPort, datadir, options._DO_NOT_USE_dbPath, installedMySQLBinary.path)
const resolved = await this.#startMySQLProcess(options, port, mySQLXPort, datadir, getInternalEnvVariable('dbPath'), installedMySQLBinary.path)
this.logger.log('Starting process was successful')
return resolved
} catch (e) {
Expand Down
10 changes: 5 additions & 5 deletions stress-tests/stress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ const dbPath = normalize(GitHubActionsTempFolder + '/dbs')
const binaryPath = normalize(GitHubActionsTempFolder + '/binaries')

for (let i = 0; i < 100; i++) {
test.concurrent(`if run ${i} is successful`, async () => {
Error.stackTraceLimit = Infinity
test(`if run ${i} is successful`, async () => {
console.log('CI:', process.env.useCIDBPath)

process.env.mysqlmsn_internal_DO_NOT_USE_deleteDBAfterStopped = String(!process.env.useCIDBPath)

const options: ServerOptions = {
username: 'dbuser',
logLevel: 'LOG',
_DO_NOT_USE_deleteDBAfterStopped: !process.env.useCIDBPath,
ignoreUnsupportedSystemVersion: true
}

if (process.env.useCIDBPath) {
options._DO_NOT_USE_dbPath = `${dbPath}/${i}`
options._DO_NOT_USE_binaryDirectoryPath = binaryPath
process.env.mysqlmsn_internal_DO_NOT_USE_dbPath = `${dbPath}/${i}`
process.env.mysqlmsn_internal_DO_NOT_USE_binaryDirectoryPath = binaryPath
}

const db = await createDB(options)
Expand Down
9 changes: 4 additions & 5 deletions tests/sql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,23 @@ const dbPath = normalize(GitHubActionsTempFolder + '/dbs')
const binaryPath = normalize(GitHubActionsTempFolder + '/binaries')

beforeEach(async () => {
Error.stackTraceLimit = Infinity
process.env.mysqlmsn_internal_DO_NOT_USE_deleteDBAfterStopped = String(!process.env.useCIDBPath)

const options: ServerOptions = {
username: 'root',
logLevel: 'LOG',
_DO_NOT_USE_deleteDBAfterStopped: !process.env.useCIDBPath,
ignoreUnsupportedSystemVersion: true
}

if (process.env.useCIDBPath) {
options._DO_NOT_USE_dbPath = `${dbPath}/${randomUUID()}`
options._DO_NOT_USE_binaryDirectoryPath = binaryPath
process.env.mysqlmsn_internal_DO_NOT_USE_dbPath = `${dbPath}/${randomUUID()}`
process.env.mysqlmsn_internal_DO_NOT_USE_binaryDirectoryPath = binaryPath
}

db = await createDB(options)
})

afterEach(async () => {
Error.stackTraceLimit = Infinity
await db.stop();
})

Expand Down
10 changes: 5 additions & 5 deletions tests/versions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ jest.setTimeout(500_000);

for (const version of versions) {
for (const username of usernames) {
test.concurrent(`running on version ${version} with username ${username}`, async () => {
Error.stackTraceLimit = Infinity
test(`running on version ${version} with username ${username}`, async () => {
process.env.mysqlmsn_internal_DO_NOT_USE_deleteDBAfterStopped = String(!process.env.useCIDBPath)

const options: ServerOptions = {
version,
dbName: 'testingdata',
username: username,
logLevel: 'LOG',
_DO_NOT_USE_deleteDBAfterStopped: !process.env.useCIDBPath,
ignoreUnsupportedSystemVersion: true,
initSQLString: 'CREATE DATABASE mytestdb;'
}

if (process.env.useCIDBPath) {
options._DO_NOT_USE_dbPath = `${dbPath}/${randomUUID()}`
options._DO_NOT_USE_binaryDirectoryPath = binaryPath
process.env.mysqlmsn_internal_DO_NOT_USE_dbPath = `${dbPath}/${randomUUID()}`
process.env.mysqlmsn_internal_DO_NOT_USE_binaryDirectoryPath = binaryPath
}

const db = await createDB(options)
Expand Down
12 changes: 2 additions & 10 deletions types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ export type ServerOptions = {
xPort?: number | undefined,
downloadRetries?: number | undefined,
initSQLString?: string | undefined,
arch?: "arm64" | "x64" | undefined,
_DO_NOT_USE_deleteDBAfterStopped?: boolean | undefined,
_DO_NOT_USE_dbPath?: string | undefined,
_DO_NOT_USE_binaryDirectoryPath?: string | undefined,
_DO_NOT_USE_cli?: boolean | undefined
arch?: "arm64" | "x64" | undefined
}

export type InternalServerOptions = {
Expand All @@ -37,11 +33,7 @@ export type InternalServerOptions = {
xPort: number,
downloadRetries: number,
initSQLString: string,
arch: string,
_DO_NOT_USE_deleteDBAfterStopped: boolean,
_DO_NOT_USE_dbPath: string,
_DO_NOT_USE_binaryDirectoryPath: string,
_DO_NOT_USE_cli: boolean
arch: string
}

export type ExecutorOptions = {
Expand Down
Loading