mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
70aee92706
Building the release packages can take up a lot of time and we should rather run `npm login` before starting the build because otherwise if the login fails, the output needs to be rebuilt again. Additionally this commit fixes that existing local/remote tags are not properly parsed due to an issue with the Git references that refer to tags. Closes #16141
115 lines
4.6 KiB
TypeScript
115 lines
4.6 KiB
TypeScript
import {spawnSync, SpawnSyncReturns} from 'child_process';
|
|
|
|
/**
|
|
* Class that can be used to execute Git commands within a given project directory.
|
|
*
|
|
* Relying on the working directory of the current process is not good because it's not
|
|
* guaranteed that the working directory is always the target project directory.
|
|
*/
|
|
export class GitClient {
|
|
constructor(public projectDir: string, public remoteGitUrl: string) {}
|
|
|
|
/**
|
|
* Spawns a child process running Git. The "stderr" output is inherited and will be printed
|
|
* in case of errors. This makes it easier to debug failed commands.
|
|
*/
|
|
private _spawnGitProcess(args: string[], printStderr = true): SpawnSyncReturns<string> {
|
|
return spawnSync('git', args, {
|
|
cwd: this.projectDir,
|
|
stdio: ['pipe', 'pipe', printStderr ? 'inherit' : 'pipe'],
|
|
encoding: 'utf8',
|
|
});
|
|
}
|
|
|
|
/** Gets the currently checked out branch for the project directory. */
|
|
getCurrentBranch() {
|
|
return this._spawnGitProcess(['symbolic-ref', '--short', 'HEAD']).stdout.trim();
|
|
}
|
|
|
|
/** Gets the commit SHA for the specified remote repository branch. */
|
|
getRemoteCommitSha(branchName: string): string {
|
|
return this._spawnGitProcess(['ls-remote', this.remoteGitUrl, '-h', `refs/heads/${branchName}`])
|
|
.stdout.split('\t')[0]
|
|
.trim();
|
|
}
|
|
|
|
/** Gets the latest commit SHA for the specified git reference. */
|
|
getLocalCommitSha(refName: string) {
|
|
return this._spawnGitProcess(['rev-parse', refName]).stdout.trim();
|
|
}
|
|
|
|
/** Gets whether the current Git repository has uncommitted changes. */
|
|
hasUncommittedChanges(): boolean {
|
|
return this._spawnGitProcess(['diff-index', '--quiet', 'HEAD']).status !== 0;
|
|
}
|
|
|
|
/** Checks out an existing branch with the specified name. */
|
|
checkoutBranch(branchName: string): boolean {
|
|
return this._spawnGitProcess(['checkout', branchName]).status === 0;
|
|
}
|
|
|
|
/** Creates a new branch which is based on the previous active branch. */
|
|
checkoutNewBranch(branchName: string): boolean {
|
|
return this._spawnGitProcess(['checkout', '-b', branchName]).status === 0;
|
|
}
|
|
|
|
/** Stages all changes by running `git add -A`. */
|
|
stageAllChanges(): boolean {
|
|
return this._spawnGitProcess(['add', '-A']).status === 0;
|
|
}
|
|
|
|
/** Creates a new commit within the current branch with the given commit message. */
|
|
createNewCommit(message: string): boolean {
|
|
// Disable pre-commit hooks when creating a commit. Developers might not have set up
|
|
// their Git configuration for the "git-clang-format" pre-commit hook.
|
|
return this._spawnGitProcess(['commit', '--no-verify', '-m', message]).status === 0;
|
|
}
|
|
|
|
/** Gets the title of a specified commit reference. */
|
|
getCommitTitle(commitRef: string): string {
|
|
return this._spawnGitProcess(['log', '-n1', '--format="%s"', commitRef]).stdout.trim();
|
|
}
|
|
|
|
/** Creates a tag for the specified commit reference. */
|
|
createTag(commitRef: string, tagName: string, message: string): boolean {
|
|
return this._spawnGitProcess(['tag', tagName, '-m', message]).status === 0;
|
|
}
|
|
|
|
/** Checks whether the specified tag exists locally. */
|
|
hasLocalTag(tagName: string) {
|
|
return this._spawnGitProcess(['rev-parse', `refs/tags/${tagName}`], false).status === 0;
|
|
}
|
|
|
|
/** Gets the Git SHA of the specified local tag. */
|
|
getShaOfLocalTag(tagName: string) {
|
|
// We need to use the "^{}" suffix to instruct Git to deference the tag to
|
|
// the actual commit. See: https://www.git-scm.com/docs/git-rev-parse
|
|
return this._spawnGitProcess(['rev-parse', `refs/tags/${tagName}^{}`]).stdout.trim();
|
|
}
|
|
|
|
/** Gets the Git SHA of the specified remote tag. */
|
|
getShaOfRemoteTag(tagName: string): string {
|
|
// We need to use the "^{}" suffix to instruct Git to deference the tag to
|
|
// the actual commit. See: https://www.git-scm.com/docs/git-rev-parse
|
|
return this._spawnGitProcess(['ls-remote', this.remoteGitUrl, '-t', `refs/tags/${tagName}^{}`])
|
|
.stdout.split('\t')[0]
|
|
.trim();
|
|
}
|
|
|
|
/** Pushes the specified tag to the remote git repository. */
|
|
pushTagToRemote(tagName: string, remoteName: string = this.remoteGitUrl): boolean {
|
|
return this._spawnGitProcess(['push', remoteName, `refs/tags/${tagName}`]).status === 0;
|
|
}
|
|
|
|
/** Checks whether the given remote has been set up. */
|
|
hasRemote(remoteName: string): boolean {
|
|
return this._spawnGitProcess(['remote', 'get-url', remoteName], false).status == 0;
|
|
}
|
|
|
|
/** Gets a list of all available remotes set up. */
|
|
getAvailableRemotes(): string[] {
|
|
// Note that "git" always uses a line feed for new lines.
|
|
return this._spawnGitProcess(['remote']).stdout.trim().split('\n');
|
|
}
|
|
}
|