Skip to content

Commit b2a5fe4

Browse files
Prompt to update PowerShell version (#2105) (#2177)
* prompt to update PowerShell version * address all feedback * say homebrew is used * Feedback Co-Authored-By: Christoph Bergmeister [MVP] <[email protected]> * Feedback Co-Authored-By: Christoph Bergmeister [MVP] <[email protected]> * PR feedback * use correct url from github api
1 parent a3b9caf commit b2a5fe4

File tree

6 files changed

+296
-6
lines changed

6 files changed

+296
-6
lines changed

package-lock.json

+89-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+9
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@
4040
"onView:PowerShellCommands"
4141
],
4242
"dependencies": {
43+
"node-fetch": "^2.6.0",
44+
"semver": "^6.3.0",
4345
"vscode-extension-telemetry": "~0.1.2",
4446
"vscode-languageclient": "~5.2.1"
4547
},
4648
"devDependencies": {
4749
"@types/mocha": "~5.2.7",
4850
"@types/node": "~10.11.0",
51+
"@types/node-fetch": "^2.5.0",
4952
"@types/rewire": "^2.5.28",
53+
"@types/semver": "^6.0.1",
5054
"mocha": "~5.2.0",
5155
"mocha-junit-reporter": "~1.23.1",
5256
"mocha-multi-reporters": "~1.1.7",
@@ -575,6 +579,11 @@
575579
"type": "string",
576580
"description": "Specifies the PowerShell version name, as displayed by the 'PowerShell: Show Session Menu' command, used when the extension loads e.g \"Windows PowerShell (x86)\" or \"PowerShell Core 6 (x64)\"."
577581
},
582+
"powershell.promptToUpdatePowerShell": {
583+
"type": "boolean",
584+
"description": "Specifies whether you should be prompted to update your version of PowerShell.",
585+
"default": true
586+
},
578587
"powershell.startAutomatically": {
579588
"type": "boolean",
580589
"default": true,

src/features/UpdatePowerShell.ts

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import fetch from "node-fetch";
6+
import * as semver from "semver";
7+
import { MessageItem, window } from "vscode";
8+
import { LanguageClient } from "vscode-languageclient";
9+
import * as Settings from "../settings";
10+
import { EvaluateRequestType } from "./Console";
11+
12+
const PowerShellGitHubReleasesUrl =
13+
"https://api.github.com/repos/PowerShell/PowerShell/releases/latest";
14+
const PowerShellGitHubPrereleasesUrl =
15+
"https://api.github.com/repos/PowerShell/PowerShell/releases";
16+
17+
export class GitHubReleaseInformation {
18+
public static async FetchLatestRelease(preview: boolean): Promise<GitHubReleaseInformation> {
19+
// Fetch the latest PowerShell releases from GitHub.
20+
let releaseJson: any;
21+
if (preview) {
22+
// This gets all releases and the first one is the latest prerelease if
23+
// there is a prerelease version.
24+
releaseJson = (await fetch(PowerShellGitHubPrereleasesUrl)
25+
.then((res) => res.json())).find((release: any) => release.prerelease);
26+
} else {
27+
releaseJson = await fetch(PowerShellGitHubReleasesUrl)
28+
.then((res) => res.json());
29+
}
30+
31+
return new GitHubReleaseInformation(
32+
releaseJson.tag_name, releaseJson.assets);
33+
}
34+
35+
public version: semver.SemVer;
36+
public isPreview: boolean = false;
37+
public assets: any[];
38+
39+
public constructor(version: string | semver.SemVer, assets: any[] = []) {
40+
this.version = semver.parse(version);
41+
42+
if (semver.prerelease(this.version)) {
43+
this.isPreview = true;
44+
}
45+
46+
this.assets = assets;
47+
}
48+
}
49+
50+
interface IUpdateMessageItem extends MessageItem {
51+
id: number;
52+
}
53+
54+
export async function InvokePowerShellUpdateCheck(
55+
languageServerClient: LanguageClient,
56+
localVersion: semver.SemVer,
57+
arch: string,
58+
release: GitHubReleaseInformation) {
59+
const options: IUpdateMessageItem[] = [
60+
{
61+
id: 0,
62+
title: "Yes",
63+
},
64+
{
65+
id: 1,
66+
title: "Not now",
67+
},
68+
{
69+
id: 2,
70+
title: "Do not show this notification again",
71+
},
72+
];
73+
74+
// If our local version is up-to-date, we can return early.
75+
if (semver.compare(localVersion, release.version) >= 0) {
76+
return;
77+
}
78+
79+
const commonText: string = `You have an old version of PowerShell (${
80+
localVersion.raw
81+
}). The current latest release is ${
82+
release.version.raw
83+
}.`;
84+
85+
if (process.platform === "linux") {
86+
await window.showInformationMessage(
87+
`${commonText} We recommend updating to the latest version.`);
88+
return;
89+
}
90+
91+
const isMacOS: boolean = process.platform === "darwin";
92+
const result = await window.showInformationMessage(
93+
`${commonText} Would you like to update the version? ${
94+
isMacOS ? "(Homebrew is required on macOS)" : ""
95+
}`, ...options);
96+
97+
// If the user cancels the notification.
98+
if (!result) { return; }
99+
100+
// Yes choice.
101+
switch (result.id) {
102+
// Yes choice.
103+
case 0:
104+
let script: string;
105+
if (process.platform === "win32") {
106+
const msiMatcher = arch === "x86" ?
107+
"win-x86.msi" : "win-x64.msi";
108+
109+
const assetUrl = release.assets.filter((asset: any) =>
110+
asset.name.indexOf(msiMatcher) >= 0)[0].browser_download_url;
111+
112+
// Grab MSI and run it.
113+
// tslint:disable-next-line: max-line-length
114+
script = `
115+
$randomFileName = [System.IO.Path]::GetRandomFileName()
116+
$tmpMsiPath = Microsoft.PowerShell.Management\\Join-Path ([System.IO.Path]::GetTempPath()) "$randomFileName.msi"
117+
Microsoft.PowerShell.Utility\\Invoke-RestMethod -Uri ${assetUrl} -OutFile $tmpMsiPath
118+
try
119+
{
120+
Microsoft.PowerShell.Management\\Start-Process -Wait -Path $tmpMsiPath
121+
}
122+
finally
123+
{
124+
Microsoft.PowerShell.Management\\Remove-Item $tmpMsiPath
125+
}`;
126+
127+
} else if (isMacOS) {
128+
script = "brew cask upgrade powershell";
129+
if (release.isPreview) {
130+
script = "brew cask upgrade powershell-preview";
131+
}
132+
}
133+
134+
await languageServerClient.sendRequest(EvaluateRequestType, {
135+
expression: script,
136+
});
137+
break;
138+
139+
// Never choice.
140+
case 2:
141+
await Settings.change("promptToUpdatePowerShell", false, true);
142+
break;
143+
default:
144+
break;
145+
}
146+
}

0 commit comments

Comments
 (0)