diff --git a/tools/release/git/git-client.ts b/tools/release/git/git-client.ts index 9c424f26637..5861b79cd37 100644 --- a/tools/release/git/git-client.ts +++ b/tools/release/git/git-client.ts @@ -91,8 +91,19 @@ export class GitClient { } /** Pushes the specified tag to the remote git repository. */ - pushTagToRemote(tagName: string): boolean { - return this._spawnGitProcess(['push', this.remoteGitUrl, `refs/tags/${tagName}`]).status === 0; + 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'); } } diff --git a/tools/release/prompt/upstream-remote-prompt.ts b/tools/release/prompt/upstream-remote-prompt.ts new file mode 100644 index 00000000000..3fb093fedd5 --- /dev/null +++ b/tools/release/prompt/upstream-remote-prompt.ts @@ -0,0 +1,16 @@ +import {prompt} from 'inquirer'; + +/** + * Prompts the current user-input interface for a Git remote that refers to the upstream + * of the current Git project. + */ +export async function promptForUpstreamRemote(availableRemotes: string[]): Promise { + const {distTag} = await prompt<{ distTag: string }>({ + type: 'list', + name: 'distTag', + message: 'What is the Git remote for pushing changes upstream?', + choices: availableRemotes.map(remoteName => ({value: remoteName, name: remoteName})), + }); + + return distTag; +} diff --git a/tools/release/publish-release.ts b/tools/release/publish-release.ts index f9fa861abfb..1e547597c5a 100644 --- a/tools/release/publish-release.ts +++ b/tools/release/publish-release.ts @@ -9,6 +9,7 @@ import {GitClient} from './git/git-client'; import {getGithubNewReleaseUrl} from './git/github-urls'; import {isNpmAuthenticated, runInteractiveNpmLogin, runNpmPublish} from './npm/npm-client'; import {promptForNpmDistTag} from './prompt/npm-dist-tag-prompt'; +import {promptForUpstreamRemote} from './prompt/upstream-remote-prompt'; import {releasePackages} from './release-output/release-packages'; import {CHANGELOG_FILE_NAME} from './stage-release'; import {parseVersionName, Version} from './version-name/parse-version'; @@ -76,6 +77,7 @@ class PublishReleaseTask extends BaseReleaseTask { this.verifyLastCommitVersionBump(); this.verifyLocalCommitsMatchUpstream(publishBranch); + const upstreamRemote = await this.getProjectUpstreamRemote(); const npmDistTag = await promptForNpmDistTag(newVersion); // In case the user wants to publish a stable version to the "next" npm tag, we want @@ -101,7 +103,7 @@ class PublishReleaseTask extends BaseReleaseTask { // Create and push the release tag before publishing to NPM. this.createReleaseTag(newVersionName, releaseNotes); - this.pushReleaseTag(newVersionName); + this.pushReleaseTag(newVersionName, upstreamRemote); // Ensure that we are authenticated before running "npm publish" for each package. this.checkNpmAuthentication(); @@ -250,7 +252,7 @@ class PublishReleaseTask extends BaseReleaseTask { } /** Pushes the release tag to the remote repository. */ - private pushReleaseTag(tagName: string) { + private pushReleaseTag(tagName: string, upstreamRemote: string) { const remoteTagSha = this.git.getShaOfRemoteTag(tagName); const expectedSha = this.git.getLocalCommitSha('HEAD'); @@ -267,8 +269,8 @@ class PublishReleaseTask extends BaseReleaseTask { return; } - if (!this.git.pushTagToRemote(tagName)) { - console.error(red(` ✘ Could not push the "${tagName} "tag upstream.`)); + if (!this.git.pushTagToRemote(tagName, upstreamRemote)) { + console.error(red(` ✘ Could not push the "${tagName}" tag upstream.`)); console.error(red(` Please make sure you have permission to push to the ` + `"${this.git.remoteGitUrl}" remote.`)); process.exit(1); @@ -276,6 +278,18 @@ class PublishReleaseTask extends BaseReleaseTask { console.info(green(` ✓ Pushed release tag upstream.`)); } + + /** + * Determines the name of the Git remote that is used for pushing changes + * upstream to github. + */ + private async getProjectUpstreamRemote() { + const remoteName = this.git.hasRemote('upstream') ? + 'upstream' : await promptForUpstreamRemote(this.git.getAvailableRemotes()); + + console.info(green(` ✓ Using the "${remoteName}" remote for pushing changes upstream.`)); + return remoteName; + } } /** Entry-point for the create release script. */