Skip to content

Commit

Permalink
update ZLS install tool
Browse files Browse the repository at this point in the history
ZLS builds are now offered by https://github.com/zigtools/release-worker
  • Loading branch information
Techatrix committed Aug 21, 2024
1 parent ebaa3d7 commit 4551079
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 213 deletions.
100 changes: 18 additions & 82 deletions src/zigSetup.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import childProcess from "child_process";
import crypto from "crypto";
import fs from "fs";
import path from "path";

import axios from "axios";
import mkdirp from "mkdirp";
import semver from "semver";
import vscode from "vscode";
import which from "which";

import { getHostZigName, getVersion, getZigPath, isWindows, shouldCheckUpdate } from "./zigUtil";
import { install as installZLS } from "./zls";
import { downloadAndExtractTarball, getHostZigName, getVersion, getZigPath, shouldCheckUpdate } from "./zigUtil";
import { installZLS } from "./zls";

const DOWNLOAD_INDEX = "https://ziglang.org/download/index.json";

Expand All @@ -29,6 +24,20 @@ interface ZigVersion {
notes?: string;
}

export async function installZig(context: vscode.ExtensionContext, version: ZigVersion) {
const zigPath = await downloadAndExtractTarball(context, "Zig", "zig", "zig_install", version.url, version.sha, [
"--strip-components=1",
]);
if (zigPath !== null) {
const configuration = vscode.workspace.getConfiguration("zig");
await configuration.update("path", zigPath, true);

void vscode.window.showInformationMessage(
`Zig has been installed successfully. Relaunch your integrated terminal to make it available.`,
);
}
}

async function getVersions(): Promise<ZigVersion[]> {
const hostName = getHostZigName();
const indexJson = (await axios.get<VersionIndex>(DOWNLOAD_INDEX, {})).data;
Expand Down Expand Up @@ -56,79 +65,6 @@ async function getVersions(): Promise<ZigVersion[]> {
return result;
}

async function install(context: vscode.ExtensionContext, version: ZigVersion) {
await vscode.window.withProgress(
{
title: "Installing Zig",
location: vscode.ProgressLocation.Notification,
},
async (progress) => {
progress.report({ message: "downloading Zig tarball..." });
const response = await axios.get<Buffer>(version.url, {
responseType: "arraybuffer",
onDownloadProgress: (progressEvent) => {
if (progressEvent.total) {
const increment = (progressEvent.bytes / progressEvent.total) * 100;
progress.report({
message: progressEvent.progress
? `downloading tarball ${(progressEvent.progress * 100).toFixed()}%`
: "downloading tarball...",
increment: increment,
});
}
},
});
const tarHash = crypto.createHash("sha256").update(response.data).digest("hex");
if (tarHash !== version.sha) {
throw Error(`hash of downloaded tarball ${tarHash} does not match expected hash ${version.sha}`);
}

const installDir = vscode.Uri.joinPath(context.globalStorageUri, "zig_install");
if (fs.existsSync(installDir.fsPath)) {
fs.rmSync(installDir.fsPath, { recursive: true, force: true });
}
mkdirp.sync(installDir.fsPath);

const tarPath = which.sync("tar", { nothrow: true });
if (!tarPath) {
void vscode.window.showErrorMessage(
"Downloaded Zig tarball can't be extracted because 'tar' could not be found",
);
return;
}

progress.report({ message: "Extracting..." });
try {
childProcess.execFileSync(tarPath, ["-xJf", "-", "-C", installDir.fsPath, "--strip-components=1"], {
encoding: "buffer",
input: response.data,
maxBuffer: 100 * 1024 * 1024, // 100MB
timeout: 60000, // 60 seconds
});
} catch (err) {
if (err instanceof Error) {
void vscode.window.showErrorMessage(`Failed to extract Zig tarball: ${err.message}`);
} else {
throw err;
}
return;
}

progress.report({ message: "Installing..." });
const exeName = `zig${isWindows ? ".exe" : ""}`;
const zigPath = vscode.Uri.joinPath(installDir, exeName).fsPath;
fs.chmodSync(zigPath, 0o755);

const configuration = vscode.workspace.getConfiguration("zig");
await configuration.update("path", zigPath, true);

void vscode.window.showInformationMessage(
`Zig has been installed successfully. Relaunch your integrated terminal to make it available.`,
);
},
);
}

async function selectVersionAndInstall(context: vscode.ExtensionContext) {
try {
const available = await getVersions();
Expand All @@ -147,7 +83,7 @@ async function selectVersionAndInstall(context: vscode.ExtensionContext) {
if (selection === undefined) return;
for (const option of available) {
if (option.name === selection.label) {
await install(context, option);
await installZig(context, option);
return;
}
}
Expand All @@ -174,7 +110,7 @@ async function checkUpdate(context: vscode.ExtensionContext) {
);
switch (response) {
case "Install":
await install(context, update);
await installZig(context, update);
break;
case "Ignore":
case undefined:
Expand Down
91 changes: 89 additions & 2 deletions src/zigUtil.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import vscode from "vscode";

import childProcess from "child_process";
import crypto from "crypto";
import fs from "fs";
import os from "os";
import path from "path";

import semver from "semver";
import assert from "assert";
import axios from "axios";
import mkdirp from "mkdirp";
import which from "which";

export const isWindows = process.platform === "win32";
import semver, { SemVer } from "semver";

// Replace any references to predefined variables in config string.
// https://code.visualstudio.com/docs/editor/variables-reference#_predefined-variables
Expand Down Expand Up @@ -125,3 +128,87 @@ export function getVersion(filePath: string, arg: string): semver.SemVer | null
return null;
}
}

export async function downloadAndExtractTarball(
context: vscode.ExtensionContext,
/** e.g. `Zig` or `ZLS` */
title: string,
/** e.g. `zig` or `zls` */
executableName: string,
/** e.g. `zig_install` or `zls_install` */
subPath: string,
tarballURL: string,
sha256: string,
/** e.g. `--strip-components=1` */
extraTarArgs: string[],
): Promise<string | null> {
assert.strictEqual(sha256.length, 64);

return await vscode.window.withProgress<string | null>(
{
title: `Installing ${title}`,
location: vscode.ProgressLocation.Notification,
},
async (progress) => {
progress.report({ message: `downloading ${title} tarball...` });
const response = await axios.get<Buffer>(tarballURL, {
responseType: "arraybuffer",
onDownloadProgress: (progressEvent) => {
if (progressEvent.total) {
const increment = (progressEvent.bytes / progressEvent.total) * 100;
progress.report({
message: progressEvent.progress
? `downloading tarball ${(progressEvent.progress * 100).toFixed()}%`
: "downloading tarball...",
increment: increment,
});
}
},
});
const tarHash = crypto.createHash("sha256").update(response.data).digest("hex");
if (tarHash !== sha256) {
throw Error(`hash of downloaded tarball ${tarHash} does not match expected hash ${sha256}`);
}

const installDir = vscode.Uri.joinPath(context.globalStorageUri, subPath);
if (fs.existsSync(installDir.fsPath)) {
fs.rmSync(installDir.fsPath, { recursive: true, force: true });
}
mkdirp.sync(installDir.fsPath);

const tarPath = which.sync("tar", { nothrow: true });
if (!tarPath) {
void vscode.window.showErrorMessage(
`Downloaded ${title} tarball can't be extracted because 'tar' could not be found`,
);
return null;
}

progress.report({ message: "Extracting..." });
try {
childProcess.execFileSync(tarPath, ["-xJf", "-", "-C", installDir.fsPath].concat(extraTarArgs), {
encoding: "buffer",
input: response.data,
maxBuffer: 100 * 1024 * 1024, // 100MB
timeout: 60000, // 60 seconds
});
} catch (err) {
if (err instanceof Error) {
void vscode.window.showErrorMessage(`Failed to extract ${title} tarball: ${err.message}`);
} else {
throw err;
}
return null;
}

progress.report({ message: "Installing..." });

const isWindows = process.platform === "win32";
const exeName = `${executableName}${isWindows ? ".exe" : ""}`;
const exePath = vscode.Uri.joinPath(installDir, exeName).fsPath;
fs.chmodSync(exePath, 0o755);

return exePath;
},
);
}
Loading

0 comments on commit 4551079

Please sign in to comment.