eas-cli
Version:
EAS command line tool
321 lines (320 loc) • 12.9 kB
JavaScript
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,
});
}
}
;