Skip to content

Commit

Permalink
Overhaul extension bundling (#844)
Browse files Browse the repository at this point in the history
* Overhaul extension bundling

* Check version matches requested + stylua update

* Prompt do not show again on updates

* Add logging + check if binary found on PATH is executable

* Update changelog
  • Loading branch information
JohnnyMorganz authored Dec 30, 2023
1 parent 2cf5e80 commit 863c0cc
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 69 deletions.
6 changes: 6 additions & 0 deletions stylua-vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ To view the changelog of the StyLua binary, see [here](https://github.com/Johnny

### Added

- The extension now supports using a StyLua binary found on the PATH
- This can be configured via setting `stylua.searchBinaryOnPATH`
- If the binary fails to execute, we fall back to the bundled version
- Added configuration option `stylua.configPath` to provide a direct path to a `stylua.toml` file. Note: this will override any workspace config lookup
- Added configuration option `stylua.verify` to pass `--verify` to StyLua CLI when formatting a file. This enforces output verification
- Added command `StyLua: Select Version` to customize which version of StyLua to install. This command updates the `stylua.targetReleaseVersion` setting
Expand All @@ -27,6 +30,9 @@ To view the changelog of the StyLua binary, see [here](https://github.com/Johnny

- Removed excessive error notifications on formatting failure and replaced with VSCode language status bar item
- `.styluaignore` is now registered as an ignore file with an appropriate file icon
- StyLua version updates will now be shown on the status bar. To disable these notifications, configure `stylua.disableVersionCheck`
- If `stylua.targetReleaseVersion` is set, we will now still notify about the latest release version
- If `stylua.targetReleaseVersion` is set, but the installed version does not match, prompt to install desired version

## [1.5.0] - 2023-03-11

Expand Down
25 changes: 25 additions & 0 deletions stylua-vscode/package-lock.json

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

7 changes: 7 additions & 0 deletions stylua-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@
"default": null,
"description": "Specifies the path of StyLua. If not specified, will automatically download one from the GitHub releases."
},
"stylua.searchBinaryInPATH": {
"type": "boolean",
"default": true,
"markdownDescription": "Search for the StyLua binary in the `PATH` environment variable, and use this if available. If disabled, falls back to a bundled binary"
},
"stylua.disableVersionCheck": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -143,6 +148,7 @@
"@types/node-fetch": "^2.5.8",
"@types/unzipper": "^0.10.8",
"@types/vscode": "^1.82.0",
"@types/which": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@vscode/test-cli": "^0.0.4",
Expand All @@ -156,6 +162,7 @@
"dependencies": {
"ignore": "^5.1.8",
"node-fetch": "^2.6.1",
"node-which": "^1.0.0",
"semver": "^7.5.4",
"unzipper": "^0.10.14"
}
Expand Down
186 changes: 127 additions & 59 deletions stylua-vscode/src/download.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import * as vscode from "vscode";
import * as unzip from "unzipper";
import * as util from "./util";
import * as semver from "semver";
import fetch from "node-fetch";
import { createWriteStream } from "fs";
import { executeStylua } from "./stylua";
import { GitHub, GitHubRelease } from "./github";
import which = require("which");

interface StyluaInfo {
export enum ResolveMode {
configuration = "configuration",
path = "PATH",
bundled = "bundled",
}

export interface StyluaInfo {
path: string;
version: string | undefined;
resolveMode: ResolveMode;
version?: string | undefined;
}

const getStyluaVersion = async (path: string, cwd?: string) => {
Expand All @@ -22,52 +31,100 @@ const getStyluaVersion = async (path: string, cwd?: string) => {
}
};

export class StyluaDownloader {
export class StyluaDownloader implements vscode.Disposable {
statusBarUpdateItem = vscode.window.createStatusBarItem(
"stylua.installUpdate",
vscode.StatusBarAlignment.Right
);

constructor(
private readonly storageDirectory: vscode.Uri,
private readonly github: GitHub
private readonly github: GitHub,
private readonly outputChannel: vscode.LogOutputChannel
) {}

public async findStylua(cwd?: string): Promise<StyluaInfo> {
// 1) If `stylua.styluaPath` has been specified, use that directly
const settingPath = vscode.workspace
.getConfiguration("stylua")
.get<string | null>("styluaPath");
if (settingPath) {
this.outputChannel.info(
`Stylua path explicitly configured: ${settingPath}`
);
return { path: settingPath, resolveMode: ResolveMode.configuration };
}

// 2) Find a `stylua` binary available on PATH
if (
vscode.workspace
.getConfiguration("stylua")
.get<boolean>("searchBinaryInPATH")
) {
this.outputChannel.info("Searching for stylua on PATH");
const resolvedPath = await which("stylua", { nothrow: true });
if (resolvedPath) {
this.outputChannel.info(`Stylua found on PATH: ${resolvedPath}`);
if (await getStyluaVersion(resolvedPath, cwd)) {
return { path: resolvedPath, resolveMode: ResolveMode.path };
} else {
this.outputChannel.error(
"Stylua binary found on PATH failed to execute"
);
}
}
}

// 3) Fallback to bundled stylua version
this.outputChannel.info("Falling back to bundled StyLua version");
const downloadPath = vscode.Uri.joinPath(
this.storageDirectory,
util.getDownloadOutputFilename()
);
return { path: downloadPath.fsPath, resolveMode: ResolveMode.bundled };
}

public async ensureStyluaExists(
cwd?: string
): Promise<StyluaInfo | undefined> {
const path = await this.getStyluaPath();

if (path === undefined) {
await vscode.workspace.fs.createDirectory(this.storageDirectory);
await this.downloadStyLuaVisual(util.getDesiredVersion());
const path = await this.getStyluaPath();
if (path) {
return {
path,
version: await getStyluaVersion(path),
};
} else {
return;
const stylua = await this.findStylua(cwd);

if (stylua.resolveMode === ResolveMode.bundled) {
if (!(await util.fileExists(stylua.path))) {
await vscode.workspace.fs.createDirectory(this.storageDirectory);
await this.downloadStyLuaVisual(util.getDesiredVersion());
}
} else {
if (!(await util.fileExists(path))) {
vscode.window.showErrorMessage(
`The path given for StyLua (${path}) does not exist`
);
return;
stylua.version = await getStyluaVersion(stylua.path, cwd);

// Check bundled version matches requested version
const desiredVersion = util.getDesiredVersion();
if (stylua.version && desiredVersion !== "latest") {
const desiredVersionSemver = semver.coerce(desiredVersion);
const styluaVersionSemver = semver.parse(stylua.version);
if (
desiredVersionSemver &&
styluaVersionSemver &&
semver.neq(desiredVersionSemver, styluaVersionSemver)
) {
this.openIncorrectVersionPrompt(stylua.version, desiredVersion);
}
}

const currentVersion = await getStyluaVersion(path);

// Check for latest version
if (
!vscode.workspace.getConfiguration("stylua").get("disableVersionCheck")
) {
try {
const desiredVersion = util.getDesiredVersion();
const release = await this.github.getRelease(desiredVersion);
const latestRelease = await this.github.getRelease("latest");
if (
currentVersion !==
(release.tagName.startsWith("v")
? release.tagName.substr(1)
: release.tagName)
stylua.version !==
(latestRelease.tagName.startsWith("v")
? latestRelease.tagName.substr(1)
: latestRelease.tagName)
) {
this.openUpdatePrompt(release);
this.showUpdateAvailable(latestRelease);
} else {
this.statusBarUpdateItem.hide();
}
} catch (err) {
vscode.window.showWarningMessage(
Expand All @@ -86,10 +143,22 @@ export class StyluaDownloader {
}
}
}
} else {
this.statusBarUpdateItem.hide();
}

return { path, version: currentVersion };
} else if (stylua.resolveMode === ResolveMode.configuration) {
if (!(await util.fileExists(stylua.path))) {
vscode.window.showErrorMessage(
`The path given for StyLua (${stylua.path}) does not exist`
);
return;
}
stylua.version = await getStyluaVersion(stylua.path, cwd);
} else if (stylua.resolveMode === ResolveMode.path) {
stylua.version = await getStyluaVersion(stylua.path, cwd);
}

return stylua;
}

public downloadStyLuaVisual(version: string): Thenable<void> {
Expand Down Expand Up @@ -140,41 +209,40 @@ export class StyluaDownloader {
}
}

private openUpdatePrompt(release: GitHubRelease) {
private showUpdateAvailable(release: GitHubRelease) {
this.statusBarUpdateItem.name = "StyLua Update";
this.statusBarUpdateItem.text = `StyLua update available (${release.tagName}) $(cloud-download)`;
this.statusBarUpdateItem.tooltip = "Click to update StyLua";
this.statusBarUpdateItem.command = {
title: "Update StyLua",
command: "stylua.installUpdate",
arguments: [release],
};
this.statusBarUpdateItem.backgroundColor = new vscode.ThemeColor(
"statusBarItem.warningBackground"
);
this.statusBarUpdateItem.show();
}

private openIncorrectVersionPrompt(
currentVersion: string,
requestedVersion: string
) {
vscode.window
.showInformationMessage(
`StyLua ${release.tagName} is available to install.`,
"Install",
"Later",
"Release Notes"
`The currently installed version of StyLua (${currentVersion}) does not match the requested version (${requestedVersion})`,
"Install"
)
.then((option) => {
switch (option) {
case "Install":
this.downloadStyLuaVisual(release.tagName);
break;
case "Release Notes":
vscode.env.openExternal(vscode.Uri.parse(release.htmlUrl));
this.openUpdatePrompt(release);
vscode.commands.executeCommand("stylua.reinstall");
break;
}
});
}

public async getStyluaPath(): Promise<string | undefined> {
const settingPath = vscode.workspace
.getConfiguration("stylua")
.get<string | null>("styluaPath");
if (settingPath) {
return settingPath;
}

const downloadPath = vscode.Uri.joinPath(
this.storageDirectory,
util.getDownloadOutputFilename()
);
if (await util.fileExists(downloadPath)) {
return downloadPath.fsPath;
}
dispose() {
this.statusBarUpdateItem.dispose();
}
}
Loading

0 comments on commit 863c0cc

Please sign in to comment.