diff --git a/src/features/UpdatePowerShell.ts b/src/features/UpdatePowerShell.ts index 5252e11f65..ba33673063 100644 --- a/src/features/UpdatePowerShell.ts +++ b/src/features/UpdatePowerShell.ts @@ -2,13 +2,23 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ +import { spawn } from "child_process"; +import * as fs from "fs"; import fetch, { RequestInit } from "node-fetch"; +import * as os from "os"; +import * as path from "path"; import * as semver from "semver"; -import { MessageItem, window } from "vscode"; +import * as stream from "stream"; +import * as util from "util"; +import { MessageItem, ProgressLocation, window } from "vscode"; import { LanguageClient } from "vscode-languageclient"; +import { SessionManager } from "../session"; import * as Settings from "../settings"; +import { isMacOS, isWindows } from "../utils"; import { EvaluateRequestType } from "./Console"; +const streamPipeline = util.promisify(stream.pipeline); + const PowerShellGitHubReleasesUrl = "https://api.github.com/repos/PowerShell/PowerShell/releases/latest"; const PowerShellGitHubPrereleasesUrl = @@ -67,6 +77,7 @@ interface IUpdateMessageItem extends MessageItem { } export async function InvokePowerShellUpdateCheck( + sessionManager: SessionManager, languageServerClient: LanguageClient, localVersion: semver.SemVer, arch: string, @@ -103,7 +114,6 @@ export async function InvokePowerShellUpdateCheck( return; } - const isMacOS: boolean = process.platform === "darwin"; const result = await window.showInformationMessage( `${commonText} Would you like to update the version? ${ isMacOS ? "(Homebrew is required on macOS)" : "" @@ -116,39 +126,50 @@ export async function InvokePowerShellUpdateCheck( switch (result.id) { // Yes choice. case 0: - let script: string; - if (process.platform === "win32") { + if (isWindows) { const msiMatcher = arch === "x86" ? "win-x86.msi" : "win-x64.msi"; - const assetUrl = release.assets.filter((asset: any) => - asset.name.indexOf(msiMatcher) >= 0)[0].browser_download_url; - - // Grab MSI and run it. - // tslint:disable-next-line: max-line-length - script = ` -$randomFileName = [System.IO.Path]::GetRandomFileName() -$tmpMsiPath = Microsoft.PowerShell.Management\\Join-Path ([System.IO.Path]::GetTempPath()) "$randomFileName.msi" -Microsoft.PowerShell.Utility\\Invoke-RestMethod -Uri ${assetUrl} -OutFile $tmpMsiPath -try -{ - Microsoft.PowerShell.Management\\Start-Process -Wait -Path $tmpMsiPath -} -finally -{ - Microsoft.PowerShell.Management\\Remove-Item $tmpMsiPath -}`; + const asset = release.assets.filter((a: any) => a.name.indexOf(msiMatcher) >= 0)[0]; + const msiDownloadPath = path.join(os.tmpdir(), asset.name); - } else if (isMacOS) { - script = "brew cask upgrade powershell"; - if (release.isPreview) { - script = "brew cask upgrade powershell-preview"; + const res = await fetch(asset.browser_download_url); + if (!res.ok) { + throw new Error("unable to fetch MSI"); } + + await window.withProgress({ + title: "Downloading PowerShell Installer...", + location: ProgressLocation.Notification, + cancellable: false, + }, + async () => { + // Streams the body of the request to a file. + await streamPipeline(res.body, fs.createWriteStream(msiDownloadPath)); + }); + + // Stop the Integrated Console session because Windows likes to hold on to files. + sessionManager.stop(); + + // Invoke the MSI via cmd. + const msi = spawn("msiexec", ["/i", msiDownloadPath]); + + msi.on("close", (code) => { + // Now that the MSI is finished, start the Integrated Console session. + sessionManager.start(); + fs.unlinkSync(msiDownloadPath); + }); + + } else if (isMacOS) { + const script = release.isPreview + ? "brew cask upgrade powershell-preview" + : "brew cask upgrade powershell"; + + await languageServerClient.sendRequest(EvaluateRequestType, { + expression: script, + }); } - await languageServerClient.sendRequest(EvaluateRequestType, { - expression: script, - }); break; // Never choice. diff --git a/src/process.ts b/src/process.ts index 051da88488..d3d9a660ca 100644 --- a/src/process.ts +++ b/src/process.ts @@ -69,7 +69,7 @@ export class PowerShellProcess { ]; // Only add ExecutionPolicy param on Windows - if (utils.isWindowsOS()) { + if (utils.isWindows) { powerShellArgs.push("-ExecutionPolicy", "Bypass"); } @@ -77,7 +77,7 @@ export class PowerShellProcess { PowerShellProcess.escapeSingleQuotes(startScriptPath) + "' " + this.startArgs; - if (utils.isWindowsOS()) { + if (utils.isWindows) { powerShellArgs.push( "-Command", startEditorServices); diff --git a/src/session.ts b/src/session.ts index 7b4e5cb6b8..5a2cd3a6ac 100644 --- a/src/session.ts +++ b/src/session.ts @@ -546,6 +546,7 @@ export class SessionManager implements Middleware { await GitHubReleaseInformation.FetchLatestRelease(isPreRelease); await InvokePowerShellUpdateCheck( + this, this.languageServerClient, localVersion, this.versionDetails.architecture, diff --git a/src/utils.ts b/src/utils.ts index 8604c26deb..57527cf529 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,7 +8,7 @@ import fs = require("fs"); import os = require("os"); import path = require("path"); -export let PowerShellLanguageId = "powershell"; +export const PowerShellLanguageId = "powershell"; export function ensurePathExists(targetPath: string) { // Ensure that the path exists @@ -116,6 +116,6 @@ export function getTimestampString() { return `[${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}]`; } -export function isWindowsOS(): boolean { - return os.platform() === "win32"; -} +export const isMacOS: boolean = process.platform === "darwin"; +export const isWindows: boolean = process.platform === "win32"; +export const isLinux: boolean = !isMacOS && !isWindows;