UNPKG

eas-cli

Version:
321 lines (320 loc) 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isGitCaseSensitiveAsync = void 0; const tslib_1 = require("tslib"); const PackageManagerUtils = tslib_1.__importStar(require("@expo/package-manager")); const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async")); const core_1 = require("@oclif/core"); const chalk_1 = tslib_1.__importDefault(require("chalk")); const path_1 = tslib_1.__importDefault(require("path")); const log_1 = tslib_1.__importStar(require("../../log")); const ora_1 = require("../../ora"); const prompts_1 = require("../../prompts"); const git_1 = require("../git"); const vcs_1 = require("../vcs"); class GitClient extends vcs_1.Client { maybeCwdOverride; constructor(maybeCwdOverride) { super(); this.maybeCwdOverride = maybeCwdOverride; } async ensureRepoExistsAsync() { try { if (!(await (0, git_1.isGitInstalledAsync)())) { log_1.default.error(`${chalk_1.default.bold('git')} command not found. Install it before proceeding or set ${chalk_1.default.bold('EAS_NO_VCS=1')} to use EAS CLI without Git (or any other version control system).`); log_1.default.error((0, log_1.learnMore)('https://expo.fyi/eas-vcs-workflow')); core_1.Errors.exit(1); } } catch (error) { log_1.default.error(`${chalk_1.default.bold('git')} found, but ${chalk_1.default.bold('git --help')} exited with status ${error?.status}${error?.stderr ? `:` : '.'}`); if (error?.stderr) { log_1.default.error(error?.stderr); } log_1.default.error(`Repair your Git installation, or set ${chalk_1.default.bold('EAS_NO_VCS=1')} to use EAS CLI without Git (or any other version control system).`); log_1.default.error((0, log_1.learnMore)('https://expo.fyi/eas-vcs-workflow')); core_1.Errors.exit(1); } if (await (0, git_1.doesGitRepoExistAsync)(this.maybeCwdOverride)) { return; } log_1.default.warn("It looks like you haven't initialized the git repository yet."); log_1.default.warn('EAS requires you to use a git repository for your project.'); const cwd = process.cwd(); const repoRoot = PackageManagerUtils.resolveWorkspaceRoot(cwd) ?? cwd; const confirmInit = await (0, prompts_1.confirmAsync)({ message: `Would you like us to run 'git init' in ${this.maybeCwdOverride ?? repoRoot} for you?`, }); if (!confirmInit) { throw new Error('A git repository is required for building your project. Initialize it and run this command again.'); } await (0, spawn_async_1.default)('git', ['init'], { cwd: this.maybeCwdOverride ?? repoRoot }); log_1.default.log("We're going to make an initial commit for your repository."); const { message } = await (0, prompts_1.promptAsync)({ type: 'text', name: 'message', message: 'Commit message:', initial: 'Initial commit', validate: (input) => input !== '', }); await this.commitAsync({ commitAllFiles: true, commitMessage: message, nonInteractive: false }); } async commitAsync({ commitMessage, commitAllFiles, nonInteractive = false, }) { await ensureGitConfiguredAsync({ nonInteractive }); try { if (commitAllFiles) { await (0, spawn_async_1.default)('git', ['add', '-A'], { cwd: this.maybeCwdOverride, }); } await (0, spawn_async_1.default)('git', ['add', '-u'], { cwd: this.maybeCwdOverride, }); await (0, spawn_async_1.default)('git', ['commit', '-m', commitMessage], { cwd: this.maybeCwdOverride, }); } catch (err) { if (err?.stdout) { log_1.default.error(err.stdout); } if (err?.stderr) { log_1.default.error(err.stderr); } throw err; } } async isCommitRequiredAsync() { return await this.hasUncommittedChangesAsync(); } async showChangedFilesAsync() { const gitStatusOutput = await (0, git_1.gitStatusAsync)({ showUntracked: true, cwd: this.maybeCwdOverride, }); log_1.default.log(gitStatusOutput); } async hasUncommittedChangesAsync() { const changes = await (0, git_1.gitStatusAsync)({ showUntracked: true, cwd: this.maybeCwdOverride }); return changes.length > 0; } async getRootPathAsync() { return (await (0, spawn_async_1.default)('git', ['rev-parse', '--show-toplevel'], { cwd: this.maybeCwdOverride, })).stdout.trim(); } async makeShallowCopyAsync(destinationPath) { if (await this.hasUncommittedChangesAsync()) { // it should already be checked before this function is called, but in case it wasn't // we want to ensure that any changes were introduced by call to `setGitCaseSensitivityAsync` throw new Error('You have some uncommitted changes in your repository.'); } let gitRepoUri; if (process.platform === 'win32') { // getRootDirectoryAsync() will return C:/path/to/repo on Windows and path // prefix should be file:/// gitRepoUri = `file:///${await this.getRootPathAsync()}`; } else { // getRootDirectoryAsync() will /path/to/repo, and path prefix should be // file:/// so only file:// needs to be prepended gitRepoUri = `file://${await this.getRootPathAsync()}`; } const isCaseSensitive = await isGitCaseSensitiveAsync(this.maybeCwdOverride); await setGitCaseSensitivityAsync(true, this.maybeCwdOverride); try { if (await this.hasUncommittedChangesAsync()) { log_1.default.error('Detected inconsistent filename casing between your local filesystem and git.'); log_1.default.error('This will likely cause your build to fail. Impacted files:'); await (0, spawn_async_1.default)('git', ['status', '--short'], { stdio: 'inherit', cwd: this.maybeCwdOverride, }); log_1.default.newLine(); log_1.default.error(`Error: Resolve filename casing inconsistencies before proceeding. ${(0, log_1.learnMore)('https://expo.fyi/macos-ignorecase')}`); throw new Error('You have some uncommitted changes in your repository.'); } await (0, spawn_async_1.default)('git', ['clone', '--no-hardlinks', '--depth', '1', gitRepoUri, destinationPath], { cwd: this.maybeCwdOverride, }); } finally { await setGitCaseSensitivityAsync(isCaseSensitive, this.maybeCwdOverride); } } async getCommitHashAsync() { try { return (await (0, spawn_async_1.default)('git', ['rev-parse', 'HEAD'], { cwd: this.maybeCwdOverride, })).stdout.trim(); } catch { return undefined; } } async trackFileAsync(file) { await (0, spawn_async_1.default)('git', ['add', '--intent-to-add', file], { cwd: this.maybeCwdOverride, }); } async getBranchNameAsync() { try { return (await (0, spawn_async_1.default)('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: this.maybeCwdOverride, })).stdout.trim(); } catch { return null; } } async getLastCommitMessageAsync() { try { return (await (0, spawn_async_1.default)('git', ['--no-pager', 'log', '-1', '--pretty=%B'], { cwd: this.maybeCwdOverride, })).stdout.trim(); } catch { return null; } } async showDiffAsync() { const outputTooLarge = (await (0, git_1.getGitDiffOutputAsync)(this.maybeCwdOverride)).split(/\r\n|\r|\n/).length > 100; await (0, git_1.gitDiffAsync)({ withPager: outputTooLarge, cwd: this.maybeCwdOverride }); } async isFileUntrackedAsync(path) { const withUntrackedFiles = await (0, git_1.gitStatusAsync)({ showUntracked: true, cwd: this.maybeCwdOverride, }); const trackedFiles = await (0, git_1.gitStatusAsync)({ showUntracked: false, cwd: this.maybeCwdOverride }); const pathWithoutLeadingDot = path.replace(/^\.\//, ''); // remove leading './' from path return (withUntrackedFiles.includes(pathWithoutLeadingDot) && !trackedFiles.includes(pathWithoutLeadingDot)); } async isFileIgnoredAsync(filePath) { try { await (0, spawn_async_1.default)('git', ['check-ignore', '-q', filePath], { cwd: this.maybeCwdOverride ?? path_1.default.normalize(await this.getRootPathAsync()), }); return true; } catch { return false; } } canGetLastCommitMessage() { return true; } } exports.default = GitClient; async function ensureGitConfiguredAsync({ nonInteractive, }) { let usernameConfigured = true; let emailConfigured = true; try { await (0, spawn_async_1.default)('git', ['config', '--get', 'user.name']); } catch (err) { log_1.default.debug(err); usernameConfigured = false; } try { await (0, spawn_async_1.default)('git', ['config', '--get', 'user.email']); } catch (err) { log_1.default.debug(err); emailConfigured = false; } if (usernameConfigured && emailConfigured) { return; } log_1.default.warn(`You need to configure Git with your ${[ !usernameConfigured && 'username (user.name)', !emailConfigured && 'email address (user.email)', ] .filter(i => i) .join(' and ')}`); if (nonInteractive) { throw new Error('Git cannot be configured automatically in non-interactive mode'); } if (!usernameConfigured) { const { username } = await (0, prompts_1.promptAsync)({ type: 'text', name: 'username', message: 'Username:', validate: (input) => input !== '', }); const spinner = (0, ora_1.ora)(`Running ${chalk_1.default.bold(`git config --local user.name ${username}`)}`).start(); try { await (0, spawn_async_1.default)('git', ['config', '--local', 'user.name', username]); spinner.succeed(); } catch (err) { spinner.fail(); throw err; } } if (!emailConfigured) { const { email } = await (0, prompts_1.promptAsync)({ type: 'text', name: 'email', message: 'Email address:', validate: (input) => input !== '', }); const spinner = (0, ora_1.ora)(`Running ${chalk_1.default.bold(`git config --local user.email ${email}`)}`).start(); try { await (0, spawn_async_1.default)('git', ['config', '--local', 'user.email', email]); spinner.succeed(); } catch (err) { spinner.fail(); throw err; } } } /** * Checks if git is configured to be case sensitive * @returns {boolean | undefined} * - boolean - is git case sensitive * - undefined - case sensitivity is not configured and git is using default behavior */ async function isGitCaseSensitiveAsync(cwd) { if (process.platform !== 'darwin') { return undefined; } try { const result = await (0, spawn_async_1.default)('git', ['config', '--get', 'core.ignorecase'], { cwd, }); const isIgnoreCaseEnabled = result.stdout.trim(); if (isIgnoreCaseEnabled === '') { return undefined; } else if (isIgnoreCaseEnabled === 'true') { return false; } else { return true; } } catch { return undefined; } } exports.isGitCaseSensitiveAsync = isGitCaseSensitiveAsync; async function setGitCaseSensitivityAsync(enable, cwd) { // we are assuming that if someone sets that on non-macos device then // they know what they are doing if (process.platform !== 'darwin') { return; } if (enable === undefined) { await (0, spawn_async_1.default)('git', ['config', '--unset', 'core.ignorecase'], { cwd, }); } else { await (0, spawn_async_1.default)('git', ['config', 'core.ignorecase', String(!enable)], { cwd, }); } }