hugo-extended 0.154.3 → 0.154.4
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.
- package/README.md +220 -65
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +28 -0
- package/dist/generated/flags.json +1143 -0
- package/dist/generated/types.d.mts +383 -0
- package/dist/generated/types.mjs +1 -0
- package/dist/generated/types.ts +490 -0
- package/dist/hugo.d.mts +240 -0
- package/dist/hugo.mjs +246 -0
- package/dist/lib/args.d.mts +30 -0
- package/dist/lib/args.mjs +126 -0
- package/dist/lib/env.d.mts +62 -0
- package/dist/lib/env.mjs +138 -0
- package/dist/lib/install.d.mts +66 -0
- package/dist/lib/install.mjs +190 -0
- package/dist/lib/utils.d.mts +123 -0
- package/dist/lib/utils.mjs +191 -0
- package/package.json +52 -26
- package/postinstall.js +57 -3
- package/index.d.ts +0 -7
- package/index.js +0 -22
- package/lib/cli.js +0 -15
- package/lib/install.js +0 -144
- package/lib/utils.js +0 -109
- /package/{LICENSE.md → LICENSE} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { getEnvConfig } from "./env.mjs";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
//#region src/lib/utils.ts
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
/**
|
|
10
|
+
* The first Hugo version that uses .pkg installers for macOS.
|
|
11
|
+
* Versions before this use .tar.gz archives.
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/gohugoio/hugo/issues/14135
|
|
14
|
+
*/
|
|
15
|
+
const MACOS_PKG_MIN_VERSION = "0.153.0";
|
|
16
|
+
/**
|
|
17
|
+
* Compares two semver version strings.
|
|
18
|
+
*
|
|
19
|
+
* @param a - First version string (e.g., "0.153.0")
|
|
20
|
+
* @param b - Second version string (e.g., "0.152.1")
|
|
21
|
+
* @returns -1 if a < b, 0 if a === b, 1 if a > b
|
|
22
|
+
*/
|
|
23
|
+
function compareVersions(a, b) {
|
|
24
|
+
const partsA = a.split(".").map((n) => Number.parseInt(n, 10));
|
|
25
|
+
const partsB = b.split(".").map((n) => Number.parseInt(n, 10));
|
|
26
|
+
const maxLen = Math.max(partsA.length, partsB.length);
|
|
27
|
+
for (let i = 0; i < maxLen; i++) {
|
|
28
|
+
const numA = partsA[i] ?? 0;
|
|
29
|
+
const numB = partsB[i] ?? 0;
|
|
30
|
+
if (numA < numB) return -1;
|
|
31
|
+
if (numA > numB) return 1;
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a version uses .pkg installers for macOS.
|
|
37
|
+
* Hugo v0.153.0+ uses .pkg, earlier versions use .tar.gz.
|
|
38
|
+
*
|
|
39
|
+
* @param version - The Hugo version to check
|
|
40
|
+
* @returns true if the version uses .pkg installers on macOS
|
|
41
|
+
*/
|
|
42
|
+
function usesMacOSPkg(version) {
|
|
43
|
+
return compareVersions(version, MACOS_PKG_MIN_VERSION) >= 0;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Gets the Hugo version to install.
|
|
47
|
+
*
|
|
48
|
+
* Resolution order:
|
|
49
|
+
* 1. HUGO_OVERRIDE_VERSION environment variable (if set)
|
|
50
|
+
* 2. `hugoVersion` field in package.json (for emergency overrides)
|
|
51
|
+
* 3. `version` field in package.json (should match Hugo release)
|
|
52
|
+
*
|
|
53
|
+
* @throws {Error} If package.json cannot be found and no override is set
|
|
54
|
+
* @returns The version string (e.g., "0.88.1")
|
|
55
|
+
*/
|
|
56
|
+
function getPkgVersion() {
|
|
57
|
+
const envConfig = getEnvConfig();
|
|
58
|
+
if (envConfig.overrideVersion) return envConfig.overrideVersion;
|
|
59
|
+
const packageJsonPath = path.join(__dirname, "..", "..", "package.json");
|
|
60
|
+
try {
|
|
61
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
62
|
+
return packageJson.hugoVersion || packageJson.version;
|
|
63
|
+
} catch {
|
|
64
|
+
throw new Error(`Could not find or read package.json at ${packageJsonPath}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generates the full URL to a Hugo release file.
|
|
69
|
+
*
|
|
70
|
+
* By default, downloads from GitHub releases. Can be overridden with
|
|
71
|
+
* HUGO_MIRROR_BASE_URL for mirrors or air-gapped environments.
|
|
72
|
+
*
|
|
73
|
+
* @param version - The Hugo version number (e.g., "0.88.1")
|
|
74
|
+
* @param filename - The release filename (e.g., "hugo_extended_0.88.1_darwin-universal.pkg")
|
|
75
|
+
* @returns The complete download URL for the release file
|
|
76
|
+
*/
|
|
77
|
+
function getReleaseUrl(version, filename) {
|
|
78
|
+
const envConfig = getEnvConfig();
|
|
79
|
+
if (envConfig.downloadBaseUrl) return `${envConfig.downloadBaseUrl.replace(/\/$/, "")}/${filename}`;
|
|
80
|
+
return `https://github.com/gohugoio/hugo/releases/download/v${version}/${filename}`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Gets the Hugo binary filename for the current platform.
|
|
84
|
+
*
|
|
85
|
+
* @returns "hugo.exe" on Windows, "hugo" on all other platforms
|
|
86
|
+
*/
|
|
87
|
+
function getBinFilename() {
|
|
88
|
+
return process.platform === "win32" ? "hugo.exe" : "hugo";
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Gets the absolute path to the Hugo binary.
|
|
92
|
+
*
|
|
93
|
+
* Resolution order:
|
|
94
|
+
* 1. HUGO_BIN_PATH environment variable (if set)
|
|
95
|
+
* 2. Local bin directory (./bin/hugo or ./bin/hugo.exe)
|
|
96
|
+
*
|
|
97
|
+
* @returns The absolute path to hugo binary
|
|
98
|
+
*/
|
|
99
|
+
function getBinPath() {
|
|
100
|
+
const envConfig = getEnvConfig();
|
|
101
|
+
if (envConfig.binPath) return envConfig.binPath;
|
|
102
|
+
return path.join(__dirname, "..", "..", "bin", getBinFilename());
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Executes the Hugo binary and returns its version string.
|
|
106
|
+
*
|
|
107
|
+
* @param bin - The absolute path to the Hugo binary
|
|
108
|
+
* @returns The version output string (e.g., "hugo v0.88.1-5BC54738+extended darwin/arm64 BuildDate=...")
|
|
109
|
+
* @throws {Error} If the binary cannot be executed
|
|
110
|
+
*/
|
|
111
|
+
function getBinVersion(bin) {
|
|
112
|
+
return execFileSync(bin, ["version"]).toString().trim();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Checks if the Hugo binary exists at the specified path.
|
|
116
|
+
*
|
|
117
|
+
* @param bin - The absolute path to check for the Hugo binary
|
|
118
|
+
* @returns `true` if the file exists, `false` if it doesn't
|
|
119
|
+
* @throws {Error} If an unexpected error occurs (other than ENOENT)
|
|
120
|
+
*/
|
|
121
|
+
function doesBinExist(bin) {
|
|
122
|
+
try {
|
|
123
|
+
if (fs.existsSync(bin)) return true;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") throw error;
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Determines the correct Hugo release filename for the current platform and architecture.
|
|
132
|
+
*
|
|
133
|
+
* Hugo Extended is available for:
|
|
134
|
+
* - macOS: x64 and ARM64 (universal binaries as of v0.102.0)
|
|
135
|
+
* - Linux: x64 and ARM64
|
|
136
|
+
* - Windows: x64 only
|
|
137
|
+
*
|
|
138
|
+
* Other platform/architecture combinations fall back to vanilla Hugo where available.
|
|
139
|
+
* Set HUGO_NO_EXTENDED=1 to force vanilla Hugo even on platforms that support Extended.
|
|
140
|
+
*
|
|
141
|
+
* Note: macOS uses .pkg installers starting from v0.153.0. Earlier versions use .tar.gz.
|
|
142
|
+
*
|
|
143
|
+
* @param version - The Hugo version number (e.g., "0.88.1")
|
|
144
|
+
* @returns The release filename if supported (e.g., "hugo_extended_0.88.1_darwin-universal.pkg"),
|
|
145
|
+
* or `null` if the platform/architecture combination is not supported
|
|
146
|
+
*/
|
|
147
|
+
function getReleaseFilename(version) {
|
|
148
|
+
const { platform, arch } = process;
|
|
149
|
+
const forceStandard = getEnvConfig().forceStandard;
|
|
150
|
+
const edition = (extended, standard) => forceStandard ? standard : extended;
|
|
151
|
+
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) {
|
|
152
|
+
if (usesMacOSPkg(version)) return edition(`hugo_extended_${version}_darwin-universal.pkg`, `hugo_${version}_darwin-universal.pkg`);
|
|
153
|
+
return edition(`hugo_extended_${version}_darwin-universal.tar.gz`, `hugo_${version}_darwin-universal.tar.gz`);
|
|
154
|
+
}
|
|
155
|
+
return platform === "win32" && arch === "x64" ? edition(`hugo_extended_${version}_windows-amd64.zip`, `hugo_${version}_windows-amd64.zip`) : platform === "win32" && arch === "arm64" ? `hugo_${version}_windows-arm64.zip` : platform === "linux" && arch === "x64" ? edition(`hugo_extended_${version}_linux-amd64.tar.gz`, `hugo_${version}_linux-amd64.tar.gz`) : platform === "linux" && arch === "arm64" ? edition(`hugo_extended_${version}_linux-arm64.tar.gz`, `hugo_${version}_linux-arm64.tar.gz`) : platform === "freebsd" && arch === "x64" ? `hugo_${version}_freebsd-amd64.tar.gz` : platform === "openbsd" && arch === "x64" ? `hugo_${version}_openbsd-amd64.tar.gz` : null;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Generates the checksums filename for a given Hugo version.
|
|
159
|
+
*
|
|
160
|
+
* @param version - The Hugo version number (e.g., "0.88.1")
|
|
161
|
+
* @returns The checksums filename (e.g., "hugo_0.88.1_checksums.txt")
|
|
162
|
+
*/
|
|
163
|
+
function getChecksumFilename(version) {
|
|
164
|
+
return `hugo_${version}_checksums.txt`;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Determines if a release filename corresponds to Hugo Extended or vanilla Hugo.
|
|
168
|
+
*
|
|
169
|
+
* @param releaseFile - The release filename to check (e.g., "hugo_extended_0.88.1_darwin-universal.pkg")
|
|
170
|
+
* @returns `true` if the release is Hugo Extended, `false` if it's vanilla Hugo
|
|
171
|
+
*/
|
|
172
|
+
function isExtended(releaseFile) {
|
|
173
|
+
return releaseFile.startsWith("hugo_extended_");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Logger utility that respects the HUGO_QUIET setting.
|
|
177
|
+
*/
|
|
178
|
+
const logger = {
|
|
179
|
+
info: (message) => {
|
|
180
|
+
if (!getEnvConfig().quiet) console.info(message);
|
|
181
|
+
},
|
|
182
|
+
warn: (message) => {
|
|
183
|
+
if (!getEnvConfig().quiet) console.warn(`⚠ ${message}`);
|
|
184
|
+
},
|
|
185
|
+
error: (message) => {
|
|
186
|
+
console.error(`✖ ${message}`);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
export { compareVersions, doesBinExist, getBinFilename, getBinPath, getBinVersion, getChecksumFilename, getPkgVersion, getReleaseFilename, getReleaseUrl, isExtended, logger, usesMacOSPkg };
|
package/package.json
CHANGED
|
@@ -1,50 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hugo-extended",
|
|
3
|
-
"version": "0.154.
|
|
3
|
+
"version": "0.154.4",
|
|
4
4
|
"description": "✏️ Plug-and-play binary wrapper for Hugo Extended, the awesomest static-site generator.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jakejarvis/hugo-extended.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/jakejarvis/hugo-extended/issues"
|
|
12
|
+
},
|
|
7
13
|
"author": {
|
|
8
14
|
"name": "Jake Jarvis",
|
|
9
15
|
"email": "jake@jarv.is",
|
|
10
16
|
"url": "https://github.com/jakejarvis"
|
|
11
17
|
},
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/jakejarvis/hugo-extended.git"
|
|
15
|
-
},
|
|
16
18
|
"publishConfig": {
|
|
17
19
|
"access": "public"
|
|
18
20
|
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/hugo.mjs",
|
|
23
|
+
"module": "./dist/hugo.mjs",
|
|
24
|
+
"types": "./dist/hugo.d.mts",
|
|
25
|
+
"bin": {
|
|
26
|
+
"hugo": "dist/cli.mjs",
|
|
27
|
+
"hugo-extended": "dist/cli.mjs"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/hugo.d.mts",
|
|
32
|
+
"import": "./dist/hugo.mjs",
|
|
33
|
+
"default": "./dist/hugo.mjs"
|
|
34
|
+
},
|
|
35
|
+
"./types": {
|
|
36
|
+
"types": "./dist/generated/types.d.mts",
|
|
37
|
+
"default": "./dist/generated/types.mjs"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
19
40
|
"files": [
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"postinstall.js",
|
|
23
|
-
"lib"
|
|
41
|
+
"dist",
|
|
42
|
+
"postinstall.js"
|
|
24
43
|
],
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"dev": "tsdown --watch",
|
|
47
|
+
"generate-types": "bun scripts/generate-types.ts",
|
|
48
|
+
"lint": "biome check",
|
|
49
|
+
"lint:fix": "biome check --write",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"test:unit": "vitest run tests/unit",
|
|
54
|
+
"test:integration": "vitest run tests/integration",
|
|
55
|
+
"test:e2e": "vitest run tests/e2e",
|
|
56
|
+
"test:coverage": "vitest run --coverage",
|
|
57
|
+
"postinstall": "node postinstall.js",
|
|
58
|
+
"prepublishOnly": "npm run generate-types && npm run build"
|
|
28
59
|
},
|
|
29
|
-
"type": "module",
|
|
30
|
-
"exports": "./index.js",
|
|
31
|
-
"types": "./index.d.ts",
|
|
32
60
|
"dependencies": {
|
|
33
61
|
"adm-zip": "^0.5.16",
|
|
34
|
-
"log-symbols": "^7.0.1",
|
|
35
|
-
"read-package-up": "^12.0.0",
|
|
36
62
|
"tar": "^7.5.2"
|
|
37
63
|
},
|
|
38
64
|
"devDependencies": {
|
|
65
|
+
"@biomejs/biome": "^2.3.11",
|
|
39
66
|
"@types/adm-zip": "^0.5.7",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
"test": "eslint . && mocha"
|
|
67
|
+
"@types/node": "^25.0.3",
|
|
68
|
+
"@types/tar": "^6.1.13",
|
|
69
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
70
|
+
"tinyexec": "^1.0.2",
|
|
71
|
+
"tsdown": "^0.19.0-beta.5",
|
|
72
|
+
"typescript": "^5.9.3",
|
|
73
|
+
"vitest": "^4.0.16"
|
|
48
74
|
},
|
|
49
75
|
"engines": {
|
|
50
76
|
"node": ">=18.17"
|
package/postinstall.js
CHANGED
|
@@ -1,4 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Postinstall wrapper script that properly handles errors during Hugo binary installation.
|
|
9
|
+
* This script imports and executes the install function, logging any errors with full stack traces
|
|
10
|
+
* and exiting with a non-zero code on failure.
|
|
11
|
+
*
|
|
12
|
+
* During development/CI (before build), the dist folder won't exist and this script will exit gracefully.
|
|
13
|
+
* For published packages, the dist folder is included and installation will proceed.
|
|
14
|
+
*
|
|
15
|
+
* Environment variables:
|
|
16
|
+
* - HUGO_SKIP_DOWNLOAD: Skip installation entirely (useful for CI caching, Docker layers)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const installPath = join(__dirname, "dist", "lib", "install.mjs");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if an environment variable is set to a truthy value.
|
|
24
|
+
* @param {string} name - Environment variable name
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
function isEnvTruthy(name) {
|
|
28
|
+
const value = process.env[name];
|
|
29
|
+
if (!value) return false;
|
|
30
|
+
return ["1", "true", "yes", "on"].includes(value.toLowerCase().trim());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function run() {
|
|
34
|
+
// Skip installation if HUGO_SKIP_DOWNLOAD is set
|
|
35
|
+
if (isEnvTruthy("HUGO_SKIP_DOWNLOAD")) {
|
|
36
|
+
console.log("Skipping Hugo installation (HUGO_SKIP_DOWNLOAD is set)");
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Skip installation if dist folder doesn't exist (development/CI environment)
|
|
41
|
+
if (!existsSync(installPath)) {
|
|
42
|
+
console.log(
|
|
43
|
+
"Skipping Hugo installation (dist not found - likely in CI or development environment)",
|
|
44
|
+
);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const m = await import("./dist/lib/install.mjs");
|
|
50
|
+
await m.default();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Hugo installation failed:");
|
|
53
|
+
console.error(error);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
run();
|
package/index.d.ts
DELETED
package/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import logSymbols from "log-symbols";
|
|
2
|
-
import install from "./lib/install.js";
|
|
3
|
-
import { getBinPath, doesBinExist } from "./lib/utils.js";
|
|
4
|
-
|
|
5
|
-
const hugo = async () => {
|
|
6
|
-
const bin = getBinPath();
|
|
7
|
-
|
|
8
|
-
// A fix for fleeting ENOENT errors, where Hugo seems to disappear. For now,
|
|
9
|
-
// just reinstall Hugo when it's missing and then continue normally like
|
|
10
|
-
// nothing happened.
|
|
11
|
-
// See: https://github.com/jakejarvis/hugo-extended/issues/81
|
|
12
|
-
if (!doesBinExist(bin)) {
|
|
13
|
-
// Hugo isn't there for some reason. Try re-installing.
|
|
14
|
-
console.info(`${logSymbols.info} Hugo is missing, reinstalling now...`);
|
|
15
|
-
await install();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return bin;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// The only thing this module really exports is the absolute path to Hugo:
|
|
22
|
-
export default hugo;
|
package/lib/cli.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { spawn } from "child_process";
|
|
4
|
-
import hugo from "../index.js";
|
|
5
|
-
|
|
6
|
-
(async () => {
|
|
7
|
-
const args = process.argv.slice(2);
|
|
8
|
-
const bin = await hugo();
|
|
9
|
-
|
|
10
|
-
spawn(bin, args, { stdio: "inherit" })
|
|
11
|
-
.on("exit", (code) => {
|
|
12
|
-
// forward Hugo's exit code so this module itself reports success/failure
|
|
13
|
-
process.exitCode = code;
|
|
14
|
-
});
|
|
15
|
-
})();
|
package/lib/install.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { spawnSync } from "child_process";
|
|
5
|
-
import { pipeline } from "stream/promises";
|
|
6
|
-
import crypto from "crypto";
|
|
7
|
-
import * as tar from "tar";
|
|
8
|
-
import AdmZip from "adm-zip";
|
|
9
|
-
import logSymbols from "log-symbols";
|
|
10
|
-
import {
|
|
11
|
-
getPkgVersion,
|
|
12
|
-
getReleaseUrl,
|
|
13
|
-
getReleaseFilename,
|
|
14
|
-
getBinFilename,
|
|
15
|
-
getBinVersion,
|
|
16
|
-
getChecksumFilename,
|
|
17
|
-
isExtended,
|
|
18
|
-
} from "./utils.js";
|
|
19
|
-
|
|
20
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
|
|
22
|
-
async function downloadFile(url, dest) {
|
|
23
|
-
const response = await fetch(url);
|
|
24
|
-
if (!response.ok) {
|
|
25
|
-
throw new Error(`Failed to download ${url}: ${response.statusText}`);
|
|
26
|
-
}
|
|
27
|
-
await pipeline(response.body, fs.createWriteStream(dest));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function verifyChecksum(filePath, checksumUrl, filename) {
|
|
31
|
-
const response = await fetch(checksumUrl);
|
|
32
|
-
if (!response.ok) {
|
|
33
|
-
throw new Error(`Failed to download checksums: ${response.statusText}`);
|
|
34
|
-
}
|
|
35
|
-
const checksums = await response.text();
|
|
36
|
-
|
|
37
|
-
// checksums file format: "sha256 filename"
|
|
38
|
-
const expectedChecksum = checksums
|
|
39
|
-
.split("\n")
|
|
40
|
-
.find((line) => line.endsWith(filename))
|
|
41
|
-
?.split(/\s+/)[0];
|
|
42
|
-
|
|
43
|
-
if (!expectedChecksum) {
|
|
44
|
-
throw new Error(`Checksum for ${filename} not found in checksums file.`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const fileBuffer = fs.readFileSync(filePath);
|
|
48
|
-
const hash = crypto.createHash("sha256");
|
|
49
|
-
hash.update(fileBuffer);
|
|
50
|
-
const actualChecksum = hash.digest("hex");
|
|
51
|
-
|
|
52
|
-
if (actualChecksum !== expectedChecksum) {
|
|
53
|
-
throw new Error(`Checksum mismatch! Expected ${expectedChecksum}, got ${actualChecksum}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function install() {
|
|
58
|
-
try {
|
|
59
|
-
const version = getPkgVersion();
|
|
60
|
-
const releaseFile = getReleaseFilename(version);
|
|
61
|
-
const checksumFile = getChecksumFilename(version);
|
|
62
|
-
const binFile = getBinFilename();
|
|
63
|
-
|
|
64
|
-
if (!releaseFile) {
|
|
65
|
-
throw new Error(`Are you sure this platform is supported? See: https://github.com/gohugoio/hugo/releases/tag/v${version}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!isExtended(releaseFile)) {
|
|
69
|
-
console.warn(`${logSymbols.info} Hugo Extended isn't supported on this platform, downloading vanilla Hugo instead.`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Prepare vendor directory
|
|
73
|
-
const vendorDir = path.join(__dirname, "..", "vendor");
|
|
74
|
-
if (!fs.existsSync(vendorDir)) {
|
|
75
|
-
fs.mkdirSync(vendorDir, { recursive: true });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const releaseUrl = getReleaseUrl(version, releaseFile);
|
|
79
|
-
const checksumUrl = getReleaseUrl(version, checksumFile);
|
|
80
|
-
const downloadPath = path.join(vendorDir, releaseFile);
|
|
81
|
-
|
|
82
|
-
console.info(`${logSymbols.info} Downloading ${releaseFile}...`);
|
|
83
|
-
await downloadFile(releaseUrl, downloadPath);
|
|
84
|
-
|
|
85
|
-
console.info(`${logSymbols.info} Verifying checksum...`);
|
|
86
|
-
await verifyChecksum(downloadPath, checksumUrl, releaseFile);
|
|
87
|
-
|
|
88
|
-
if (process.platform === "darwin") {
|
|
89
|
-
console.info(`${logSymbols.info} Installing ${releaseFile} (requires sudo)...`);
|
|
90
|
-
// Run MacOS installer
|
|
91
|
-
const result = spawnSync("sudo", ["installer", "-pkg", downloadPath, "-target", "/"], {
|
|
92
|
-
stdio: "inherit",
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
if (result.error) throw result.error;
|
|
96
|
-
if (result.status !== 0) {
|
|
97
|
-
throw new Error(`Installer failed with exit code ${result.status}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Cleanup downloaded pkg
|
|
101
|
-
fs.unlinkSync(downloadPath);
|
|
102
|
-
} else {
|
|
103
|
-
console.info(`${logSymbols.info} Extracting...`);
|
|
104
|
-
|
|
105
|
-
if (releaseFile.endsWith(".zip")) {
|
|
106
|
-
const zip = new AdmZip(downloadPath);
|
|
107
|
-
zip.extractAllTo(vendorDir, true);
|
|
108
|
-
|
|
109
|
-
// Cleanup zip
|
|
110
|
-
fs.unlinkSync(downloadPath);
|
|
111
|
-
} else if (releaseFile.endsWith(".tar.gz")) {
|
|
112
|
-
await tar.x({
|
|
113
|
-
file: downloadPath,
|
|
114
|
-
cwd: vendorDir,
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Cleanup tar.gz
|
|
118
|
-
fs.unlinkSync(downloadPath);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const binPath = path.join(vendorDir, binFile);
|
|
122
|
-
if (fs.existsSync(binPath)) {
|
|
123
|
-
fs.chmodSync(binPath, 0o755);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
console.info(`${logSymbols.success} Hugo installed successfully!`);
|
|
128
|
-
|
|
129
|
-
// Check version
|
|
130
|
-
if (process.platform === "darwin") {
|
|
131
|
-
console.info(getBinVersion("/usr/local/bin/hugo"));
|
|
132
|
-
return "/usr/local/bin/hugo";
|
|
133
|
-
} else {
|
|
134
|
-
const binPath = path.join(vendorDir, binFile);
|
|
135
|
-
console.info(getBinVersion(binPath));
|
|
136
|
-
return binPath;
|
|
137
|
-
}
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error(`${logSymbols.error} Hugo installation failed. :(`);
|
|
140
|
-
throw error;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export default install;
|
package/lib/utils.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { execFileSync } from "child_process";
|
|
5
|
-
import { readPackageUpSync } from "read-package-up";
|
|
6
|
-
|
|
7
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
|
|
9
|
-
// This package's version number (should) always match the Hugo release we want.
|
|
10
|
-
// We check for a `hugoVersion` field in package.json just in case it doesn't
|
|
11
|
-
// match in the future (from pushing an emergency package update, etc.).
|
|
12
|
-
export function getPkgVersion() {
|
|
13
|
-
const { packageJson } = readPackageUpSync({ cwd: __dirname });
|
|
14
|
-
return packageJson.hugoVersion || packageJson.version;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Generate the full GitHub URL to a given release file.
|
|
18
|
-
export function getReleaseUrl(version, filename) {
|
|
19
|
-
return `https://github.com/gohugoio/hugo/releases/download/v${version}/${filename}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Binary is named `hugo.exe` on Windows, and simply `hugo` otherwise.
|
|
23
|
-
export function getBinFilename() {
|
|
24
|
-
return process.platform === "win32" ? "hugo.exe" : "hugo";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Simple shortcut to ./vendor/hugo[.exe] from package root.
|
|
28
|
-
export function getBinPath() {
|
|
29
|
-
if (process.platform === "darwin") return "/usr/local/bin/hugo";
|
|
30
|
-
|
|
31
|
-
return path.join(
|
|
32
|
-
__dirname,
|
|
33
|
-
"..",
|
|
34
|
-
"vendor",
|
|
35
|
-
getBinFilename()
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Returns the output of the `hugo version` command, i.e.:
|
|
40
|
-
// "hugo v0.88.1-5BC54738+extended darwin/arm64 BuildDate=..."
|
|
41
|
-
export function getBinVersion(bin) {
|
|
42
|
-
const stdout = execFileSync(bin, ["version"]);
|
|
43
|
-
return stdout.toString().trim();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Simply detect if the given file exists.
|
|
47
|
-
export function doesBinExist(bin) {
|
|
48
|
-
try {
|
|
49
|
-
if (fs.existsSync(bin)) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
// something bad happened besides Hugo not existing
|
|
54
|
-
if (error.code !== "ENOENT") {
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Hugo Extended supports: macOS x64 / ARM64, Linux x64 / ARM64, Windows x64.
|
|
63
|
-
// All other combos fall back to vanilla Hugo. There are surely much better ways
|
|
64
|
-
// to do this but this is easy to read/update. :)
|
|
65
|
-
export function getReleaseFilename(version) {
|
|
66
|
-
const { platform, arch } = process;
|
|
67
|
-
|
|
68
|
-
const filename =
|
|
69
|
-
// macOS: as of 0.102.0, binaries are universal
|
|
70
|
-
platform === "darwin" && arch === "x64"
|
|
71
|
-
? `hugo_extended_${version}_darwin-universal.pkg`
|
|
72
|
-
: platform === "darwin" && arch === "arm64"
|
|
73
|
-
? `hugo_extended_${version}_darwin-universal.pkg`
|
|
74
|
-
|
|
75
|
-
// Windows
|
|
76
|
-
: platform === "win32" && arch === "x64"
|
|
77
|
-
? `hugo_extended_${version}_windows-amd64.zip`
|
|
78
|
-
: platform === "win32" && arch === "arm64"
|
|
79
|
-
? `hugo_${version}_windows-arm64.zip`
|
|
80
|
-
|
|
81
|
-
// Linux
|
|
82
|
-
: platform === "linux" && arch === "x64"
|
|
83
|
-
? `hugo_extended_${version}_linux-amd64.tar.gz`
|
|
84
|
-
: platform === "linux" && arch === "arm64"
|
|
85
|
-
? `hugo_extended_${version}_linux-arm64.tar.gz`
|
|
86
|
-
|
|
87
|
-
// FreeBSD
|
|
88
|
-
: platform === "freebsd" && arch === "x64"
|
|
89
|
-
? `hugo_${version}_freebsd-amd64.tar.gz`
|
|
90
|
-
|
|
91
|
-
// OpenBSD
|
|
92
|
-
: platform === "openbsd" && arch === "x64"
|
|
93
|
-
? `hugo_${version}_openbsd-amd64.tar.gz`
|
|
94
|
-
|
|
95
|
-
// not gonna work :(
|
|
96
|
-
: null;
|
|
97
|
-
|
|
98
|
-
return filename;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Simple formula for the checksums.txt file.
|
|
102
|
-
export function getChecksumFilename(version) {
|
|
103
|
-
return `hugo_${version}_checksums.txt`;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Check if Hugo extended is being downloaded (as opposed to plain Hugo) based on the release filename.
|
|
107
|
-
export function isExtended(releaseFile) {
|
|
108
|
-
return releaseFile.startsWith("hugo_extended_");
|
|
109
|
-
}
|
/package/{LICENSE.md → LICENSE}
RENAMED
|
File without changes
|