Skip to content

Commit 8723187

Browse files
hasezoeynodkz
authored andcommitted
fix(ArchLinux): binary not downloaded when no release file is found. Plus other code fixes and cleanups (tnx @hasezoey)
* Add notes on what is needed to detect the linux-distro fixes #248 * Adding basic Pull Request Template Adding Inital Bug Issue Template Adding Inital Question Issue Template fixes #255 * Change note about ArchLinux * Add TSDoc to MongoBinaryDownloadUrl Add cases for ArchLinux & Alpine (no offical build) ElementaryOS Error handling (warn lsb_release error) Make some string assembling more readable * Add sub-chapter "Known Incompatibilities" to "Requirements" * Make "locationExists" async for non-blocking * Shrink resolve-config functions thanks to "optional-chaining" add function "envToBool" to resolve-config * Convert "MONGOMS_MD5_CHECK" to use resolve-config Shrink thanks to Nullish Coalescing Add TSDoc Remove "eslint-disable-line" (why was it there?) Check agianst "this.dlProgress.length" instead of an constant number Promisify sync-fs functions to make them non-blocking and awaitable * MongoBinary: use envToBool instead of long line postinstall: disable rule "@typescript-eslint/no-var-requires" * fix tests
1 parent 38124fe commit 8723187

File tree

10 files changed

+274
-57
lines changed

10 files changed

+274
-57
lines changed

.github/ISSUE_TEMPLATE/bug.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
name: Bug
3+
about: Create a bug report
4+
title: ''
5+
labels: bug
6+
assignees: ''
7+
---
8+
9+
<!--
10+
Make sure you read [Mastering-Markdown](https://guides.github.com/features/mastering-markdown/)
11+
-->
12+
13+
## Versions
14+
15+
- NodeJS: 0.0.0
16+
- mongodb-memory-server-*: 0.0.0
17+
- mongodb: 0.0.0
18+
- mongoose: 0.0.0 <!--remove this if not used-->
19+
- system: <!--either Windows, MacOS, Linux (with distro and distro version)-->
20+
21+
package: mongo-memory-server <!--State the package you are using-->
22+
<!--
23+
possible are:
24+
mongo-memory-server
25+
mongo-memory-server-core
26+
mongo-memory-server-global
27+
-->
28+
29+
## What is the Problem?
30+
31+
<!--Please add an description of what the bug / problem is-->
32+
33+
## Code Example
34+
35+
```ts
36+
code here
37+
```
38+
39+
## Do you know *why* it happenes?
40+
41+
*no*

.github/ISSUE_TEMPLATE/question.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
name: Question
3+
about: Ask a question
4+
title: ''
5+
labels: question
6+
assignees: ''
7+
---
8+
9+
<!--
10+
Make sure you read [Mastering-Markdown](https://guides.github.com/features/mastering-markdown/)
11+
-->
12+
13+
## Versions
14+
15+
- NodeJS: 0.0.0
16+
- mongodb-memory-server-*: 0.0.0
17+
- mongodb: 0.0.0
18+
- mongoose: 0.0.0 <!--remove this if not used-->
19+
- system: <!--either Windows, MacOS, Linux (with distro and distro version)-->
20+
21+
package: mongo-memory-server <!--State the package you are using-->
22+
<!--
23+
possible are:
24+
mongo-memory-server
25+
mongo-memory-server-core
26+
mongo-memory-server-global
27+
-->
28+
29+
## What is your question?
30+
31+
<!--Please include code samples if the question is about it-->

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!--
2+
## Make sure you have done these steps
3+
4+
- Make sure you read [Mastering-Markdown](https://guides.github.com/features/mastering-markdown/)
5+
- remove the parts that are not applicable
6+
- Please have "Allow edits from maintainers" activated
7+
-->
8+
9+
## Related Issues
10+
<!--Remove this part if not applicable-->
11+
12+
- #1

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ Choose any package, because they are the same. Differs only by default configura
3434
NodeJS: 8+
3535
Typescript: 3.7+ (if used)
3636

37+
One of those:
38+
39+
- having `lsb-core` installed (or any that provides the `lsb_release` command)
40+
- having an `/etc/os-release` file that is compliant to the [OS-Release Spec](https://www.freedesktop.org/software/systemd/man/os-release.html)
41+
- having an `/etc/*-release` file that is compliant to the [OS-Release Spec](https://www.freedesktop.org/software/systemd/man/os-release.html) (and does not include `lsb`)
42+
43+
#### Known Incompatibilities
44+
45+
- ArchLinux & Alpine do not have an offical mongodb build
46+
- ArchLinux(Docker) does not have an `/etc/os-release` file by default
47+
3748
### `mongodb-memory-server`
3849

3950
Auto-downloads the latest `mongod` binary on npm install to: `node_modules/.cache/mongodb-binaries`.

packages/mongodb-memory-server-core/src/util/MongoBinary.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import dedent from 'dedent';
99
import { promisify } from 'util';
1010
import MongoBinaryDownload from './MongoBinaryDownload';
1111
import { DebugFn } from '../types';
12-
import resolveConfig from './resolve-config';
12+
import resolveConfig, { envToBool } from './resolve-config';
1313

1414
// TODO: return back `latest` version when it will be fixed in MongoDB distro (for now use 4.0.3 😂)
1515
// More details in https://github.com/nodkz/mongodb-memory-server/issues/131
@@ -104,7 +104,6 @@ export default class MongoBinary {
104104

105105
static async getPath(opts: MongoBinaryOpts = {}): Promise<string> {
106106
const legacyDLDir = path.resolve(os.homedir(), '.cache/mongodb-binaries');
107-
const envDebug = resolveConfig('DEBUG');
108107

109108
// if we're in postinstall script, npm will set the cwd too deep
110109
let nodeModulesDLDir = process.cwd();
@@ -128,8 +127,7 @@ export default class MongoBinary {
128127
arch: resolveConfig('ARCH') || os.arch(),
129128
version: resolveConfig('VERSION') || LATEST_VERSION,
130129
systemBinary: resolveConfig('SYSTEM_BINARY'),
131-
debug:
132-
typeof envDebug === 'string' ? ['1', 'on', 'yes', 'true'].indexOf(envDebug) !== -1 : false,
130+
debug: envToBool(resolveConfig('DEBUG') ?? ''),
133131
};
134132

135133
if (opts.debug) {

packages/mongodb-memory-server-core/src/util/MongoBinaryDownload.ts

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import MongoBinaryDownloadUrl from './MongoBinaryDownloadUrl';
99
import { DebugFn, DebugPropT, DownloadProgressT } from '../types';
1010
import { LATEST_VERSION } from './MongoBinary';
1111
import HttpsProxyAgent from 'https-proxy-agent';
12+
import { promisify } from 'util';
13+
import resolveConfig, { envToBool } from './resolve-config';
1214

1315
export interface MongoBinaryDownloadOpts {
1416
version?: string;
@@ -39,17 +41,11 @@ export default class MongoBinaryDownload {
3941
platform: string;
4042

4143
constructor({ platform, arch, downloadDir, version, checkMD5, debug }: MongoBinaryDownloadOpts) {
42-
this.platform = platform || os.platform();
43-
this.arch = arch || os.arch();
44-
this.version = version || LATEST_VERSION;
44+
this.platform = platform ?? os.platform();
45+
this.arch = arch ?? os.arch();
46+
this.version = version ?? LATEST_VERSION;
4547
this.downloadDir = path.resolve(downloadDir || 'mongodb-download');
46-
if (checkMD5 === undefined) {
47-
this.checkMD5 =
48-
typeof process.env.MONGOMS_MD5_CHECK === 'string' &&
49-
['1', 'on', 'yes', 'true'].indexOf(process.env.MONGOMS_MD5_CHECK.toLowerCase()) !== -1;
50-
} else {
51-
this.checkMD5 = checkMD5;
52-
}
48+
this.checkMD5 = checkMD5 ?? envToBool(resolveConfig('MD5_CHECK') ?? '');
5349
this.dlProgress = {
5450
current: 0,
5551
length: 0,
@@ -68,24 +64,32 @@ export default class MongoBinaryDownload {
6864
}
6965
}
7066

67+
/**
68+
* Get the path of the already downloaded "mongod" file
69+
* otherwise download it and then return the path
70+
*/
7171
async getMongodPath(): Promise<string> {
7272
const binaryName = this.platform === 'win32' ? 'mongod.exe' : 'mongod';
7373
const mongodPath = path.resolve(this.downloadDir, this.version, binaryName);
74-
if (this.locationExists(mongodPath)) {
74+
if (await this.locationExists(mongodPath)) {
7575
return mongodPath;
7676
}
7777

7878
const mongoDBArchive = await this.startDownload();
7979
await this.extract(mongoDBArchive);
8080
fs.unlinkSync(mongoDBArchive);
8181

82-
if (this.locationExists(mongodPath)) {
82+
if (await this.locationExists(mongodPath)) {
8383
return mongodPath;
8484
}
8585

8686
throw new Error(`Cannot find downloaded mongod binary by path ${mongodPath}`);
8787
}
8888

89+
/**
90+
* Download the MongoDB Archive and check it against an MD5
91+
* @returns The MongoDB Archive location
92+
*/
8993
async startDownload(): Promise<string> {
9094
const mbdUrl = new MongoBinaryDownloadUrl({
9195
platform: this.platform,
@@ -106,6 +110,11 @@ export default class MongoBinaryDownload {
106110
return mongoDBArchive;
107111
}
108112

113+
/**
114+
* Download MD5 file and check it against the MongoDB Archive
115+
* @param urlForReferenceMD5 URL to download the MD5
116+
* @param mongoDBArchive The MongoDB Archive file location
117+
*/
109118
async makeMD5check(
110119
urlForReferenceMD5: string,
111120
mongoDBArchive: string
@@ -119,11 +128,15 @@ export default class MongoBinaryDownload {
119128
const md5Remote = m ? m[1] : null;
120129
const md5Local = md5File.sync(mongoDBArchive);
121130
if (md5Remote !== md5Local) {
122-
throw new Error('MongoBinaryDownload: md5 check is failed');
131+
throw new Error('MongoBinaryDownload: md5 check failed');
123132
}
124133
return true;
125134
}
126135

136+
/**
137+
* Download file from downloadUrl
138+
* @param downloadUrl URL to download a File
139+
*/
127140
async download(downloadUrl: string): Promise<string> {
128141
const proxy =
129142
process.env['yarn_https-proxy'] ||
@@ -165,6 +178,11 @@ export default class MongoBinaryDownload {
165178
return downloadedFile;
166179
}
167180

181+
/**
182+
* Extract given Archive
183+
* @param mongoDBArchive Archive location
184+
* @returns extracted directory location
185+
*/
168186
async extract(mongoDBArchive: string): Promise<string> {
169187
const binaryName = this.platform === 'win32' ? 'mongod.exe' : 'mongod';
170188
const extractDir = path.resolve(this.downloadDir, this.version);
@@ -188,20 +206,26 @@ export default class MongoBinaryDownload {
188206
filter,
189207
// extract to root folder
190208
map: (file) => {
191-
file.path = path.basename(file.path); // eslint-disable-line
209+
file.path = path.basename(file.path);
192210
return file;
193211
},
194212
});
195213

196-
if (!this.locationExists(path.resolve(this.downloadDir, this.version, binaryName))) {
214+
if (!(await this.locationExists(path.resolve(this.downloadDir, this.version, binaryName)))) {
197215
throw new Error(
198216
`MongoBinaryDownload: missing mongod binary in ${mongoDBArchive} (downloaded from ${this
199-
._downloadingUrl || ''}). Broken package in MongoDB distro?`
217+
._downloadingUrl ?? 'unkown'}). Broken archive from MongoDB Provider?`
200218
);
201219
}
202220
return extractDir;
203221
}
204222

223+
/**
224+
* Downlaod given httpOptions to tempDownloadLocation, then move it to downloadLocation
225+
* @param httpOptions The httpOptions directly passed to https.get
226+
* @param downloadLocation The location the File should be after the download
227+
* @param tempDownloadLocation The location the File should be while downloading
228+
*/
205229
async httpDownload(
206230
httpOptions: HttpDownloadOptions,
207231
downloadLocation: string,
@@ -210,15 +234,18 @@ export default class MongoBinaryDownload {
210234
return new Promise((resolve, reject) => {
211235
const fileStream = fs.createWriteStream(tempDownloadLocation);
212236

213-
const req: any = https.get(httpOptions, (response: any) => {
237+
const req = https.get(httpOptions, (response: any) => {
214238
this.dlProgress.current = 0;
215239
this.dlProgress.length = parseInt(response.headers['content-length'], 10);
216240
this.dlProgress.totalMb = Math.round((this.dlProgress.length / 1048576) * 10) / 10;
217241

218242
response.pipe(fileStream);
219243

220-
fileStream.on('finish', () => {
221-
if (this.dlProgress.current < 1000000 && !httpOptions.path.endsWith('.md5')) {
244+
fileStream.on('finish', async () => {
245+
if (
246+
this.dlProgress.current < this.dlProgress.length &&
247+
!httpOptions.path.endsWith('.md5')
248+
) {
222249
const downloadUrl =
223250
this._downloadingUrl || `https://${httpOptions.hostname}/${httpOptions.path}`;
224251
reject(
@@ -228,24 +255,31 @@ export default class MongoBinaryDownload {
228255
);
229256
return;
230257
}
258+
231259
fileStream.close();
232-
fs.renameSync(tempDownloadLocation, downloadLocation);
260+
await promisify(fs.rename)(tempDownloadLocation, downloadLocation);
233261
this.debug(`renamed ${tempDownloadLocation} to ${downloadLocation}`);
262+
234263
resolve(downloadLocation);
235264
});
236265

237266
response.on('data', (chunk: any) => {
238267
this.printDownloadProgress(chunk);
239268
});
240269

241-
req.on('error', (e: any) => {
242-
this.debug('request error:', e);
270+
req.on('error', (e: Error) => {
271+
// log it without having debug enabled
272+
console.error(`Couldnt download ${httpOptions.path}!`, e.message);
243273
reject(e);
244274
});
245275
});
246276
});
247277
}
248278

279+
/**
280+
* Print the Download Progress to STDOUT
281+
* @param chunk A chunk to get the length
282+
*/
249283
printDownloadProgress(chunk: any): void {
250284
this.dlProgress.current += chunk.length;
251285

@@ -264,9 +298,14 @@ export default class MongoBinaryDownload {
264298
);
265299
}
266300

267-
locationExists(location: string): boolean {
301+
/**
302+
* Test if the location given is already used
303+
* Does *not* dereference links
304+
* @param location The Path to test
305+
*/
306+
async locationExists(location: string): Promise<boolean> {
268307
try {
269-
fs.lstatSync(location);
308+
await promisify(fs.lstat)(location);
270309
return true;
271310
} catch (e) {
272311
if (e.code !== 'ENOENT') throw e;

0 commit comments

Comments
 (0)