mirror of
https://github.com/jakejarvis/careful-downloader.git
synced 2025-04-26 06:35:22 -04:00
BREAKING: allow hashes provided as a URL to a text file or a string
(closes #1)
This commit is contained in:
parent
bcd2e89f7d
commit
4d3134afb8
42
README.md
42
README.md
@ -4,7 +4,7 @@
|
||||
[](https://www.npmjs.com/package/careful-downloader)
|
||||
[](LICENSE)
|
||||
|
||||
Downloads a file and its checksums to a temporary directory, validates the hash, and optionally extracts it if safe. A headache-averting wrapper around [`got`](https://github.com/sindresorhus/got), [`sumchecker`](https://github.com/malept/sumchecker), and [`decompress`](https://github.com/kevva/decompress).
|
||||
Downloads a file and its checksums to a temporary directory, validates the hash, and optionally extracts it if safe.
|
||||
|
||||
## Install
|
||||
|
||||
@ -21,20 +21,34 @@ import downloader from "careful-downloader";
|
||||
|
||||
await downloader(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_extended_0.88.1_Windows-64bit.zip",
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
{
|
||||
checksumUrl: "https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
destDir: "./vendor",
|
||||
algorithm: "sha256",
|
||||
encoding: "binary",
|
||||
extract: true,
|
||||
},
|
||||
);
|
||||
//=> '/Users/jake/src/carefully-downloaded/vendor/hugo.exe'
|
||||
```
|
||||
|
||||
Instead of `options.checksumUrl`, you can also simply provide a hash as a string via `options.checksumHash`.
|
||||
|
||||
```js
|
||||
await downloader(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_extended_0.88.1_Windows-64bit.zip",
|
||||
{
|
||||
checksumHash: "aaa20e258cd668cff66400d365d73ddc375e44487692d49a5285b56330f6e6b2",
|
||||
destDir: "./vendor",
|
||||
algorithm: "sha256",
|
||||
extract: false, // default
|
||||
},
|
||||
);
|
||||
//=> '/Users/jake/src/carefully-downloaded/vendor/hugo_extended_0.88.1_Windows-64bit.zip'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### downloader(downloadUrl, checksumUrl, options?)
|
||||
### downloader(downloadUrl, options)
|
||||
|
||||
#### downloadUrl
|
||||
|
||||
@ -42,7 +56,11 @@ Type: `string`
|
||||
|
||||
Absolute URL to the desired file to download.
|
||||
|
||||
#### checksumUrl
|
||||
#### options
|
||||
|
||||
Type: `object`
|
||||
|
||||
##### checksumUrl
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -54,9 +72,15 @@ ad81192d188cb584a73074d3dea9350d4609a13ed5fccaafd229b424247e5890 hugo_0.88.1_Wi
|
||||
aaa20e258cd668cff66400d365d73ddc375e44487692d49a5285b56330f6e6b2 hugo_extended_0.88.1_Windows-64bit.zip
|
||||
```
|
||||
|
||||
#### options
|
||||
**Either this option or `checksumHash` is required.**
|
||||
|
||||
Type: `object`
|
||||
##### checksumHash
|
||||
|
||||
Type: `string`
|
||||
|
||||
A single hash for the given downloaded file, e.g. `abcd1234abcd1234abcd1234...`.
|
||||
|
||||
**Either this option or `checksumUrl` is required.**
|
||||
|
||||
##### filename
|
||||
|
||||
@ -98,9 +122,7 @@ On recent releases of OpenSSL, `openssl list -digest-algorithms` will display th
|
||||
##### encoding
|
||||
|
||||
Type: `string`\
|
||||
Default: `"binary"`
|
||||
|
||||
Tell the file stream to read the download as a binary, UTF-8 text file, base64, etc.
|
||||
Default: `"hex"`
|
||||
|
||||
## License
|
||||
|
||||
|
107
index.js
107
index.js
@ -1,33 +1,44 @@
|
||||
import path from "path";
|
||||
import stream from "stream";
|
||||
import { promisify } from "util";
|
||||
import createDebug from "debug";
|
||||
import fs from "fs-extra";
|
||||
import tempy from "tempy";
|
||||
import got from "got";
|
||||
import sumchecker from "sumchecker";
|
||||
import decompress from "decompress";
|
||||
import urlParse from "url-parse";
|
||||
import isPathInCwd from "is-path-in-cwd";
|
||||
import isPathInside from "is-path-inside";
|
||||
|
||||
// set DEBUG=careful-downloader in environment to enable detailed logging
|
||||
const debug = new createDebug("careful-downloader");
|
||||
import debug from "./lib/debug.js";
|
||||
import download from "./lib/download.js";
|
||||
import { checksumViaFile, checksumViaString } from "./lib/checksum.js";
|
||||
|
||||
export default async function downloader(downloadUrl, checksumUrl, options = {}) {
|
||||
// normalize options and set defaults
|
||||
export default async (downloadUrl, options = {}) => {
|
||||
debug(`User-provided config: ${JSON.stringify(options)}`);
|
||||
|
||||
let checksumMethod;
|
||||
let checksumKey;
|
||||
if (options.checksumUrl) {
|
||||
// download and use checksum text file to parse and check
|
||||
checksumMethod = "file";
|
||||
checksumKey = options.checksumUrl;
|
||||
} else if (options.checksumHash) {
|
||||
// simply compare hash of file to provided string
|
||||
checksumMethod = "string";
|
||||
checksumKey = options.checksumHash;
|
||||
} else {
|
||||
throw new Error("must either provide checksumUrl or checksumHash.");
|
||||
}
|
||||
debug(`Provided a ${checksumMethod} to validate against: ${checksumKey}`);
|
||||
|
||||
// normalize options and set defaults
|
||||
options = {
|
||||
filename: options.filename || urlParse(downloadUrl).pathname.split("/").pop(),
|
||||
filename: options.filename || new URL(downloadUrl).pathname.split("/").pop(),
|
||||
extract: !!options.extract,
|
||||
destDir: options.destDir ? path.resolve(process.cwd(), options.destDir) : path.resolve(process.cwd(), "downloads"),
|
||||
cleanDestDir: !!options.cleanDestDir,
|
||||
algorithm: options.algorithm || "sha256",
|
||||
encoding: options.encoding || "binary",
|
||||
encoding: options.encoding || "hex",
|
||||
};
|
||||
debug(`Normalized config with defaults: ${JSON.stringify(options)}`);
|
||||
|
||||
// throw an error if destDir is outside of the module to prevent path traversal for security reasons
|
||||
if (!isPathInCwd(options.destDir)) {
|
||||
if (!isPathInside(options.destDir, process.cwd())) {
|
||||
throw new Error(`destDir must be located within '${process.cwd()}', it's currently set to '${options.destDir}'.`);
|
||||
}
|
||||
|
||||
@ -36,14 +47,36 @@ export default async function downloader(downloadUrl, checksumUrl, options = {})
|
||||
debug(`Temp dir generated: '${tempDir}'`);
|
||||
|
||||
try {
|
||||
// simultaneously download the desired file and its checksums
|
||||
await Promise.all([
|
||||
downloadFile(downloadUrl, path.join(tempDir, options.filename)),
|
||||
downloadFile(checksumUrl, path.join(tempDir, "checksums.txt")),
|
||||
]);
|
||||
// get the desired file
|
||||
await download(downloadUrl, path.join(tempDir, options.filename));
|
||||
|
||||
// validate the checksum of the download
|
||||
if (await checkChecksum(tempDir, options.filename, "checksums.txt", options.algorithm, options.encoding)) {
|
||||
let validated = false;
|
||||
|
||||
if (checksumMethod === "file") {
|
||||
debug("Using a downloaded checksum file to validate...");
|
||||
|
||||
const checksumFilename = new URL(checksumKey).pathname.split("/").pop();
|
||||
await download(checksumKey, path.join(tempDir, checksumFilename));
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
if (await checksumViaFile(path.join(tempDir, options.filename), path.join(tempDir, checksumFilename), options.algorithm, options.encoding)) {
|
||||
validated = true;
|
||||
}
|
||||
} else if (checksumMethod === "string") {
|
||||
debug("Using a provided hash to validate...");
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
if (await checksumViaString(path.join(tempDir, options.filename), checksumKey, options.algorithm, options.encoding)) {
|
||||
validated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// stop here if the checksum wasn't validated by either method
|
||||
if (!validated) {
|
||||
throw new Error(`Invalid checksum for '${options.filename}'.`);
|
||||
}
|
||||
|
||||
// optionally clear the target directory of existing files
|
||||
if (options.cleanDestDir && fs.existsSync(options.destDir)) {
|
||||
debug(`Deleting contents of '${options.destDir}'`);
|
||||
@ -65,41 +98,9 @@ export default async function downloader(downloadUrl, checksumUrl, options = {})
|
||||
await fs.copy(path.join(tempDir, options.filename), path.join(options.destDir, options.filename));
|
||||
return path.join(options.destDir, options.filename);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Invalid checksum for '${options.filename}'.`);
|
||||
}
|
||||
} finally {
|
||||
// delete temporary directory
|
||||
debug(`Deleting temp dir: '${tempDir}'`);
|
||||
await fs.remove(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
// Download any file to any destination. Returns a promise.
|
||||
async function downloadFile(url, dest) {
|
||||
debug(`Downloading '${url}' to '${dest}'`);
|
||||
|
||||
// get remote file and write locally
|
||||
const pipeline = promisify(stream.pipeline);
|
||||
const download = await pipeline(
|
||||
got.stream(url, { followRedirect: true }), // GitHub releases redirect to unpredictable URLs
|
||||
fs.createWriteStream(dest),
|
||||
);
|
||||
|
||||
return download;
|
||||
}
|
||||
|
||||
// Check da checksum.
|
||||
async function checkChecksum(baseDir, downloadFile, checksumFile, algorithm, encoding) {
|
||||
debug(`Validating checksum of '${downloadFile}' (hash: '${algorithm}', encoding: '${encoding}')`);
|
||||
|
||||
// instantiate checksum validator
|
||||
const checker = new sumchecker.ChecksumValidator(algorithm, path.join(baseDir, checksumFile), {
|
||||
defaultTextEncoding: encoding,
|
||||
});
|
||||
|
||||
// finally test the file
|
||||
const valid = await checker.validate(baseDir, downloadFile);
|
||||
|
||||
return valid;
|
||||
}
|
||||
};
|
||||
|
97
lib/checksum.js
Normal file
97
lib/checksum.js
Normal file
@ -0,0 +1,97 @@
|
||||
import path from "path";
|
||||
import crypto from "crypto";
|
||||
import fs from "fs-extra";
|
||||
|
||||
import debug from "./debug.js";
|
||||
|
||||
// Check the checksum via a parsed text file containing one or more hashes.
|
||||
export const checksumViaFile = async (desiredFile, checksumFile, algorithm, encoding) => {
|
||||
debug(`Validating checksum of '${desiredFile}' against hashes listed in '${checksumFile}' (algo: '${algorithm}', encoding: '${encoding}')`);
|
||||
|
||||
const parsedHashes = await parseChecksumFile(checksumFile);
|
||||
debug(`All hashes pulled from the checksums file '${path.basename(checksumFile)}' : ${JSON.stringify(parsedHashes)}`);
|
||||
|
||||
const generatedHash = await generateHashFromFile(desiredFile, algorithm, encoding);
|
||||
debug(`Generated a ${algorithm} hash of '${path.basename(desiredFile)}' => ${generatedHash}`);
|
||||
|
||||
const correctHash = parsedHashes[path.basename(desiredFile)];
|
||||
debug(`Found '${path.basename(desiredFile)}' in '${path.basename(checksumFile)}', provided hash is: ${correctHash}`);
|
||||
|
||||
if (!correctHash) {
|
||||
throw new Error(`'${path.basename(desiredFile)}' isn't listed in checksums file.`);
|
||||
}
|
||||
|
||||
debug(`Checking if generated === provided... ${generatedHash} : ${correctHash}`);
|
||||
if (generatedHash !== correctHash) {
|
||||
debug(`Nope... ${generatedHash} !== ${correctHash}`);
|
||||
throw new Error(`Hash of '${path.basename(desiredFile)}' doesn't match the given checksum.`);
|
||||
}
|
||||
|
||||
debug("Checksum validated via file, it's a match!");
|
||||
return true;
|
||||
};
|
||||
|
||||
// Check the checksum via a provided hash.
|
||||
export const checksumViaString = async (desiredFile, correctHash, algorithm, encoding) => {
|
||||
debug(`Validating checksum of '${desiredFile}' against provided hash '${correctHash}' (algo: '${algorithm}', encoding: '${encoding}')`);
|
||||
|
||||
const generatedHash = await generateHashFromFile(desiredFile, algorithm, encoding);
|
||||
debug(`Generated a ${algorithm} hash of '${path.basename(desiredFile)}' => ${generatedHash}`);
|
||||
|
||||
debug(`Checking if generated === provided... ${generatedHash} : ${correctHash}`);
|
||||
if (generatedHash !== correctHash) {
|
||||
debug(`Nope... ${generatedHash} !== ${correctHash}`);
|
||||
throw new Error(`Hash of '${path.basename(desiredFile)}' doesn't match the given checksum.`);
|
||||
}
|
||||
|
||||
debug("Checksum validated via string, it's a match!");
|
||||
return true;
|
||||
};
|
||||
|
||||
// Takes a path to a file and returns its hash.
|
||||
const generateHashFromFile = async (file, algorithm, encoding) => {
|
||||
const fileBuffer = fs.readFileSync(file);
|
||||
const hashSum = crypto.createHash(algorithm);
|
||||
hashSum.update(fileBuffer);
|
||||
|
||||
return hashSum.digest(encoding);
|
||||
};
|
||||
|
||||
// Largely adapted from sumchecker:
|
||||
// https://github.com/malept/sumchecker/blob/28aed640a02787490d033fda56eaee30e24e5a71/src/index.ts#L97
|
||||
const parseChecksumFile = async (checksumFile) => {
|
||||
// read the text file holding one or more checksums
|
||||
const data = fs.readFileSync(checksumFile, { encoding: "utf8" });
|
||||
|
||||
// https://regexr.com/67k7i
|
||||
const lineRegex = /^([\da-fA-F]+) ([ *])(.+)$/;
|
||||
|
||||
// extract each file and its hash into this object
|
||||
const checksums = {};
|
||||
|
||||
// loop through each line (should be one file & hash each)
|
||||
let lineNumber = 0;
|
||||
for (const line of data.trim().split(/[\r\n]+/)) {
|
||||
lineNumber += 1;
|
||||
|
||||
// parse the current line using the regex pattern above
|
||||
const parsedLine = lineRegex.exec(line);
|
||||
|
||||
if (parsedLine === null) {
|
||||
// didn't match regex
|
||||
debug(`Could not parse line number ${lineNumber}`);
|
||||
throw new Error(lineNumber, line);
|
||||
} else {
|
||||
parsedLine.shift();
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [hash, binary, file] = parsedLine;
|
||||
|
||||
// save the current file & hash in the checksums object
|
||||
checksums[file] = hash;
|
||||
}
|
||||
}
|
||||
|
||||
// send back the cleaned up object of filenames & hashes
|
||||
return checksums;
|
||||
};
|
4
lib/debug.js
Normal file
4
lib/debug.js
Normal file
@ -0,0 +1,4 @@
|
||||
import debug from "debug";
|
||||
|
||||
// set DEBUG=careful-downloader in environment to enable detailed logging
|
||||
export default debug("careful-downloader");
|
20
lib/download.js
Normal file
20
lib/download.js
Normal file
@ -0,0 +1,20 @@
|
||||
import stream from "stream";
|
||||
import { promisify } from "util";
|
||||
import fs from "fs-extra";
|
||||
import got from "got";
|
||||
|
||||
import debug from "./debug.js";
|
||||
|
||||
// Download any file to any destination. Returns a promise.
|
||||
export default async (url, dest) => {
|
||||
debug(`Downloading '${url}' to '${dest}'`);
|
||||
|
||||
// get remote file and write locally
|
||||
const pipeline = promisify(stream.pipeline);
|
||||
const download = pipeline(
|
||||
got.stream(url, { followRedirect: true }), // GitHub releases redirect to unpredictable URLs
|
||||
fs.createWriteStream(dest),
|
||||
);
|
||||
|
||||
return download;
|
||||
};
|
13
package.json
13
package.json
@ -18,7 +18,8 @@
|
||||
"types": "./index.d.ts",
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
"index.d.ts",
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "eslint . && mocha"
|
||||
@ -28,10 +29,8 @@
|
||||
"decompress": "^4.2.1",
|
||||
"fs-extra": "^10.0.0",
|
||||
"got": "^11.8.2",
|
||||
"is-path-in-cwd": "^4.0.0",
|
||||
"sumchecker": "^3.0.1",
|
||||
"tempy": "^2.0.0",
|
||||
"url-parse": "^1.5.3"
|
||||
"is-path-inside": "^4.0.0",
|
||||
"tempy": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jakejarvis/eslint-config": "*",
|
||||
@ -40,8 +39,8 @@
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/url-parse": "^1.4.4",
|
||||
"chai": "^4.3.4",
|
||||
"eslint": "^8.0.0",
|
||||
"mocha": "^9.1.2"
|
||||
"eslint": "^8.0.1",
|
||||
"mocha": "^9.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
|
@ -4,21 +4,20 @@ import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { expect } from "chai";
|
||||
|
||||
import downloader from "../index.js";
|
||||
import download from "../index.js";
|
||||
|
||||
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#what-do-i-use-instead-of-__dirname-and-__filename
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
describe("checksum via downloaded text file", function () {
|
||||
it("verified checksum, hugo.exe was extracted", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
await downloader(
|
||||
await download(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_extended_0.88.1_Windows-64bit.zip",
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
{
|
||||
checksumUrl: "https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
destDir: path.join(__dirname, "temp"),
|
||||
algorithm: "sha256",
|
||||
encoding: "binary",
|
||||
extract: true,
|
||||
},
|
||||
);
|
||||
@ -29,17 +28,16 @@ it("verified checksum, hugo.exe was extracted", async function () {
|
||||
fs.removeSync(path.join(__dirname, "temp"));
|
||||
});
|
||||
|
||||
it("incorrect checksum, not extracted", async function () {
|
||||
it("incorrect checksum file, not extracted", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
expect(async () => downloader(
|
||||
expect(async () => download(
|
||||
// download mismatching versions to trigger error
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.0/hugo_0.88.0_Windows-64bit.zip",
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
{
|
||||
checksumUrl: "https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
destDir: path.join(__dirname, "temp"),
|
||||
algorithm: "sha256",
|
||||
encoding: "binary",
|
||||
extract: false,
|
||||
},
|
||||
)).to.throw;
|
||||
@ -53,10 +51,10 @@ it("incorrect checksum, not extracted", async function () {
|
||||
it("destDir located outside of module, throw error", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
expect(async () => downloader(
|
||||
expect(async () => download(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_Windows-64bit.zip",
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
{
|
||||
checksumUrl: "https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
destDir: "../vendor", // invalid path
|
||||
},
|
||||
)).to.throw;
|
||||
@ -65,9 +63,11 @@ it("destDir located outside of module, throw error", async function () {
|
||||
it("zero options, download zip and leave it alone", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
await downloader(
|
||||
await download(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_extended_0.88.1_Windows-64bit.zip",
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
{
|
||||
checksumUrl: "https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_0.88.1_checksums.txt",
|
||||
},
|
||||
);
|
||||
|
||||
expect(fs.existsSync(path.join(__dirname, "../downloads", "hugo_extended_0.88.1_Windows-64bit.zip"))).to.be.true;
|
||||
@ -75,3 +75,45 @@ it("zero options, download zip and leave it alone", async function () {
|
||||
// clean up
|
||||
fs.removeSync(path.join(__dirname, "../downloads"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("checksum via string", function () {
|
||||
it("verified checksum, hugo.exe was extracted", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
await download(
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.1/hugo_extended_0.88.1_Windows-64bit.zip",
|
||||
{
|
||||
checksumHash: "aaa20e258cd668cff66400d365d73ddc375e44487692d49a5285b56330f6e6b2",
|
||||
destDir: path.join(__dirname, "temp"),
|
||||
algorithm: "sha256",
|
||||
extract: true,
|
||||
},
|
||||
);
|
||||
|
||||
expect(fs.existsSync(path.join(__dirname, "temp", "hugo.exe"))).to.be.true;
|
||||
|
||||
// clean up
|
||||
fs.removeSync(path.join(__dirname, "temp"));
|
||||
});
|
||||
|
||||
it("incorrect checksum string, not extracted", async function () {
|
||||
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
|
||||
|
||||
expect(async () => download(
|
||||
// download mismatching versions to trigger error
|
||||
"https://github.com/gohugoio/hugo/releases/download/v0.88.0/hugo_0.88.0_Windows-64bit.zip",
|
||||
{
|
||||
checksumHash: "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
|
||||
destDir: path.join(__dirname, "temp"),
|
||||
algorithm: "sha256",
|
||||
extract: false,
|
||||
},
|
||||
)).to.throw;
|
||||
|
||||
expect(fs.existsSync(path.join(__dirname, "temp", "hugo.exe"))).to.be.false;
|
||||
|
||||
// clean up
|
||||
fs.removeSync(path.join(__dirname, "temp"));
|
||||
});
|
||||
});
|
||||
|
66
yarn.lock
66
yarn.lock
@ -2,10 +2,10 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@eslint/eslintrc@^1.0.2":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.2.tgz#6044884f7f93c4ecc2d1694c7486cce91ef8f746"
|
||||
integrity sha512-x1ZXdEFsvTcnbTZgqcWUL9w2ybgZCw/qbKTPQnab+XnYA2bMQpJCh+/bBzCRfDJaJdlrrQlOk49jNtru9gL/6Q==
|
||||
"@eslint/eslintrc@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.3.tgz#41f08c597025605f672251dcc4e8be66b5ed7366"
|
||||
integrity sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.3.2"
|
||||
@ -118,9 +118,9 @@
|
||||
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
|
||||
|
||||
"@types/node@*":
|
||||
version "16.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.4.tgz#592f12b0b5f357533ddc3310b0176d42ea3e45d1"
|
||||
integrity sha512-EITwVTX5B4nDjXjGeQAfXOrm+Jn+qNjDmyDRtWoD+wZsl/RDPRTFRKivs4Mt74iOFlLOrE5+Kf+p5yjyhm3+cA==
|
||||
version "16.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.0.tgz#4b95f2327bacd1ef8f08d8ceda193039c5d7f52e"
|
||||
integrity sha512-8MLkBIYQMuhRBQzGN9875bYsOhPnf/0rgXGo66S2FemHkhbn9qtsz9ywV1iCG+vbjigE4WUNVvw37Dx+L0qsPg==
|
||||
|
||||
"@types/responselike@*", "@types/responselike@^1.0.0":
|
||||
version "1.0.0"
|
||||
@ -420,7 +420,7 @@ crypto-random-string@^4.0.0:
|
||||
dependencies:
|
||||
type-fest "^1.0.1"
|
||||
|
||||
debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
|
||||
debug@4.3.2, debug@^4.1.1, debug@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||
@ -596,12 +596,12 @@ eslint-visitor-keys@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186"
|
||||
integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q==
|
||||
|
||||
eslint@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.0.0.tgz#2c2d0ac6353755667ac90c9ff4a9c1315e43fcff"
|
||||
integrity sha512-03spzPzMAO4pElm44m60Nj08nYonPGQXmw6Ceai/S4QK82IgwWO1EXx1s9namKzVlbVu3Jf81hb+N+8+v21/HQ==
|
||||
eslint@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.0.1.tgz#3610e7fe4a05c2154669515ca60835a76a19f700"
|
||||
integrity sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA==
|
||||
dependencies:
|
||||
"@eslint/eslintrc" "^1.0.2"
|
||||
"@eslint/eslintrc" "^1.0.3"
|
||||
"@humanwhocodes/config-array" "^0.6.0"
|
||||
ajv "^6.10.0"
|
||||
chalk "^4.0.0"
|
||||
@ -1019,13 +1019,6 @@ is-path-cwd@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb"
|
||||
integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==
|
||||
|
||||
is-path-in-cwd@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-4.0.0.tgz#e5a97a09cf34655d7bab7204e6c3997d834ef0b6"
|
||||
integrity sha512-FjDhtYysbIKBKRFCQN8NcMaHHWfwAzJLOrRAhzd4hnK6Y1979p6ZthIUqdPjCyAk5jvrmY2fn56Y+kFE8RdsrA==
|
||||
dependencies:
|
||||
is-path-inside "^4.0.0"
|
||||
|
||||
is-path-inside@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||
@ -1189,10 +1182,10 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
mocha@^9.1.2:
|
||||
version "9.1.2"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.2.tgz#93f53175b0f0dc4014bd2d612218fccfcf3534d3"
|
||||
integrity sha512-ta3LtJ+63RIBP03VBjMGtSqbe6cWXRejF9SyM9Zyli1CKZJZ+vfCTj3oW24V7wAphMJdpOFLoMI3hjJ1LWbs0w==
|
||||
mocha@^9.1.3:
|
||||
version "9.1.3"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb"
|
||||
integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==
|
||||
dependencies:
|
||||
"@ungap/promise-all-settled" "1.1.2"
|
||||
ansi-colors "4.1.1"
|
||||
@ -1391,11 +1384,6 @@ punycode@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
querystringify@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
|
||||
integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
@ -1443,11 +1431,6 @@ require-directory@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
resolve-alpn@^1.0.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
@ -1572,13 +1555,6 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
sumchecker@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
|
||||
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
|
||||
supports-color@8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
|
||||
@ -1698,14 +1674,6 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-parse@^1.5.3:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
|
||||
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
Loading…
x
Reference in New Issue
Block a user