microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
indexed-sourcemap-null-section-issue

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/commands/installPods.ts

256lines · modeblame

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