microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/v-zhenyuan/update-parameters

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commands/installPods.ts

256lines · 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 assert = require("assert");
5import * as fs from "fs";
6import * as os from "os";
7import * as path from "path";
8import * as url from "url";
9import * as vscode from "vscode";
10import { ErrorHelper } from "../../common/error/errorHelper";
11import { InternalErrorCode } from "../../common/error/internalErrorCode";
12import { ChildProcess } from "../../common/node/childProcess";
13import { OutputChannelLogger } from "../log/OutputChannelLogger";
14import { Command } from "./util/command";
15
16const childProcess = new ChildProcess();
17
18export class InstallPods extends Command {
19 codeName = "installPods";
20 label = "Install CocoaPods dependencies";
21 error = ErrorHelper.getInternalError(InternalErrorCode.FailedToInstallPods);
22 requiresTrust = true;
23
24 async baseFn(): Promise<void> {
25 assert(this.project);
26 const logger = OutputChannelLogger.getMainChannel();
27 if (os.platform() !== "darwin") {
28 void vscode.window.showWarningMessage("CocoaPods is only supported on macOS.");
29 return;
30 }
31 const projectPath = this.project.getPackager().getProjectPath();
32 const iosPath = path.join(projectPath, "ios");
33 if (!fs.existsSync(iosPath)) {
34 const errorMsg =
35 "iOS directory not found. Make sure this is a React Native project with iOS support.";
36 logger.error(errorMsg);
37 void vscode.window.showErrorMessage(errorMsg);
38 return;
39 }
40 const podfilePath = path.join(iosPath, "Podfile");
41 if (!fs.existsSync(podfilePath)) {
42 const errorMsg = "Podfile not found in the ios directory.";
43 logger.error(errorMsg);
44 void vscode.window.showErrorMessage(errorMsg);
45 return;
46 }
47 logger.info("Installing CocoaPods dependencies...");
48 logger.info(`Working directory: ${iosPath}`);
49 try {
50 const enhancedEnv = this.getEnhancedEnvironment();
51 const podCommand = this.findPodCommand();
52 logger.info(`Using pod command: ${podCommand}`);
53 try {
54 const versionResult = await childProcess.exec(`${podCommand} --version`, {
55 env: enhancedEnv,
56 timeout: 10000,
57 });
58 const versionOutput = await versionResult.outcome;
59 logger.info(`Pod version: ${versionOutput.trim()}`);
60 } catch (versionError) {
61 const errorMsg =
62 "Cannot execute pod command. Please ensure CocoaPods is properly installed.";
63 logger.error(errorMsg);
64 void vscode.window.showErrorMessage(errorMsg);
65 throw new Error(errorMsg);
66 }
67 logger.info(`Executing: ${podCommand} install`);
68 const installResult = await childProcess.exec(`${podCommand} install`, {
69 cwd: iosPath,
70 env: enhancedEnv,
71 maxBuffer: 1024 * 1024 * 10,
72 timeout: 300000,
73 });
74 const stdout = await installResult.outcome;
75 if (stdout) {
76 logger.info(stdout);
77 }
78 logger.info("CocoaPods installation completed successfully");
79 void vscode.window.showInformationMessage(
80 "CocoaPods dependencies installed successfully.",
81 );
82 } catch (error: any) {
83 let errorMessage = "Unknown error";
84 let stderr = "";
85 let stdout = "";
86 if (error instanceof Error) {
87 errorMessage = error.message || "Unknown error";
88 }
89 if (error && typeof error === "object") {
90 if ("stderr" in error && error.stderr) {
91 stderr = String(error.stderr);
92 }
93 if ("stdout" in error && error.stdout) {
94 stdout = String(error.stdout);
95 }
96 }
97 const suggestion = this.getSuggestionForError(`${errorMessage} ${stderr}`);
98 const baseMessage = "Failed to install CocoaPods dependencies.";
99 let fullErrorMessage = `${baseMessage}\n${errorMessage}`;
100 if (stderr) {
101 fullErrorMessage = `${fullErrorMessage}\n\nError details:\n${stderr}`;
102 }
103 if (suggestion) {
104 fullErrorMessage = `${fullErrorMessage}\n\n${suggestion}`;
105 }
106 logger.error(fullErrorMessage);
107 if (stdout) {
108 logger.info(`Command output: ${stdout}`);
109 }
110 if (error instanceof Error && error.stack) {
111 logger.error(`Stack trace: ${error.stack}`);
112 }
113 void vscode.window.showErrorMessage(fullErrorMessage);
114 const enhancedError = new Error(fullErrorMessage);
115 if (error instanceof Error && error.stack) {
116 enhancedError.stack = error.stack;
117 }
118 throw enhancedError;
119 }
120 }
121
122 private findPodCommand(): string {
123 const logger = OutputChannelLogger.getMainChannel();
124 const homeDir = os.homedir();
125 const possiblePodPaths = [
126 `${homeDir}/.rbenv/shims/pod`,
127 `${homeDir}/.rvm/bin/pod`,
128 "/opt/homebrew/bin/pod",
129 "/usr/local/bin/pod",
130 "/Library/Ruby/Gems/2.6.0/bin/pod",
131 "/Library/Ruby/Gems/3.0.0/bin/pod",
132 "/Library/Ruby/Gems/3.3.0/bin/pod",
133 ];
134 logger.info("Searching for pod command...");
135 for (const possiblePath of possiblePodPaths) {
136 if (fs.existsSync(possiblePath)) {
137 try {
138 fs.accessSync(possiblePath, fs.constants.X_OK);
139 logger.info(`Found executable pod at: ${possiblePath}`);
140 return possiblePath;
141 } catch (accessError) {
142 logger.warning(`Found pod at ${possiblePath} but it's not executable`);
143 }
144 }
145 }
146 logger.warning("Pod command not found in common locations, using 'pod' from PATH");
147 return "pod";
148 }
149
150 private getEnhancedEnvironment(): { [key: string]: string } {
151 const logger = OutputChannelLogger.getMainChannel();
152 const env = { ...process.env } as { [key: string]: string };
153 const homeDir = os.homedir();
154 logger.info(`Using HOME directory: ${homeDir}`);
155 const rbenvRoot = process.env.RBENV_ROOT || `${homeDir}/.rbenv`;
156 env.RBENV_ROOT = rbenvRoot;
157 logger.info(`RBENV_ROOT: ${rbenvRoot}`);
158 const rbenvShims = `${rbenvRoot}/shims`;
159 const rbenvBin = `${rbenvRoot}/bin`;
160 const additionalPaths = [
161 rbenvShims,
162 rbenvBin,
163 "/usr/local/bin",
164 "/opt/homebrew/bin",
165 "/opt/homebrew/sbin",
166 `${homeDir}/.rvm/bin`,
167 `${homeDir}/.gem/ruby/2.6.0/bin`,
168 `${homeDir}/.gem/ruby/3.0.0/bin`,
169 `${homeDir}/.gem/ruby/3.3.0/bin`,
170 "/Library/Ruby/Gems/2.6.0/bin",
171 "/Library/Ruby/Gems/3.0.0/bin",
172 "/Library/Ruby/Gems/3.3.0/bin",
173 "/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin",
174 "/usr/bin",
175 "/bin",
176 "/usr/sbin",
177 "/sbin",
178 ];
179 const originalPath = env.PATH || "";
180 const originalPathArray = originalPath
181 .split(":")
182 .filter(p => !p.includes(".rbenv") && p.trim() !== "");
183 const allPaths = [...additionalPaths, ...originalPathArray];
184 const uniquePaths = Array.from(new Set(allPaths)).filter(Boolean);
185 env.PATH = uniquePaths.join(":");
186 logger.info(`Enhanced PATH: ${env.PATH}`);
187 if (!env.SHELL) {
188 env.SHELL = "/bin/zsh";
189 }
190 if (process.env.RBENV_VERSION) {
191 env.RBENV_VERSION = process.env.RBENV_VERSION;
192 }
193 if (process.env.GEM_HOME) {
194 env.GEM_HOME = process.env.GEM_HOME;
195 }
196 if (process.env.GEM_PATH) {
197 env.GEM_PATH = process.env.GEM_PATH;
198 }
199 env.LC_ALL = env.LC_ALL || "en_US.UTF-8";
200 env.LANG = env.LANG || "en_US.UTF-8";
201 return env;
202 }
203
204 private isCDNError(errorMessage: string): boolean {
205 const cdnDomains = ["trunk.cocoapods.org", "cdn.cocoapods.org"];
206 if (errorMessage.toLowerCase().includes("cdn")) {
207 return true;
208 }
209 const urlPattern = /https?:\/\/\S+/gi;
210 const urls = errorMessage.match(urlPattern);
211 if (!urls) {
212 return cdnDomains.some(domain => errorMessage.includes(domain));
213 }
214 for (const urlString of urls) {
215 try {
216 const parsedUrl = new url.URL(urlString);
217 const hostname = parsedUrl.hostname.toLowerCase();
218 for (const domain of cdnDomains) {
219 if (hostname === domain || hostname.endsWith(`.${domain}`)) {
220 return true;
221 }
222 }
223 } catch (e) {
224 continue;
225 }
226 }
227 return false;
228 }
229
230 private getSuggestionForError(errorMessage: string): string {
231 if (
232 errorMessage.includes("command not found") ||
233 errorMessage.includes("pod: not found") ||
234 errorMessage.includes("'pod' is not recognized") ||
235 errorMessage.includes("Cannot execute pod command")
236 ) {
237 return "CocoaPods may not be installed or not accessible. Install it via:\n • System Ruby: sudo gem install cocoapods\n • Homebrew: brew install cocoapods\n • rbenv: gem install cocoapods\nAfter installation, please restart VS Code to refresh the environment.";
238 }
239 if (this.isCDNError(errorMessage)) {
240 return "CDN error. Try running 'pod repo update' in the terminal.";
241 }
242 if (errorMessage.includes("Xcode") || errorMessage.includes("xcrun")) {
243 return "Xcode command line tools may be missing. Run 'xcode-select --install' in the terminal.";
244 }
245 if (errorMessage.includes("ruby") || errorMessage.includes("Ruby")) {
246 return "Ruby environment issue. Check your Ruby installation and version with 'ruby --version'.";
247 }
248 if (errorMessage.includes("permission") || errorMessage.includes("Permission")) {
249 return "Permission denied. Try checking directory permissions or running the command with appropriate privileges.";
250 }
251 if (errorMessage.includes("Gem::") || errorMessage.includes("gem")) {
252 return "Ruby gem issue. Try updating your gems with 'gem update --system' or 'sudo gem update --system'.";
253 }
254 return "";
255 }
256}
257