hugo-installer
Version:
Installs hugo into your repository.
866 lines (862 loc) • 22.8 kB
JavaScript
import { spawn } from 'node:child_process';
import * as crypto from 'node:crypto';
import decompress from 'decompress';
import { deleteAsync } from 'del';
import * as fs from 'node:fs';
import got from 'got';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
import * as path from 'node:path';
import semver from 'semver';
const binaries = [
{
os: "win32",
arch: "x64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_windows-amd64.zip"
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Windows-64bit.zip"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Windows-64bit.zip"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_Windows-64bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_windows-64bit.zip"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_windows_amd64.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-windows-amd64"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-windows-x64"
}
]
},
{
os: "win32",
arch: "x86",
fileNamePatternHistory: [
{
version: "0.102.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Windows-32bit.zip"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Windows-32bit.zip"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_Windows-32bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_windows-32bit.zip"
},
{
version: "0.15",
fileNamePattern: "{{variant}}_{{version}}_windows_386_32-bit-only.zip"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_windows_386.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-windows-386"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-windows-386"
}
]
},
{
os: "darwin",
arch: "x64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_darwin-universal.tar.gz"
},
{
version: "0.102.0",
fileNamePattern: "{{variant}}_{{version}}_macOS-universal.tar.gz"
},
{
version: "0.89.1",
fileNamePattern: "{{variant}}_{{version}}_macOS-64bit.tar.gz"
},
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_macOS-64bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_macOS-64bit.tar.gz"
},
{
version: "0.18",
fileNamePattern: "{{variant}}_{{version}}_macOS-64bit.zip"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_MacOS-64bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_osx-64bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_darwin_amd64.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-darwin-amd64"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-darwin-x64"
}
]
},
{
os: "darwin",
arch: "x86",
fileNamePatternHistory: [
{
version: "0.75.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_macOS-32bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_macOS-32bit.tar.gz"
},
{
version: "0.18",
fileNamePattern: "{{variant}}_{{version}}_macOS-32bit.zip"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_MacOS-32bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_osx-32bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_darwin_386.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-darwin-386"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-darwin-386"
}
]
},
{
os: "darwin",
arch: "arm",
fileNamePatternHistory: [
{
version: "0.17",
fileNamePattern: null
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_darwin-arm32.tgz"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "darwin",
arch: "arm64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_darwin-universal.tar.gz"
},
{
version: "0.102.0",
fileNamePattern: "{{variant}}_{{version}}_macOS-universal.tar.gz"
},
{
version: "0.89.1",
fileNamePattern: "{{variant}}_{{version}}_macOS-ARM64.tar.gz"
},
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.88.1",
fileNamePattern: "{{variant}}_{{version}}_macOS-ARM64.tar.gz"
},
{
version: "0.88.0",
fileNamePattern: null
},
{
version: "0.81.0",
fileNamePattern: "{{variant}}_{{version}}_macOS-ARM64.tar.gz"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "linux",
arch: "x64",
fileNamePatternHistory: [
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Linux-64bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Linux-64bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_Linux-64bit.tar.gz"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_linux-64bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_linux_amd64.tar.gz"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-linux-amd64"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-linux-x64"
}
]
},
{
os: "linux",
arch: "x86",
fileNamePatternHistory: [
{
version: "0.102.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Linux-32bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Linux-32bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_Linux-32bit.tar.gz"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_linux-32bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_linux_386.tar.gz"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-linux-386"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-linux-386"
}
]
},
{
os: "linux",
arch: "arm",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_linux-arm.tar.gz"
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Linux-ARM.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Linux-ARM.tar.gz"
},
{
version: "0.18",
fileNamePattern: "{{variant}}_{{version}}_Linux_ARM.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_linux_arm.tar.gz"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_linux-arm32.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_linux_arm.tar.gz"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-linux-arm"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "linux",
arch: "arm64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_linux-arm64.tar.gz"
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_Linux-ARM64.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_Linux-ARM64.tar.gz"
},
{
version: "0.17",
fileNamePattern: null
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_linux-arm64.tgz"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "freebsd",
arch: "x64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_freebsd-amd64.tar.gz"
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-64bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_FreeBSD-64bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-64bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_freebsd-64bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_freebsd_amd64.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-freebsd-amd64"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-freebsd-x64"
}
]
},
{
os: "freebsd",
arch: "x86",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-32bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_FreeBSD-32bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-32bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_freebsd-32bit.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_freebsd_386.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-freebsd-386"
},
{
version: "0.7",
fileNamePattern: "{{variant}}-{{version}}-freebsd-386"
}
]
},
{
os: "freebsd",
arch: "arm",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-ARM.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_FreeBSD-ARM.tar.gz"
},
{
version: "0.18",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD_ARM.zip"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_freebsd_arm.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_freebsd-arm32.tgz"
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_freebsd_arm.zip"
},
{
version: "0.8",
fileNamePattern: "{{variant}}-freebsd-arm"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "freebsd",
arch: "arm64",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.81.0",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-ARM64.tar.gz"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "openbsd",
arch: "x64",
fileNamePatternHistory: [
{
version: "0.103.0",
fileNamePattern: "{{variant}}_{{version}}_openbsd-amd64.tar.gz"
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_OpenBSD-64bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_OpenBSD-64bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_OpenBSD-64bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_openbsd-64bit.tgz"
},
{
version: "0.15",
fileNamePattern: "{{variant}}_{{version}}_openbsd_amd64.zip"
},
{
version: "0.10",
fileNamePattern: null
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_openbsd_amd64.zip"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "openbsd",
arch: "x86",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.20.4",
fileNamePattern: "{{variant}}_{{version}}_OpenBSD-32bit.tar.gz"
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_v{{version}}_OpenBSD-32bit.tar.gz"
},
{
version: "0.17",
fileNamePattern: "{{variant}}_{{version}}_OpenBSD-32bit.zip"
},
{
version: "0.16",
fileNamePattern: "{{variant}}_{{version}}_openbsd-32bit.tgz"
},
{
version: "0.15",
fileNamePattern: "{{variant}}_{{version}}_openbsd_386.zip"
},
{
version: "0.10",
fileNamePattern: null
},
{
version: "0.9",
fileNamePattern: "{{variant}}_{{version}}_openbsd_386.zip"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "openbsd",
arch: "arm",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.48",
fileNamePattern: "{{variant}}_{{version}}_OpenBSD-ARM.tar.gz"
},
{
version: "0.7",
fileNamePattern: null
}
]
},
{
os: "freebsd",
arch: "arm64",
fileNamePatternHistory: [
{
version: "0.89.0",
fileNamePattern: null
},
{
version: "0.81.0",
fileNamePattern: "{{variant}}_{{version}}_FreeBSD-ARM64.tar.gz"
},
{
version: "0.7",
fileNamePattern: null
}
]
}
];
const checksumFilePatternHistory = [
{
version: "0.56.0",
fileNamePattern: "{{variant}}_{{version}}_checksums.txt",
useSpecificVariant: false
},
{
version: "0.43",
fileNamePattern: "{{variant}}_{{version}}_checksums.txt",
useSpecificVariant: true
},
{
version: "0.27",
fileNamePattern: "{{variant}}_{{version}}_checksums.txt",
useSpecificVariant: false
},
{
version: "0.20.3",
fileNamePattern: "{{variant}}_checksums.txt",
useSpecificVariant: false
},
{
version: "0.7",
fileNamePattern: null,
useSpecificVariant: false
}
];
var hugoReleasesMeta = {
binaries: binaries,
checksumFilePatternHistory: checksumFilePatternHistory
};
const createFetchOptions = ({ httpProxy, httpsProxy, }) => {
return {
agent: Object.assign(Object.assign({}, (httpProxy === null
? {}
: {
http: new HttpProxyAgent({
proxy: httpProxy,
}),
})), (httpsProxy === null
? {}
: {
https: new HttpsProxyAgent({
proxy: httpsProxy,
}),
})),
};
};
const cleanupAndPrepareDestination = async ({ destination }) => {
await deleteAsync(path.join(destination, '**'));
await fs.promises.mkdir(destination, { recursive: true });
};
const fetchBinary = async ({ arch, downloadUrl, extended, httpProxy, httpsProxy, os, version, }) => {
const binaryMeta = hugoReleasesMeta.binaries.find((binary) => {
return binary.arch === arch && binary.os === os;
}) || null;
if (binaryMeta === null) {
throw new Error(`A hugo release for os="${os}" and arch="${arch}" is not known to "hugo-installer". If you think this is a bug, feel free to open a GitHub issue here: https://github.com/dominique-mueller/hugo-installer/issues/new.`);
}
const binaryFileNamePattern = binaryMeta.fileNamePatternHistory.reverse().reduce((fileNamePattern, fileNamePatternHistoryItem) => {
return semver.gte(semver.coerce(version), semver.coerce(fileNamePatternHistoryItem.version))
? fileNamePatternHistoryItem
: fileNamePattern;
}, null);
if (binaryFileNamePattern === null || binaryFileNamePattern.fileNamePattern === null) {
throw new Error(`A Hugo binary file for version="${version}"${extended ? ' (extended)' : ''}, os="${os}" and arch="${arch}" is not known to "hugo-installer". If you think this is a bug, feel free to open a GitHub issue here: https://github.com/dominique-mueller/hugo-installer/issues/new.`);
}
const binaryFileName = binaryFileNamePattern.fileNamePattern
.replace('{{variant}}', extended ? 'hugo_extended' : 'hugo')
.replace('{{version}}', version);
const binaryUrl = new URL(`v${version}/${binaryFileName}`, downloadUrl).toString();
console.log(`> Downloading binary from "${binaryUrl}"`);
let binaryAsBuffer;
try {
const binaryResponse = await got(binaryUrl, createFetchOptions({ httpProxy, httpsProxy }));
binaryAsBuffer = binaryResponse.rawBody;
}
catch (error) {
throw new Error(`An error occured while trying to download the binary from "${binaryUrl}". Details: ${error.message}`);
}
return {
binaryAsBuffer,
binaryFileName,
};
};
const verifyBinaryChecksum = async (binaryAsBuffer, binaryFileName, { downloadUrl, extended, httpProxy, httpsProxy, version, }) => {
const checksumFileNamePattern = hugoReleasesMeta.checksumFilePatternHistory
.reverse()
.reduce((fileNamePattern, fileNamePatternHistoryItem) => {
return semver.gte(semver.coerce(version), semver.coerce(fileNamePatternHistoryItem.version))
? fileNamePatternHistoryItem
: fileNamePattern;
}, null);
if (checksumFileNamePattern == null || checksumFileNamePattern.fileNamePattern === null) {
throw new Error(`A Hugo checksum file for version="${version}"${extended ? ' (extended)' : ''} is not known to "hugo-installer". If you think this is a bug, feel free to open a GitHub issue here: https://github.com/dominique-mueller/hugo-installer/issues/new.`);
}
const checksumFileName = checksumFileNamePattern.fileNamePattern
.replace('{{variant}}', extended && checksumFileNamePattern.useSpecificVariant ? 'hugo_extended' : 'hugo')
.replace('{{version}}', version);
const checksumUrl = new URL(`v${version}/${checksumFileName}`, downloadUrl).toString();
console.log(`> Downloading checksum from "${checksumUrl}"`);
let rawChecksums;
try {
const checksumResponse = await got(checksumUrl, createFetchOptions({ httpProxy, httpsProxy }));
rawChecksums = checksumResponse.body;
}
catch (error) {
throw new Error(`An error occured while trying to download the checksum. Details: ${error.message}`);
}
const rawChecksumLine = rawChecksums.split('\n').find((rawChecksumLine) => {
return rawChecksumLine.endsWith(binaryFileName);
}) || null;
if (rawChecksumLine === null) {
throw new Error(`An error occured while trying to find the checksum for version "${version}" the checksum.`);
}
const expectedChecksum = rawChecksumLine.split(' ')[0];
const actualChecksum = crypto.createHash('sha256').update(binaryAsBuffer).digest('hex');
console.log(`> Verifying binary checksum`);
if (actualChecksum !== expectedChecksum) {
throw new Error(`The binary file could not be verified by its checksum. Expected: "${expectedChecksum}". Actual: "${actualChecksum}"`);
}
};
const writeBinaryToDisk = async (binaryAsBuffer, { destination }) => {
console.log(`> Extracting binary to disk`);
const decompressedFiles = await decompress(binaryAsBuffer, destination);
await Promise.all(decompressedFiles.map((decompressedFile) => {
return fs.promises.chmod(path.join(destination, decompressedFile.path), 0o755);
}));
};
const verifyBinaryHealth = async ({ destination }) => {
console.log(`> Verifying binary health`);
return new Promise((resolve, reject) => {
let hugoVersionConsoleOutput = null;
const childProcess = spawn(path.join(destination, 'hugo'), ['version']);
childProcess.stdout.on('data', (data) => {
hugoVersionConsoleOutput = data.toString().replace(/\r?\n|\r/g, '');
});
childProcess.on('close', () => {
resolve(hugoVersionConsoleOutput);
});
childProcess.on('error', (error) => {
reject(`An error occured while verifiy the binary health. Details: ${error.message}`);
});
});
};
const generateAndWriteVersionToDisk = async ({ arch, destination, extended, os, version, }) => {
const versionFileContent = {
arch,
extended,
os,
version,
};
await fs.promises.writeFile(path.join(destination, 'version.json'), JSON.stringify(versionFileContent, null, ' '));
};
const checkForExistingBinary = async ({ arch, destination, extended, os, version, }) => {
console.log('> Checking for existing binary');
try {
const versionFileContentRaw = await fs.promises.readFile(path.join(destination, 'version.json'), { encoding: 'utf-8' });
const versionFileContent = JSON.parse(versionFileContentRaw);
const doesBinaryAlreadyExist = versionFileContent.arch === arch &&
versionFileContent.extended === extended &&
versionFileContent.os === os &&
versionFileContent.version === version;
if (doesBinaryAlreadyExist) {
console.log(' Binary already exists!');
}
return doesBinaryAlreadyExist;
}
catch (error) {
return false;
}
};
async function installHugo({ arch, downloadUrl, destination, extended, force, httpProxy, httpsProxy, os, skipChecksumCheck, skipHealthCheck, version, }) {
console.log('');
console.log('Hugo Installer');
console.log('');
const doesBinaryAlreadyExist = force
? false
: await checkForExistingBinary({
arch,
destination,
extended,
os,
version,
});
if (!doesBinaryAlreadyExist) {
await cleanupAndPrepareDestination({ destination });
const { binaryAsBuffer, binaryFileName } = await fetchBinary({ arch, downloadUrl, extended, httpProxy, httpsProxy, os, version });
if (!skipChecksumCheck) {
await verifyBinaryChecksum(binaryAsBuffer, binaryFileName, { downloadUrl, extended, httpProxy, httpsProxy, version });
}
await writeBinaryToDisk(binaryAsBuffer, { destination });
}
let versionOutput = null;
if (!skipHealthCheck) {
versionOutput = await verifyBinaryHealth({ destination });
}
await generateAndWriteVersionToDisk({
arch,
destination,
extended,
os,
version,
});
console.log('');
console.log(`Hugo is now available in "${destination}".`);
console.log('');
console.log(`- Version ${version}`);
console.log(`- Extended ${extended ? 'Yes' : 'No'}`);
console.log(`- OS ${os}`);
console.log(`- Architecture ${arch}`);
if (versionOutput) {
console.log('');
console.log(versionOutput);
}
console.log('');
}
export { installHugo };
//# sourceMappingURL=index.js.map