1
mirror of https://github.com/jakejarvis/hugo-extended.git synced 2025-04-25 15:35:22 -04:00

When Hugo seems to disappear, just reinstall and then continue normally (#82)

should fix #81 (or at least mitigate it)
This commit is contained in:
Jake Jarvis 2021-11-01 14:06:51 -04:00 committed by GitHub
parent b14f7abeb2
commit 8251c012de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 93 deletions

View File

@ -83,9 +83,13 @@ Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
import hugo from "hugo-extended";
import { execFile } from "child_process";
execFile(hugo, ["version"], (error, stdout) => {
console.log(stdout);
});
(async () => {
const binPath = await hugo();
execFile(binPath, ["version"], (error, stdout) => {
console.log(stdout);
});
})();
```
```bash

7
index.d.ts vendored
View File

@ -1,8 +1,7 @@
/// <reference types="node" />
/**
* @returns {string} Absolute path to the Hugo executable (`hugo.exe` on
* Windows, simply `hugo` otherwise).
* @returns A promise of the absolute path to the Hugo executable (`hugo.exe` on
* Windows, simply `hugo` otherwise) once it's installed.
*/
declare const hugo: string;
export = hugo;
export default function hugo(): Promise<string>;

View File

@ -1,15 +1,22 @@
import path from "path";
import { fileURLToPath } from "url";
import { getBinFilename } from "./lib/utils.js";
import logSymbols from "log-symbols";
import install from "./lib/install.js";
import { getBinPath, doesBinExist } from "./lib/utils.js";
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#what-do-i-use-instead-of-__dirname-and-__filename
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const hugo = async () => {
const bin = getBinPath();
const hugo = path.join(
__dirname,
"vendor",
getBinFilename(),
);
// A fix for fleeting ENOENT errors, where Hugo seems to disappear. For now,
// just reinstall Hugo when it's missing and then continue normally like
// nothing happened.
// See: https://github.com/jakejarvis/hugo-extended/issues/81
if (!doesBinExist(bin)) {
// Hugo isn't there for some reason. Try re-installing.
console.info(`${logSymbols.info} Hugo is missing, reinstalling now...`);
await install();
}
return bin;
};
// The only thing this module really exports is the absolute path to Hugo:
export default hugo;

View File

@ -3,10 +3,13 @@
import { spawn } from "child_process";
import hugo from "../index.js";
const args = process.argv.slice(2);
(async () => {
const args = process.argv.slice(2);
const bin = await hugo();
spawn(hugo, args, { stdio: "inherit" })
.on("exit", (code) => {
// forward Hugo's exit code so this module itself reports success/failure
process.exit(code);
});
spawn(bin, args, { stdio: "inherit" })
.on("exit", (code) => {
// forward Hugo's exit code so this module itself reports success/failure
process.exit(code);
});
})();

View File

@ -1,5 +1,6 @@
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
import downloader from "careful-downloader";
import logSymbols from "log-symbols";
import {
@ -12,50 +13,50 @@ import {
isExtended,
} from "./utils.js";
installHugo()
.then((bin) =>
// try querying hugo's version via CLI
getBinVersion(bin),
)
.then((version) => {
// print output of `hugo version` to console
console.log(`${logSymbols.success} Hugo installed successfully!`);
console.log(version);
})
.catch((error) => {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function install() {
try {
const version = getPkgVersion();
const releaseFile = getReleaseFilename(version);
const checksumFile = getChecksumFilename(version);
const binFile = getBinFilename();
// stop here if there's nothing we can download
if (!releaseFile) {
throw new Error(`Are you sure this platform is supported? See: https://github.com/gohugoio/hugo/releases/tag/v${version}`);
}
// warn if platform doesn't support Hugo Extended, proceed with vanilla Hugo
if (!isExtended(releaseFile)) {
console.warn(`${logSymbols.info} Hugo Extended isn't supported on this platform, downloading vanilla Hugo instead.`);
}
// download release from GitHub and verify its checksum
const download = await downloader(getReleaseUrl(version, releaseFile), {
checksumUrl: getReleaseUrl(version, checksumFile),
filename: releaseFile,
destDir: path.join(__dirname, "..", "vendor"),
algorithm: "sha256",
extract: true,
});
// full path to the binary
const installedToPath = path.join(download, binFile);
// ensure hugo[.exe] is executable
fs.chmodSync(installedToPath, 0o755);
console.info(`${logSymbols.success} Hugo installed successfully!`);
console.info(getBinVersion(installedToPath));
// return the full path to our Hugo binary
return installedToPath;
} catch (error) {
// pass whatever error occured along the way to console
console.error(`${logSymbols.error} Hugo installation failed. :(`);
throw error;
});
async function installHugo() {
const version = getPkgVersion();
const releaseFile = getReleaseFilename(version);
const checksumFile = getChecksumFilename(version);
const binFile = getBinFilename();
// stop here if there's nothing we can download
if (!releaseFile) {
throw new Error(`Are you sure this platform is supported? See: https://github.com/gohugoio/hugo/releases/tag/v${version}`);
}
// warn if platform doesn't support Hugo Extended, proceed with vanilla Hugo
if (!isExtended(releaseFile)) {
console.warn(`${logSymbols.info} Hugo Extended isn't supported on this platform, downloading vanilla Hugo instead.`);
}
// download release from GitHub and verify its checksum
const download = await downloader(getReleaseUrl(version, releaseFile), {
checksumUrl: getReleaseUrl(version, checksumFile),
filename: releaseFile,
destDir: "vendor",
algorithm: "sha256",
extract: true,
});
// ensure hugo[.exe] is executable
fs.chmodSync(path.join(download, binFile), 0o755);
// return the full path to our Hugo binary
return path.join(download, binFile);
}
export default install;

View File

@ -1,11 +1,16 @@
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
import { execFileSync } from "child_process";
import { readPackageUpSync } from "read-pkg-up";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// This package's version number (should) always match the Hugo release we want.
// We check for a `hugoVersion` field in package.json just in case it doesn't
// match in the future (from pushing an emergency package update, etc.).
export function getPkgVersion() {
const { packageJson } = readPackageUpSync();
const { packageJson } = readPackageUpSync({ cwd: __dirname });
return packageJson.hugoVersion || packageJson.version;
}
@ -19,6 +24,16 @@ export function getBinFilename() {
return process.platform === "win32" ? "hugo.exe" : "hugo";
}
// Simple shortcut to ./vendor/hugo[.exe] from package root.
export function getBinPath() {
return path.join(
__dirname,
"..",
"vendor",
getBinFilename(),
);
}
// Returns the output of the `hugo version` command, i.e.:
// "hugo v0.88.1-5BC54738+extended darwin/arm64 BuildDate=..."
export function getBinVersion(bin) {
@ -26,6 +41,22 @@ export function getBinVersion(bin) {
return stdout.toString().trim();
}
// Simply detect if the given file exists.
export function doesBinExist(bin) {
try {
if (fs.existsSync(bin)) {
return true;
}
} catch (error) {
// something bad happened besides Hugo not existing
if (error.code !== "ENOENT") {
throw error;
}
return false;
}
}
// Hugo Extended supports: macOS x64, macOS ARM64, Linux x64, Windows x64.
// all other combos fall back to vanilla Hugo. There are surely much better ways
// to do this but this is easy to read/update. :)

View File

@ -17,6 +17,7 @@
"files": [
"index.js",
"index.d.ts",
"postinstall.js",
"lib"
],
"bin": {
@ -33,11 +34,12 @@
},
"devDependencies": {
"@jakejarvis/eslint-config": "*",
"del": "^6.0.0",
"eslint": "^8.1.0",
"mocha": "^9.1.3"
},
"scripts": {
"postinstall": "node lib/install.js",
"postinstall": "node postinstall.js",
"test": "eslint . && mocha"
},
"engines": {

4
postinstall.js Normal file
View File

@ -0,0 +1,4 @@
import install from "./lib/install.js";
// Install Hugo right off the bat.
(async () => await install())();

View File

@ -1,12 +1,33 @@
/* eslint-env node, mocha */
import path from "path";
import { execFile } from "child_process";
import assert from "assert";
import del from "del";
import hugo from "../index.js";
import { getBinPath } from "../lib/utils.js";
it("Hugo exists and runs?", async function () {
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
assert(execFile(hugo, ["env"], function (error, stdout) {
const hugoPath = await hugo();
assert(execFile(hugoPath, ["env"], function (error, stdout) {
if (error) {
throw error;
}
console.log(stdout);
}));
});
it("Hugo doesn't exist, install it instead of throwing an error", async function () {
this.timeout(30000); // increase timeout to an excessive 30 seconds for CI
// delete binary to ensure it's auto-reinstalled
await del(path.dirname(getBinPath()));
const hugoPath = await hugo();
assert(execFile(hugoPath, ["version"], function (error, stdout) {
if (error) {
throw error;
}

View File

@ -3,23 +3,23 @@
"@babel/code-frame@^7.0.0":
version "7.15.8"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503"
integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431"
integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==
dependencies:
"@babel/highlight" "^7.14.5"
"@babel/highlight" "^7.16.0"
"@babel/helper-validator-identifier@^7.14.5":
"@babel/helper-validator-identifier@^7.15.7":
version "7.15.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
"@babel/highlight@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
"@babel/highlight@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a"
integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==
dependencies:
"@babel/helper-validator-identifier" "^7.14.5"
"@babel/helper-validator-identifier" "^7.15.7"
chalk "^2.0.0"
js-tokens "^4.0.0"
@ -113,9 +113,9 @@
"@types/node" "*"
"@types/node@*":
version "16.11.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.1.tgz#2e50a649a50fc403433a14f829eface1a3443e97"
integrity sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==
version "16.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
"@types/normalize-package-data@^2.4.1":
version "2.4.1"
@ -687,9 +687,9 @@ esrecurse@^4.3.0:
estraverse "^5.2.0"
estraverse@^5.1.0, estraverse@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
esutils@^2.0.2:
version "2.0.3"
@ -892,9 +892,9 @@ glob@^7.1.3:
path-is-absolute "^1.0.0"
globals@^13.6.0, globals@^13.9.0:
version "13.11.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7"
integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==
version "13.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e"
integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==
dependencies:
type-fest "^0.20.2"
@ -1171,9 +1171,9 @@ jsonfile@^6.0.1:
graceful-fs "^4.1.6"
keyv@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254"
integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==
version "4.0.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.4.tgz#f040b236ea2b06ed15ed86fbef8407e1a1c8e376"
integrity sha512-vqNHbAc8BBsxk+7QBYLW0Y219rWcClspR6WSeoHYKG5mnsSoOH+BL1pWq02DDCVdvvuUny5rkBlzMRzoqc+GIg==
dependencies:
json-buffer "3.0.1"
@ -1816,9 +1816,9 @@ type-fest@^1.0.1:
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
type-fest@^2.0.0, type-fest@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.5.1.tgz#17ba4f36a6abfabf0a92005d045dca77564607b0"
integrity sha512-JDcsxbLR6Z6OcL7TnGAAAGQrY4g7Q4EEALMT4Kp6FQuIc0JLQvOF3l7ejFvx8o5GmLlfMseTWUL++sYFP+o8kw==
version "2.5.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.5.2.tgz#d6a5247b8019716b300d9023fa7b1b02016dd864"
integrity sha512-WMbytmAs5PUTqwGJRE+WoRrD2S0bYFtHX8k4Y/1l18CG5kqA3keJud9pPQ/r30FE9n8XRFCXF9BbccHIZzRYJw==
unbzip2-stream@^1.0.9:
version "1.4.3"