microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
9588b66e62774c169bd461ef2aac4bc9535875bb

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/appcenter/command/commandExecutor.ts

319lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as vscode from "vscode";
5import * as Q from "q";
6import * as qs from "qs";
7import * as os from "os";
8
9import { ILogger, LogLevel } from "../../log/LogHelper";
10import Auth from "../../appcenter/auth/auth";
11import { AppCenterLoginType, ACConstants, AppCenterOS, CurrentAppDeployment } from "../appCenterConstants";
12import { Profile, getUser } from "../../appcenter/auth/profile/profile";
13import { SettingsHelper } from "../../settingsHelper";
14import { AppCenterClient, models } from "../api/index";
15import { DefaultApp, ICodePushReleaseParams } from "./commandParams";
16import { AppCenterExtensionManager } from "../appCenterExtensionManager";
17import { ACStrings } from "../appCenterStrings";
18import CodePushReleaseReact from "../codepush/releaseReact";
19import { ACUtils } from "../appCenterUtils";
20import { updateContents, reactNative, fileUtils } from "codepush-node-sdk";
21import BundleConfig = reactNative.BundleConfig;
22import { getQPromisifiedClientResult } from "../api/createClient";
23
24interface IAppCenterAuth {
25 login(appcenterManager: AppCenterExtensionManager): Q.Promise<void>;
26 logout(appcenterManager: AppCenterExtensionManager): Q.Promise<void>;
27 whoAmI(profile: Profile): Q.Promise<void>;
28}
29
30interface IAppCenterApps {
31 getCurrentApp(): Q.Promise<void>;
32 setCurrentApp(client: AppCenterClient, appCenterManager: AppCenterExtensionManager): Q.Promise<void>;
33
34 setCurrentDeployment(appCenterManager: AppCenterExtensionManager): Q.Promise<void>;
35}
36
37interface IAppCenterCodePush {
38 releaseReact(client: AppCenterClient, appCenterManager: AppCenterExtensionManager): Q.Promise<void>;
39}
40
41export class AppCenterCommandExecutor implements IAppCenterAuth, IAppCenterCodePush, IAppCenterApps {
42 private logger: ILogger;
43
44 constructor(logger: ILogger) {
45 this.logger = logger;
46 }
47
48 public login(appCenterManager: AppCenterExtensionManager): Q.Promise<void> {
49 const appCenterLoginOptions: string[] = Object.keys(AppCenterLoginType).filter(k => typeof AppCenterLoginType[k as any] === "number");
50 vscode.window.showQuickPick(appCenterLoginOptions, { placeHolder: ACStrings.SelectLoginTypeMsg })
51 .then((loginType) => {
52 switch (loginType) {
53 case (AppCenterLoginType[AppCenterLoginType.Interactive]):
54 vscode.window.showInformationMessage(ACStrings.PleaseLoginViaBrowser, "OK")
55 .then((selection: string) => {
56 if (selection.toLowerCase() === "ok") {
57 const loginUrl = `${SettingsHelper.getAppCenterLoginEndpoint()}?${qs.stringify({ hostname: os.hostname()})}`;
58 ACUtils.OpenUrl(loginUrl);
59 return vscode.window.showInputBox({ prompt: ACStrings.PleaseProvideToken, ignoreFocusOut: true })
60 .then(token => {
61 this.loginWithToken(token, appCenterManager);
62 });
63 } else return Q.resolve(void 0);
64 });
65 break;
66 case (AppCenterLoginType[AppCenterLoginType.Token]):
67 vscode.window.showInputBox({ prompt: ACStrings.PleaseProvideToken , ignoreFocusOut: true})
68 .then(token => {
69 this.loginWithToken(token, appCenterManager);
70 });
71 break;
72 default:
73 throw new Error("Unsupported login parameter!");
74 }
75 });
76 return Q.resolve(void 0);
77 }
78
79 public logout(appCenterManager: AppCenterExtensionManager): Q.Promise<void> {
80 const logoutOption = "Logout";
81 const logoutChoices: string[] = [logoutOption];
82 vscode.window.showQuickPick(logoutChoices, { placeHolder: ACStrings.LogoutPrompt })
83 .then((logoutType) => {
84 switch (logoutType) {
85 case (logoutOption):
86 return Auth.doLogout().then(() => {
87 vscode.window.showInformationMessage(ACStrings.UserLoggedOutMsg);
88 appCenterManager.setupNotAuthenticatedStatusBar();
89 return Q.resolve(void 0);
90 }).catch(() => {
91 this.logger.log("An errro occured on logout", LogLevel.Error);
92 });
93 default:
94 return Q.resolve(void 0);
95 }
96 });
97 return Q.resolve(void 0);
98 }
99
100 public whoAmI(profile: Profile): Q.Promise<void> {
101 if (profile && profile.displayName) {
102 vscode.window.showInformationMessage(ACStrings.YouAreLoggedInMsg(profile.displayName));
103 } else {
104 vscode.window.showInformationMessage(ACStrings.UserIsNotLoggedInMsg);
105 }
106 return Q.resolve(void 0);
107 }
108
109 public setCurrentDeployment(appCenterManager: AppCenterExtensionManager): Q.Promise<void> {
110 this.restoreCurrentApp().then((currentApp: DefaultApp) => {
111 if (currentApp && currentApp.currentAppDeployment && currentApp.currentAppDeployment.codePushDeployments) {
112 const deploymentOptions: string[] = currentApp.currentAppDeployment.codePushDeployments.map((deployment) => {
113 return deployment.name;
114 });
115 vscode.window.showQuickPick(deploymentOptions, { placeHolder: ACStrings.SelectCurrentDeploymentMsg })
116 .then((deploymentName) => {
117 if (deploymentName) {
118 this.saveCurrentApp(currentApp.identifier, AppCenterOS[currentApp.os], {
119 currentDeploymentName: deploymentName,
120 codePushDeployments: currentApp.currentAppDeployment.codePushDeployments,
121 }).then((app: DefaultApp | null) => {
122 if (app) {
123 appCenterManager.setCurrentDeploymentStatusBar(deploymentName);
124 }
125 });
126 }
127 });
128 } else {
129 appCenterManager.setCurrentDeploymentStatusBar(null);
130 }
131 });
132 return Q.resolve(void 0);
133 }
134
135 public getCurrentApp(): Q.Promise<void> {
136 this.restoreCurrentApp().then((app: DefaultApp) => {
137 if (app) {
138 vscode.window.showInformationMessage(ACStrings.YourCurrentAppMsg(app.identifier));
139 } else {
140 vscode.window.showInformationMessage(ACStrings.NoCurrentAppSetMsg);
141 }
142 });
143 return Q.resolve(void 0);
144 }
145
146 public setCurrentApp(client: AppCenterClient, appCenterManager: AppCenterExtensionManager): Q.Promise<void> {
147 vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: "Get Apps"}, p => {
148 return new Promise((resolve, reject) => {
149 p.report({message: ACStrings.FetchAppsStatusBarMessage });
150 getQPromisifiedClientResult(client.account.apps.list()).then((apps: models.AppResponse[]) => {
151 const appsList: models.AppResponse[] = apps;
152 const reactNativeApps = appsList.filter(app => app.platform === ACConstants.AppCenterReactNativePlatformName);
153 resolve(reactNativeApps);
154 });
155 });
156 }).then((rnApps: models.AppResponse[]) => {
157 let options = rnApps.map(app => {
158 return {
159 label: `${app.name} (${app.os})`,
160 description: app.displayName,
161 target: app.name,
162 };
163 });
164 vscode.window.showQuickPick(options, { placeHolder: ACStrings.ProvideCurrentAppPromptMsg })
165 .then((selected: {label: string, description: string, target: string}) => {
166 if (selected) {
167 const selectedApps: models.AppResponse[] = rnApps.filter(app => app.name === selected.target);
168 if (selectedApps && selectedApps.length === 1) {
169 const selectedApp: models.AppResponse = selectedApps[0];
170 const selectedAppName: string = `${selectedApp.owner.name}/${selectedApp.name}`;
171 const OS: AppCenterOS = AppCenterOS[selectedApp.os];
172
173 vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: "Get Deployments"}, p => {
174 return new Promise((resolve, reject) => {
175 p.report({message: ACStrings.FetchDeploymentsStatusBarMessage });
176 getQPromisifiedClientResult(client.codepush.codePushDeployments.list(selectedApp.name, selectedApp.owner.name))
177 .then((deployments: models.Deployment[]) => {
178 resolve(deployments);
179 });
180 });
181 }).then((appDeployments: models.Deployment[]) => {
182 let currentDeployment: CurrentAppDeployment | null = null;
183 if (appDeployments.length > 0) {
184 currentDeployment = {
185 codePushDeployments: appDeployments,
186 currentDeploymentName: appDeployments[0].name, // Select 1st one by default
187 };
188 }
189 this.saveCurrentApp(selectedAppName, OS, currentDeployment).then((app: DefaultApp | null) => {
190 if (app) {
191 vscode.window.showInformationMessage(ACStrings.YourCurrentAppAndDeployemntMsg(selected.target
192 , app.currentAppDeployment.currentDeploymentName));
193 appCenterManager.setupAppCenterStatusBarsWithCurrentApp(app);
194 }
195 });
196 });
197 }
198 }
199 });
200 });
201 return Q.resolve(void 0);
202 }
203
204 public releaseReact(client: AppCenterClient, appCenterManager: AppCenterExtensionManager): Q.Promise<void> {
205 let codePushRelaseParams = <ICodePushReleaseParams>{};
206 const projectRootPath: string = appCenterManager.projectRootPath;
207 let updateContentsDirectory: string;
208 return Q.Promise<any>((resolve, reject) => {
209 vscode.window.withProgress({ location: vscode.ProgressLocation.Window, title: "Get Apps" }, p => {
210 return new Promise<DefaultApp>((appResolve, appReject) => {
211 p.report({ message: ACStrings.GettingAppInfoMessage });
212 this.restoreCurrentApp()
213 .then((currentApp: DefaultApp) => appResolve(<DefaultApp>currentApp))
214 .catch(err => appReject(err));
215 }).then((currentApp: DefaultApp): any => {
216 p.report({ message: ACStrings.DetectingAppVersionMessage });
217 if (!currentApp) {
218 vscode.window.showInformationMessage(ACStrings.NoCurrentAppSetMsg);
219 reject(new Error(`No current app has been specified.`));
220 }
221 if (!reactNative.isValidOS(currentApp.os)) {
222 reject(new Error(`OS must be "android", "ios", or "windows".`));
223 }
224 codePushRelaseParams.app = currentApp;
225 codePushRelaseParams.deploymentName = currentApp.currentAppDeployment.currentDeploymentName;
226 currentApp.os = currentApp.os.toLowerCase();
227 switch (currentApp.os) {
228 case "android": return reactNative.getAndroidAppVersion(projectRootPath);
229 case "ios": return reactNative.getiOSAppVersion(projectRootPath);
230 case "windows": return reactNative.getWindowsAppVersion(projectRootPath);
231 default: reject(new Error(`OS must be "android", "ios", or "windows".`));
232 }
233 }).then((appVersion: string) => {
234 p.report({ message: ACStrings.RunningReactNativeBundleCommandMessage });
235 codePushRelaseParams.appVersion = appVersion;
236 return reactNative.makeUpdateContents(<BundleConfig>{
237 os: codePushRelaseParams.app.os,
238 projectRootPath: projectRootPath,
239 });
240 }).then((pathToUpdateContents: string) => {
241 p.report({ message: ACStrings.ArchivingUpdateContentsMessage });
242 updateContentsDirectory = pathToUpdateContents;
243 return updateContents.zip(pathToUpdateContents, projectRootPath);
244 }).then((pathToZippedBundle: string) => {
245 p.report({ message: ACStrings.ReleasingUpdateContentsMessage });
246 codePushRelaseParams.updatedContentZipPath = pathToZippedBundle;
247 return new Promise<any>((publishResolve, publishReject) => {
248 CodePushReleaseReact.exec(client, codePushRelaseParams, this.logger)
249 .then((response: any) => publishResolve(response))
250 .catch((error: any) => publishReject(error));
251 });
252 }).then((response: any) => {
253 if (response.succeeded && response.result) {
254 vscode.window.showInformationMessage(`Successfully released an update containing the "${updateContentsDirectory}" ` +
255 `directory to the "${codePushRelaseParams.deploymentName}" deployment of the "${codePushRelaseParams.app.appName}" app`);
256 resolve(response.result);
257 } else {
258 vscode.window.showErrorMessage(response.errorMessage);
259 }
260 fileUtils.rmDir(codePushRelaseParams.updatedContentZipPath);
261 }).catch((error: Error) => {
262 vscode.window.showErrorMessage("An error occured on doing Code Push release");
263 fileUtils.rmDir(codePushRelaseParams.updatedContentZipPath);
264 });
265 });
266 });
267 }
268
269 private saveCurrentApp(currentAppName: string, appOS: AppCenterOS, currentAppDeployment: CurrentAppDeployment | null): Q.Promise<DefaultApp | null> {
270 const defaultApp = ACUtils.toDefaultApp(currentAppName, appOS, currentAppDeployment);
271 if (!defaultApp) {
272 vscode.window.showWarningMessage(ACStrings.InvalidCurrentAppNameMsg);
273 return Q.resolve(null);
274 }
275
276 let profile = getUser();
277 if (profile) {
278 profile.defaultApp = defaultApp;
279 profile.save();
280 return Q.resolve(defaultApp);
281 } else {
282 // No profile - not logged in?
283 vscode.window.showWarningMessage(ACStrings.UserIsNotLoggedInMsg);
284 return Q.resolve(null);
285 }
286 }
287
288 private restoreCurrentApp(): Q.Promise<DefaultApp | null> {
289 const user = getUser();
290 if (user) {
291 if (user.defaultApp) {
292 const currentApp = `${user.defaultApp.ownerName}/${user.defaultApp.appName}`;
293 const defaultApp: DefaultApp | null = ACUtils.toDefaultApp(currentApp, AppCenterOS[user.defaultApp.os], user.defaultApp.currentAppDeployment);
294 if (defaultApp) {
295 return Q.resolve(defaultApp);
296 }
297 }
298 }
299 return Q.resolve(null);
300 }
301
302 private loginWithToken(token: string | undefined, appCenterManager: AppCenterExtensionManager) {
303 if (!token) {
304 return;
305 }
306 return Auth.doTokenLogin(token).then((profile: Profile) => {
307 if (!profile) {
308 this.logger.log("Failed to fetch user info from server", LogLevel.Error);
309 vscode.window.showWarningMessage(ACStrings.FailedToExecuteLoginMsg);
310 return;
311 }
312 vscode.window.showInformationMessage(ACStrings.YouAreLoggedInMsg(profile.displayName));
313 appCenterManager.setupAuthenticatedStatusBar(profile.displayName);
314 this.restoreCurrentApp().then((currentApp: DefaultApp) => {
315 appCenterManager.setupAppCenterStatusBarsWithCurrentApp(currentApp);
316 });
317 });
318 }
319}