diff --git a/.vscode/settings.json b/.vscode/settings.json index 357a88e425..5ba634561e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,13 +3,13 @@ "files.exclude": { "out": false }, - "files.associations": { - "**/.azure-pipelines/**/*.yml": "azure-pipelines" - }, "search.exclude": { "**/node_modules": true, "**/bower_components": true, "out": true }, - "typescript.tsdk": "./node_modules/typescript/lib" + "typescript.tsdk": "./node_modules/typescript/lib", + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + } } diff --git a/README.md b/README.md index b40084addc..9afd569115 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,10 @@ vscode-shellcheck (this "extension"), requires [shellcheck] (the awesome static analysis tool for shell scripts) to work. Since v0.10.0, precompiled [shellcheck] binaries are bundled for these platforms: -- Linux (x86_64) -- macOS (x86_64) -- Windows: precompiled 32bit binary will be used on both 32bit and 64bit Windows, please note that this requires you have [WoW64](https://en.wikipedia.org/wiki/WoW64) enabled, althouth it's not a problem for Desktop users. + +- Linux (x86_64) +- macOS (x86_64) +- Windows: precompiled 32bit binary will be used on both 32bit and 64bit Windows, please note that this requires you have [WoW64](https://en.wikipedia.org/wiki/WoW64) enabled, althouth it's not a problem for Desktop users. ## Requirements @@ -47,7 +48,6 @@ Default options are: "**/*.zsh-theme": true }, "shellcheck.ignoreFileSchemes": ["git", "gitfs"], - "shellcheck.useWSL": false } ``` @@ -118,13 +118,13 @@ This extension is based on [hoovercj's Haskell Linter](https://github.com/hoover ### Contributors -- [@felipecrs](https://github.com/felipecrs) -- [@sylveon](https://github.com/sylveon) -- [@ralish](https://github.com/ralish) +- [@felipecrs](https://github.com/felipecrs) +- [@sylveon](https://github.com/sylveon) +- [@ralish](https://github.com/ralish) ## LICENSE -This extension is licensed under the [MIT LICENSE](https://github.com/timonwong/vscode-shellcheck/blob/master/LICENSE). +This extension is licensed under the [MIT license](./LICENSE). Bundled [shellcheck] binaries are licensed under [GPLv3](https://github.com/koalaman/shellcheck/blob/master/LICENSE). diff --git a/package.json b/package.json index ac86289a2a..96e5adfa6d 100644 --- a/package.json +++ b/package.json @@ -143,12 +143,6 @@ "scope": "resource", "default": false }, - "shellcheck.useWSL": { - "description": "(Windows Only) Whether to use a shellcheck installation in the Windows Subsystem for Linux.", - "type": "boolean", - "scope": "resource", - "default": false - }, "shellcheck.disableVersionCheck": { "description": "Whether to disable shellcheck binary version check, which prompt for updating when outdated version found.", "type": "boolean", @@ -179,6 +173,7 @@ }, "dependencies": { "bl": "^4.0.3", + "execa": "^5.0.0", "lodash": "^4.17.19", "minimatch": "^3.0.4", "semver": "^7.3.4" diff --git a/src/linter.ts b/src/linter.ts index 867e79c509..32d698b03a 100644 --- a/src/linter.ts +++ b/src/linter.ts @@ -2,14 +2,13 @@ import * as fs from 'fs'; import * as path from 'path'; import * as semver from 'semver'; import * as vscode from 'vscode'; +import * as execa from 'execa'; import { createParser, ParseResult } from './parser'; import { ThrottledDelayer } from './utils/async'; import { FileMatcher, FileSettings } from './utils/filematcher'; import { getToolVersion, tryPromptForUpdatingTool } from './utils/tool-check'; -import * as wsl from './utils/wslSupport'; import { getWorkspaceFolderPath } from './utils/path'; - interface Executable { path: string; bundled: boolean; @@ -25,7 +24,6 @@ interface ShellCheckSettings { ignorePatterns: FileSettings; ignoreFileSchemes: Set; useWorkspaceRootAsCwd: boolean; - useWSL: boolean; } enum RunTrigger { @@ -177,7 +175,6 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { ignorePatterns: section.get('ignorePatterns', {}), ignoreFileSchemes: new Set(section.get('ignoreFileSchemes', ['git', 'gitfs'])), useWorkspaceRootAsCwd: section.get('useWorkspaceRootAsCwd', false), - useWSL: section.get('useWSL', false), enableQuickFix: section.get('enableQuickFix', false), }; this.settings = settings; @@ -199,7 +196,7 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { // Prompt user to update shellcheck binary when necessary try { - this.toolVersion = await getToolVersion(settings.useWSL, settings.executable.path); + this.toolVersion = await getToolVersion(settings.executable.path); this.executableNotFound = false; } catch (error) { this.showShellCheckError(error); @@ -293,14 +290,6 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { private runLint(textDocument: vscode.TextDocument): Promise { return new Promise((resolve, reject) => { const settings = this.settings; - if (settings.useWSL && !wsl.subsystemForLinuxPresent()) { - if (!this.executableNotFound) { - vscode.window.showErrorMessage('Got told to use WSL, but cannot find installation. Bailing out.'); - } - this.executableNotFound = true; - resolve(); - return; - } const executable = settings.executable || 'shellcheck'; const parser = createParser(textDocument, { @@ -335,7 +324,7 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { const options = cwd ? { cwd: cwd } : undefined; // this.channel.appendLine(`[DEBUG] Spawn: ${executable} ${args.join(' ')}`); - const childProcess = wsl.spawn(settings.useWSL, executable.path, args, options); + const childProcess = execa(executable.path, args, options); childProcess.on('error', (error: NodeJS.ErrnoException) => { if (!this.executableNotFound) { this.showShellCheckError(error); @@ -350,9 +339,6 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { childProcess.stdout.setEncoding('utf-8'); let script = textDocument.getText(); - if (settings.useWSL) { - script = script.replace(/\r\n/g, '\n'); // shellcheck doesn't likes CRLF, although this is caused by a git checkout on Windows. - } childProcess.stdin.write(script); childProcess.stdin.end(); @@ -392,7 +378,7 @@ export default class ShellCheckProvider implements vscode.CodeActionProvider { let message: string; let items: string[] = []; if (error.code === 'ENOENT') { - message = `The shellcheck program was not found (not installed?). Use the 'shellcheck.executablePath' setting to configure the location of 'shellcheck' or enable WSL integration with 'shellcheck.useWSL'`; + message = `The shellcheck program was not found (not installed?). Use the 'shellcheck.executablePath' setting to configure the location of 'shellcheck'`; items = ['OK', 'Installation Guide']; } else { message = `Failed to run shellcheck: [${error.code}] ${error.message}`; diff --git a/src/utils/tool-check.ts b/src/utils/tool-check.ts index fef5d1614a..c7ac88fa02 100644 --- a/src/utils/tool-check.ts +++ b/src/utils/tool-check.ts @@ -1,13 +1,8 @@ -import * as child_process from 'child_process'; import * as semver from 'semver'; -import { promisify } from 'util'; import * as vscode from 'vscode'; -import * as wsl from './wslSupport'; +import * as execa from 'execa'; - -const execFile = promisify(child_process.execFile); - -export const BEST_TOOL_VERSION = '0.7.0'; +export const BEST_TOOL_VERSION = '0.7.1'; export function tryPromptForUpdatingTool(version: semver.SemVer | null) { if (!version) { @@ -22,10 +17,10 @@ export function tryPromptForUpdatingTool(version: semver.SemVer | null) { } } -export async function getToolVersion(useWSL: boolean, executable: string): Promise { - const launchArgs = wsl.createLaunchArg(useWSL, false, undefined, executable, ['-V']); +export async function getToolVersion(executable: string): Promise { + + const { stdout } = await execa(executable, ['-V'], { timeout: 2000 }); - const { stdout } = await execFile(launchArgs.executable, launchArgs.args, { timeout: 2000 }); const matches = /version: ((?:\d+)\.(?:\d+)(?:\.\d+)*)/.exec(stdout); if (matches && matches[1]) { return semver.parse(matches[1]); @@ -35,7 +30,7 @@ export async function getToolVersion(useWSL: boolean, executable: string): Promi } async function promptForUpdatingTool(currentVersion: string, disableVersionCheckUpdateSetting: DisableVersionCheckUpdateSetting) { - const selected = await vscode.window.showInformationMessage(`The vscode-shellcheck extension is better with newer version of "shellcheck" (You got v${currentVersion}, v${BEST_TOOL_VERSION} or better is recommended)`, 'Don\'t Show Again', 'Update'); + const selected = await vscode.window.showInformationMessage(`The vscode-shellcheck extension is better with a newer version of "shellcheck" (You got v${currentVersion}, v${BEST_TOOL_VERSION} or newer is recommended)`, 'Don\'t Show Again', 'Update'); switch (selected) { case 'Don\'t Show Again': disableVersionCheckUpdateSetting.persist(); diff --git a/src/utils/wslSupport.ts b/src/utils/wslSupport.ts deleted file mode 100644 index 724553bf38..0000000000 --- a/src/utils/wslSupport.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Shamelessly stolen from https://github.com/Microsoft/vscode-node-debug2/blob/master/src/wslSupport.ts -import * as child_process from 'child_process'; -import * as fs from 'fs'; -import * as path from 'path'; - - -const isWindows = process.platform === 'win32'; -const is64bit = process.arch === 'x64'; - -function envSafeGet(key: string): string { - const v = process.env[key]; - if (typeof (v) === 'undefined') { - return ''; - } - return v; -} - -export function subsystemForLinuxPresent(): boolean { - if (!isWindows) { - return false; - } - - const bashPath32bitApp = path.join(envSafeGet('SystemRoot'), 'Sysnative', 'bash.exe'); - const bashPath64bitApp = path.join(envSafeGet('SystemRoot'), 'System32', 'bash.exe'); - const bashPathHost = is64bit ? bashPath64bitApp : bashPath32bitApp; - return fs.existsSync(bashPathHost); -} - -function windowsPathToWSLPath(windowsPath: string | null | undefined): string | undefined { - if (!isWindows || !windowsPath) { - return undefined; - } else if (path.isAbsolute(windowsPath)) { - return `/mnt/${windowsPath.substr(0, 1).toLowerCase()}/${windowsPath.substr(3).replace(/\\/g, '/')}`; - } else { - return windowsPath.replace(/\\/g, '/'); - } -} - -interface ILaunchArgs { - cwd?: string; - executable: string; - args: string[]; - combined: string[]; - localRoot?: string; - remoteRoot?: string; -} - -export function createLaunchArg(useSubsytemLinux: boolean, useExternalConsole: boolean, cwd: string | undefined, executable: string, args?: string[], program?: string): ILaunchArgs { - if (useSubsytemLinux && subsystemForLinuxPresent()) { - const bashPath32bitApp = path.join(envSafeGet('SystemRoot'), 'Sysnative', 'bash.exe'); - const bashPath64bitApp = path.join(envSafeGet('SystemRoot'), 'System32', 'bash.exe'); - const bashPathHost = is64bit ? bashPath64bitApp : bashPath32bitApp; - const subsystemLinuxPath = useExternalConsole ? bashPath64bitApp : bashPathHost; - - const bashCommand = [executable].concat(args || []).map(element => { - if (element === program) { // workaround for issue #35249 - element = element.replace(/\\/g, '/'); - } - return element.indexOf(' ') > 0 ? `'${element}'` : element; - }).join(' '); - return { - cwd, - executable: subsystemLinuxPath, - args: ['-ic', bashCommand], - combined: [subsystemLinuxPath].concat(['-ic', bashCommand]), - localRoot: cwd, - remoteRoot: windowsPathToWSLPath(cwd) - }; - } else { - return { - cwd: cwd, - executable: executable, - args: args || [], - combined: [executable].concat(args || []) - }; - } -} - -export function spawn(useWSL: boolean, executable: string, args?: string[], options?: child_process.SpawnOptions): child_process.ChildProcess { - const launchArgs = createLaunchArg(useWSL, false, undefined, executable, args); - return child_process.spawn(launchArgs.executable, launchArgs.args, options); -} diff --git a/yarn.lock b/yarn.lock index 2dbd874975..a55df0e75f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1998,7 +1998,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2693,6 +2693,21 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -3220,6 +3235,11 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3652,6 +3672,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -5496,7 +5521,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -5734,7 +5759,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -6955,7 +6980,7 @@ shelljs@^0.8.4: interpret "^1.0.0" rechoir "^0.6.2" -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==