mysql-memory-server 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,7 +36,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.downloadVersions = downloadVersions;
40
39
  exports.downloadBinary = downloadBinary;
41
40
  const https = __importStar(require("https"));
42
41
  const fs = __importStar(require("fs"));
@@ -46,18 +45,7 @@ const path_1 = require("path");
46
45
  const crypto_1 = require("crypto");
47
46
  const child_process_1 = require("child_process");
48
47
  const FileLock_1 = require("./FileLock");
49
- function getZipData(entry) {
50
- return new Promise((resolve, reject) => {
51
- entry.getDataAsync((data, err) => {
52
- if (err) {
53
- reject(err);
54
- }
55
- else {
56
- resolve(data);
57
- }
58
- });
59
- });
60
- }
48
+ const constants_1 = require("../constants");
61
49
  function handleTarExtraction(filepath, extractedPath) {
62
50
  return new Promise((resolve, reject) => {
63
51
  (0, child_process_1.execFile)(`tar`, ['-xf', filepath, '-C', extractedPath], (error, stdout, stderr) => {
@@ -68,21 +56,6 @@ function handleTarExtraction(filepath, extractedPath) {
68
56
  });
69
57
  });
70
58
  }
71
- function downloadVersions() {
72
- return new Promise((resolve, reject) => {
73
- let json = "";
74
- https.get("https://github.com/Sebastian-Webster/mysql-memory-server-nodejs/raw/main/versions.json", function (response) {
75
- response
76
- .on("data", append => json += append)
77
- .on("error", e => {
78
- reject(e);
79
- })
80
- .on("end", () => {
81
- resolve(json);
82
- });
83
- });
84
- });
85
- }
86
59
  function downloadFromCDN(url, downloadLocation, logger) {
87
60
  return new Promise(async (resolve, reject) => {
88
61
  if (fs.existsSync(downloadLocation)) {
@@ -94,15 +67,15 @@ function downloadFromCDN(url, downloadLocation, logger) {
94
67
  fileStream.on('open', () => {
95
68
  const request = https.get(url, (response) => {
96
69
  if (response.statusCode !== 200) {
70
+ request.destroy();
97
71
  fileStream.close((err) => {
98
72
  if (err) {
99
73
  logger.error('An error occurred while closing the fileStream for non-200 status code. The error was:', err);
100
74
  }
101
- fs.unlink(downloadLocation, (unlinkErr) => {
102
- if (unlinkErr) {
103
- logger.error('An error occurred while deleting downloadLocation after non-200 status code download attempt. The error was:', err);
75
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
76
+ if (rmError) {
77
+ logger.error('An error occurred while deleting downloadLocation after non-200 status code download attempt. The error was:', rmError);
104
78
  }
105
- logger.error('Received status code:', response.statusCode, 'while downloading MySQL binary.');
106
79
  reject(`Received status code ${response.statusCode} while downloading MySQL binary.`);
107
80
  });
108
81
  });
@@ -110,39 +83,62 @@ function downloadFromCDN(url, downloadLocation, logger) {
110
83
  else {
111
84
  response.pipe(fileStream);
112
85
  fileStream.on('finish', () => {
113
- if (!error) {
114
- resolve();
115
- }
86
+ request.end(() => {
87
+ if (!error) {
88
+ resolve();
89
+ }
90
+ });
116
91
  });
117
92
  }
118
93
  });
119
94
  request.on('error', (err) => {
120
95
  error = err;
121
96
  logger.error(err);
122
- fileStream.end(() => {
123
- fs.unlink(downloadLocation, (unlinkError) => {
124
- if (unlinkError) {
125
- logger.error('An error occurred while deleting downloadLocation after an error occurred with the MySQL server binary download. The error was:', unlinkError);
97
+ request.destroy();
98
+ fileStream.close((fsErr) => {
99
+ if (fsErr) {
100
+ logger.error('An error occurred while closing the fileStream on request error. The error was:', fsErr);
101
+ }
102
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
103
+ if (rmError) {
104
+ logger.error('An error occurred while deleting downloadLocation after an error occurred with the MySQL server binary download. The error was:', rmError);
126
105
  }
127
106
  reject(err.message);
128
107
  });
129
108
  });
130
109
  });
131
- });
132
- fileStream.on('error', (err) => {
133
- error = err;
134
- logger.error(err);
135
- fileStream.end(() => {
136
- fs.unlink(downloadLocation, (unlinkError) => {
137
- if (unlinkError) {
138
- logger.error('An error occurred while deleting downloadLocation after an error occurred with the fileStream. The error was:', unlinkError);
110
+ fileStream.on('error', (err) => {
111
+ error = err;
112
+ logger.error(err);
113
+ request.destroy();
114
+ fileStream.close((fsErr) => {
115
+ if (fsErr) {
116
+ logger.error('An error occurred while closing the fileStream on fileStream error. The error was:', fsErr);
139
117
  }
140
- reject(err.message);
118
+ fs.rm(downloadLocation, { force: true }, (rmError) => {
119
+ if (rmError) {
120
+ logger.error('An error occurred while deleting downloadLocation after an error occurred with the fileStream. The error was:', rmError);
121
+ }
122
+ reject(err.message);
123
+ });
141
124
  });
142
125
  });
143
126
  });
144
127
  });
145
128
  }
129
+ function promisifiedZipExtraction(archiveLocation, extractedLocation) {
130
+ return new Promise((resolve, reject) => {
131
+ const zip = new adm_zip_1.default(archiveLocation);
132
+ zip.extractAllToAsync(extractedLocation, false, false, (err) => {
133
+ if (err) {
134
+ reject(err);
135
+ }
136
+ else {
137
+ resolve();
138
+ }
139
+ });
140
+ });
141
+ }
146
142
  function extractBinary(url, archiveLocation, extractedLocation, logger) {
147
143
  return new Promise(async (resolve, reject) => {
148
144
  if (fs.existsSync(extractedLocation)) {
@@ -157,58 +153,70 @@ function extractBinary(url, archiveLocation, extractedLocation, logger) {
157
153
  return reject(`Folder name is undefined for url: ${url}`);
158
154
  }
159
155
  const folderName = mySQLFolderName.replace(`.${fileExtension}`, '');
156
+ let extractionError = undefined;
160
157
  if (fileExtension === 'zip') {
161
158
  //Only Windows MySQL files use the .zip extension
162
- const zip = new adm_zip_1.default(archiveLocation);
163
- const entries = zip.getEntries();
164
- for (const entry of entries) {
165
- if (entry.entryName.indexOf('..') === -1) {
166
- if (entry.isDirectory) {
167
- if (entry.name === folderName) {
168
- await fsPromises.mkdir(`${extractedLocation}/mysql`, { recursive: true });
169
- }
170
- else {
171
- await fsPromises.mkdir(`${extractedLocation}/${entry.entryName}`, { recursive: true });
172
- }
173
- }
174
- else {
175
- const data = await getZipData(entry);
176
- await fsPromises.writeFile(`${extractedLocation}/${entry.entryName}`, data);
177
- }
178
- }
179
- }
180
159
  try {
181
- await fsPromises.rm(archiveLocation);
160
+ await promisifiedZipExtraction(archiveLocation, extractedLocation);
182
161
  }
183
162
  catch (e) {
184
- logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
163
+ extractionError = e;
164
+ logger.log('An error occurred while extracting the ZIP file. The error was:', e);
185
165
  }
186
166
  finally {
167
+ try {
168
+ await fsPromises.rm(archiveLocation);
169
+ }
170
+ catch (e) {
171
+ logger.error('A non-fatal error occurred while deleting archive location. The error was:', e);
172
+ }
173
+ }
174
+ if (extractionError) {
175
+ return reject(extractionError);
176
+ }
177
+ try {
187
178
  await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
188
- return resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
189
179
  }
180
+ catch (e) {
181
+ logger.error('An error occurred while moving MySQL binary into correct folder (mysql folder). The error was:', e);
182
+ return reject(e);
183
+ }
184
+ resolve((0, path_1.normalize)(`${extractedLocation}/mysql/bin/mysqld.exe`));
190
185
  }
191
- handleTarExtraction(archiveLocation, extractedLocation).then(async () => {
186
+ else {
192
187
  try {
193
- await fsPromises.rm(archiveLocation);
188
+ await handleTarExtraction(archiveLocation, extractedLocation);
194
189
  }
195
190
  catch (e) {
196
- logger.error('A non-fatal error occurred while removing no longer needed archive file:', e);
191
+ extractionError = e;
192
+ logger.error('An error occurred while extracting the tar file. Please make sure tar is installed and there is enough storage space for the extraction. The error was:', e);
197
193
  }
198
194
  finally {
195
+ try {
196
+ await fsPromises.rm(archiveLocation);
197
+ }
198
+ catch (e) {
199
+ logger.error('A non-fatal error occurred while deleting archive location. The error was:', e);
200
+ }
201
+ }
202
+ if (extractionError) {
203
+ return reject(extractionError);
204
+ }
205
+ try {
199
206
  await fsPromises.rename(`${extractedLocation}/${folderName}`, `${extractedLocation}/mysql`);
200
- resolve(`${extractedLocation}/mysql/bin/mysqld`);
201
207
  }
202
- }).catch(error => {
203
- logger.error(`An error occurred while extracting the tar file. Please make sure tar is installed and there is enough storage space for the extraction. The error was: ${error}`);
204
- reject(error);
205
- });
208
+ catch (e) {
209
+ logger.error('An error occurred while moving MySQL binary into correct folder (mysql folder). The error was:', e);
210
+ return reject(e);
211
+ }
212
+ resolve(`${extractedLocation}/mysql/bin/mysqld`);
213
+ }
206
214
  });
207
215
  }
208
216
  function downloadBinary(binaryInfo, options, logger) {
209
217
  return new Promise(async (resolve, reject) => {
210
218
  const { url, version } = binaryInfo;
211
- const dirpath = options._DO_NOT_USE_binaryDirectoryPath;
219
+ const dirpath = (0, constants_1.getInternalEnvVariable)('binaryDirectoryPath');
212
220
  logger.log('Binary path:', dirpath);
213
221
  await fsPromises.mkdir(dirpath, { recursive: true });
214
222
  const lastDashIndex = url.lastIndexOf('-');
@@ -244,11 +252,16 @@ function downloadBinary(binaryInfo, options, logger) {
244
252
  }
245
253
  //The code below only runs if the lock has been acquired by us
246
254
  let downloadTries = 0;
255
+ let useDownloadsURL = false;
247
256
  do {
248
257
  try {
249
258
  downloadTries++;
250
- await downloadFromCDN(url, archivePath, logger);
251
- await extractBinary(url, archivePath, extractedPath, logger);
259
+ const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
260
+ logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
261
+ await downloadFromCDN(downloadURL, archivePath, logger);
262
+ logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
263
+ await extractBinary(downloadURL, archivePath, extractedPath, logger);
264
+ logger.log(`Finished extraction for version ${version}`);
252
265
  break;
253
266
  }
254
267
  catch (e) {
@@ -262,6 +275,24 @@ function downloadBinary(binaryInfo, options, logger) {
262
275
  catch (e) {
263
276
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
264
277
  }
278
+ if (e?.includes?.('status code 404')) {
279
+ if (!useDownloadsURL) {
280
+ //Retry with downloads URL
281
+ downloadTries--;
282
+ useDownloadsURL = true;
283
+ logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
284
+ continue;
285
+ }
286
+ else {
287
+ try {
288
+ await releaseFunction();
289
+ }
290
+ catch (e) {
291
+ logger.error('An error occurred while releasing lock after receiving a 404 error on both downloads and archives URLs. The error was:', e);
292
+ }
293
+ return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
294
+ }
295
+ }
265
296
  if (downloadTries > options.downloadRetries) {
266
297
  //Only reject if we have met the downloadRetries limit
267
298
  try {
@@ -274,12 +305,12 @@ function downloadBinary(binaryInfo, options, logger) {
274
305
  return reject(e);
275
306
  }
276
307
  else {
277
- console.warn(`An error was encountered during the binary download process. Retrying for retry ${downloadTries}/${options.downloadRetries}. The error was:`, e);
308
+ logger.warn(`An error was encountered during the binary download process. Retrying for retry ${downloadTries}/${options.downloadRetries}. The error was:`, e);
278
309
  }
279
310
  }
280
311
  } while (downloadTries <= options.downloadRetries);
281
312
  try {
282
- releaseFunction();
313
+ await releaseFunction();
283
314
  }
284
315
  catch (e) {
285
316
  logger.error('An error occurred while releasing lock after successful binary download. The error was:', e);
@@ -288,6 +319,7 @@ function downloadBinary(binaryInfo, options, logger) {
288
319
  }
289
320
  else {
290
321
  let downloadTries = 0;
322
+ let useDownloadsURL = false;
291
323
  do {
292
324
  const uuid = (0, crypto_1.randomUUID)();
293
325
  const zipFilepath = `${dirpath}/${uuid}.${fileExtension}`;
@@ -295,8 +327,12 @@ function downloadBinary(binaryInfo, options, logger) {
295
327
  const extractedPath = `${dirpath}/${uuid}`;
296
328
  try {
297
329
  downloadTries++;
298
- await downloadFromCDN(url, zipFilepath, logger);
299
- const binaryPath = await extractBinary(url, zipFilepath, extractedPath, logger);
330
+ const downloadURL = useDownloadsURL ? url.replace(constants_1.archiveBaseURL, constants_1.downloadsBaseURL) : url;
331
+ logger.log(`Starting download for MySQL version ${version} from ${downloadURL}.`);
332
+ await downloadFromCDN(downloadURL, zipFilepath, logger);
333
+ logger.log(`Finished downloading MySQL version ${version} from ${downloadURL}. Now starting binary extraction.`);
334
+ const binaryPath = await extractBinary(downloadURL, zipFilepath, extractedPath, logger);
335
+ logger.log(`Finished extraction for version ${version}`);
300
336
  return resolve(binaryPath);
301
337
  }
302
338
  catch (e) {
@@ -310,6 +346,18 @@ function downloadBinary(binaryInfo, options, logger) {
310
346
  catch (e) {
311
347
  logger.error('An error occurred while deleting extractedPath and/or archivePath:', e);
312
348
  }
349
+ if (e?.includes?.('status code 404')) {
350
+ if (!useDownloadsURL) {
351
+ //Retry with downloads URL
352
+ downloadTries--;
353
+ useDownloadsURL = true;
354
+ logger.log(`Encountered error 404 when using archives URL for version ${version}. Now retrying with the downloads URL.`);
355
+ continue;
356
+ }
357
+ else {
358
+ return reject(`Both URLs for MySQL version ${binaryInfo.version} returned status code 404. Aborting download.`);
359
+ }
360
+ }
313
361
  if (downloadTries > options.downloadRetries) {
314
362
  //Only reject if we have met the downloadRetries limit
315
363
  return reject(e);
@@ -7,6 +7,8 @@ declare class Executor {
7
7
  removeExitHandler: () => void;
8
8
  version: string;
9
9
  versionInstalledOnSystem: boolean;
10
+ databasePath: string;
11
+ killedFromPortIssue: boolean;
10
12
  constructor(logger: Logger);
11
13
  getMySQLVersion(preferredVersion?: string): Promise<DownloadedMySQLVersion | null>;
12
14
  startMySQL(options: InternalServerOptions, installedMySQLBinary: DownloadedMySQLVersion): Promise<MySQLDB>;