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 31, 2024
1 parent ebaa3d7 commit 9cab21a
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 228 deletions.
15 changes: 0 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,6 @@
"axios": "^1.7.4",
"camelcase": "^7.0.1",
"lodash-es": "^4.17.21",
"mkdirp": "^2.1.3",
"semver": "^7.5.2",
"vscode-languageclient": "8.0.2-next.5",
"which": "^3.0.0"
Expand Down
105 changes: 23 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 { downloadAndExtractArtifact, getHostZigName, getVersion, getZigPath, shouldCheckUpdate } from "./zigUtil";
import { installZLS } from "./zls";

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

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

export async function installZig(context: vscode.ExtensionContext, version: ZigVersion) {
const zigPath = await downloadAndExtractArtifact(
"Zig",
"zig",
vscode.Uri.joinPath(context.globalStorageUri, "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 +70,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 +88,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 +115,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
94 changes: 93 additions & 1 deletion src/zigUtil.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
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 { promisify } from "util";

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

export const isWindows = process.platform === "win32";
const execFile = promisify(childProcess.execFile);
const chmod = promisify(fs.chmod);

// 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 +130,90 @@ export function getVersion(filePath: string, arg: string): semver.SemVer | null
return null;
}
}

export async function downloadAndExtractArtifact(
/** e.g. `Zig` or `ZLS` */
title: string,
/** e.g. `zig` or `zls` */
executableName: string,
/** e.g. inside `context.globalStorageUri` */
installDir: vscode.Uri,
artifactUrl: string,
/** The expected sha256 hash (in hex) of the artifact/tarball. */
sha256: string,
/** Extract arguments that should be passed to `tar`. 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>(artifactUrl, {
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 tarPath = await which("tar", { nothrow: true });
if (!tarPath) {
void vscode.window.showErrorMessage(
`Downloaded ${title} tarball can't be extracted because 'tar' could not be found`,
);
return null;
}

const tarballUri = vscode.Uri.joinPath(installDir, path.basename(artifactUrl));

try {
await vscode.workspace.fs.delete(installDir, { recursive: true, useTrash: false });
} catch {}
await vscode.workspace.fs.createDirectory(installDir);
await vscode.workspace.fs.writeFile(tarballUri, response.data);

progress.report({ message: "Extracting..." });
try {
await execFile(tarPath, ["-xf", tarballUri.fsPath, "-C", installDir.fsPath].concat(extraTarArgs), {
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;
} finally {
try {
await vscode.workspace.fs.delete(tarballUri, { useTrash: false });
} catch {}
}

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

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

return exePath;
},
);
}
Loading

0 comments on commit 9cab21a

Please sign in to comment.