Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 54 additions & 9 deletions src/github/createPRViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
PullRequestDefaults,
titleAndBodyFrom,
} from './folderRepositoryManager';
import { GitHubRepository } from './githubRepository';
import { GitHubRepository, isRateLimitError, ViewerPermission } from './githubRepository';
import { IAccount, ILabel, IMilestone, IProject, isITeam, ITeam, MergeMethod, RepoAccessAndMergeMethods } from './interface';
import { BaseBranchMetadata, PullRequestGitHelper } from './pullRequestGitHelper';
import { PullRequestModel } from './pullRequestModel';
Expand Down Expand Up @@ -211,13 +211,47 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques

const defaultBaseBranch = detectedBaseMetadata?.branch ?? this._pullRequestDefaults.base;

const [defaultTitleAndDescription, mergeConfiguration, viewerPermission, mergeQueueMethodForBranch, labels] = await Promise.all([
this.getTitleAndDescription(defaultCompareBranch, defaultBaseBranch),
this.getMergeConfiguration(defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
defaultOrigin.getViewerPermission(),
this._folderRepositoryManager.mergeQueueMethodForBranch(defaultBaseBranch, defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
this.getPullRequestDefaultLabels(defaultBaseRemote)
]);
let defaultTitleAndDescription: { title: string; description: string };
let mergeConfiguration: RepoAccessAndMergeMethods;
let viewerPermission: ViewerPermission;
let mergeQueueMethodForBranch: MergeMethod | undefined;
let labels: ILabel[];
try {
[defaultTitleAndDescription, mergeConfiguration, viewerPermission, mergeQueueMethodForBranch, labels] = await Promise.all([
this.getTitleAndDescription(defaultCompareBranch, defaultBaseBranch),
this.getMergeConfiguration(defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
defaultOrigin.getViewerPermission(),
this._folderRepositoryManager.mergeQueueMethodForBranch(defaultBaseBranch, defaultBaseRemote.owner, defaultBaseRemote.repositoryName),
this.getPullRequestDefaultLabels(defaultBaseRemote)
]);
} catch (e) {
if (isRateLimitError(e)) {
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'));
}
Logger.error(`Error initializing create pull request view: ${e}`, BaseCreatePullRequestViewProvider.ID);
return {
canModifyBranches: true,
defaultBaseRemote,
defaultBaseBranch,
defaultCompareRemote,
defaultCompareBranch: this._defaultCompareBranch,
defaultTitle: '',
defaultDescription: '',
baseHasMergeQueue: false,
remoteCount: remotes.length,
autoMergeDefault: false,
createError: '',
isDraftDefault: false,
isDarkTheme: vscode.window.activeColorTheme.kind === vscode.ColorThemeKind.Dark,
generateTitleAndDescriptionTitle: undefined,
creating: false,
initializeWithGeneratedTitleAndDescription: false,
preReviewState: PreReviewState.None,
preReviewer: undefined,
reviewing: false,
usingTemplate: false,
};
}

const defaultCreateOption = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<'lastUsed' | 'create' | 'createDraft' | 'createAutoMerge'>(DEFAULT_CREATE_OPTION, 'lastUsed');
const lastCreateMethod: { autoMerge: boolean, mergeMethod: MergeMethod | undefined, isDraft: boolean } | undefined = this._folderRepositoryManager.context.workspaceState.get<{ autoMerge: boolean, mergeMethod: MergeMethod, isDraft } | undefined>(PREVIOUS_CREATE_METHOD, undefined);
Expand Down Expand Up @@ -994,7 +1028,18 @@ Don't forget to commit your template file to the repository so that it can be us
}

private async processRemoteAndBranchResult(githubRepository: GitHubRepository, result: { remote: RemoteInfo, branch: string }, isBase: boolean) {
const [defaultBranch, viewerPermission] = await Promise.all([githubRepository.getDefaultBranch(), githubRepository.getViewerPermission()]);
let viewerPermission: ViewerPermission;
try {
viewerPermission = await githubRepository.getViewerPermission();
} catch (e) {
if (isRateLimitError(e)) {
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'));
viewerPermission = ViewerPermission.Unknown;
} else {
throw e;
}
}
const defaultBranch = await githubRepository.getDefaultBranch();

commands.setContext(contexts.CREATE_PR_PERMISSIONS, viewerPermission);
let chooseResult: ChooseBaseRemoteAndBranchResult | ChooseCompareRemoteAndBranchResult;
Expand Down
13 changes: 11 additions & 2 deletions src/github/folderRepositoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ConflictModel } from './conflictGuide';
import { ConflictResolutionCoordinator } from './conflictResolutionCoordinator';
import { Conflict, ConflictResolutionModel } from './conflictResolutionModel';
import { CredentialStore } from './credentials';
import { CopilotWorkingStatus, GitHubRepository, ItemsData, PULL_REQUEST_PAGE_SIZE, PullRequestChangeEvent, PullRequestData, TeamReviewerRefreshKind, ViewerPermission } from './githubRepository';
import { CopilotWorkingStatus, GitHubRepository, isRateLimitError, ItemsData, PULL_REQUEST_PAGE_SIZE, PullRequestChangeEvent, PullRequestData, TeamReviewerRefreshKind, ViewerPermission } from './githubRepository';
import { PullRequestResponse, PullRequestState } from './graphql';
import { IAccount, ILabel, IMilestone, IProject, IPullRequestsPagingOptions, Issue, ITeam, MergeMethod, PRType, PullRequestMergeability, RepoAccessAndMergeMethods, User } from './interface';
import { IssueModel } from './issueModel';
Expand Down Expand Up @@ -2957,7 +2957,16 @@ export class FolderRepositoryManager extends Disposable {
pushRemote,
this.credentialStore,
);
const permission = await githubRepo.getViewerPermission();
let permission: ViewerPermission;
try {
permission = await githubRepo.getViewerPermission();
} catch (e) {
if (isRateLimitError(e)) {
vscode.window.showErrorMessage(vscode.l10n.t('GitHub API rate limit exceeded. Please wait and try again.'), { modal: true });
return;
}
throw e;
}
let selectedRemote: GitHubRemote | undefined;
if (
permission === ViewerPermission.Read ||
Expand Down
23 changes: 23 additions & 0 deletions src/github/githubRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ export enum ViewerPermission {
Write = 'WRITE',
}

export class RateLimitError extends Error {
constructor(message?: string) {
super(message ?? 'GitHub API rate limit exceeded');
this.name = 'RateLimitError';
}
}

export function isRateLimitError(e: unknown): boolean {
if (e instanceof RateLimitError) {
return true;
}
if (e instanceof Error) {
const msg = e.message.toLowerCase();
if (msg.includes('rate limit') || msg.includes('secondary rate') || msg.includes('abuse detection')) {
return true;
}
}
return false;
}

export enum TeamReviewerRefreshKind {
None,
Try,
Expand Down Expand Up @@ -941,6 +961,9 @@ export class GitHubRepository extends Disposable {
return parseGraphQLViewerPermission(data);
} catch (e) {
Logger.error(`Unable to fetch viewer permission: ${e}`, this.id);
if (isRateLimitError(e)) {
throw new RateLimitError();
}
return ViewerPermission.Unknown;
}
}
Expand Down