Skip to content

Commit 29f10da

Browse files
committed
Merge branch 'pawang/improveGitBranchNaming' of github.com:microsoft/vscode-pull-request-github into pawang/improveGitBranchNaming
2 parents a3cc1a2 + aef3293 commit 29f10da

File tree

15 files changed

+1192
-395
lines changed

15 files changed

+1192
-395
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
# Changelog
22

3+
## 0.120.0
4+
5+
### Changes
6+
7+
- The `#openPullRequest` tool recognizes open PR diffs and PR files as being the "open pull request".
8+
- All Copilot PR notifications can be marked as ready using the right-click context menu on the Copilot section header in the Pull Requests view.
9+
- The setting `githubIssues.issueAvatarDisplay` can be used to control whether the first assignee's avatar or the author's avatar is shown in the Issues view.
10+
- Instead of always running the pull request queries that back the Pull Requests view when refreshing, we now check to see if there are new PRs in the repo before running the queries. This should reduce API usage when there are no new PRs.
11+
- The "Copy link" action is back near the PR title in the pull request description webview.
12+
- You can configure that the default branch is pulled when you're "done" with a PR using `"githubPullRequests.postDone": "checkoutDefaultBranchAndPull"`.
13+
14+
### Fixes
15+
16+
- Unable to get list of users to assign them to a pull request. https://github.com/microsoft/vscode-pull-request-github/issues/7908
17+
- Error notifications when using GitHub Enterprise Server. https://github.com/microsoft/vscode-pull-request-github/issues/7901
18+
- Ignore worktrees that aren't in one of the workspace folders. https://github.com/microsoft/vscode-pull-request-github/issues/7896
19+
- Typing "#" and then Enter or Tab opens the GitHub issue queries settings. https://github.com/microsoft/vscode-pull-request-github/issues/7838
20+
- Unexpected branch switching when githubIssues.useBranchForIssues = off. https://github.com/microsoft/vscode-pull-request-github/issues/7827
21+
- Extension enters rapid refresh loop, causing high API usage and rate limiting. https://github.com/microsoft/vscode-pull-request-github/issues/7816
22+
- GitHub PR view highlights all repos with Copilot notification. https://github.com/microsoft/vscode-pull-request-github/issues/7852
23+
- Wrong commit is checked out when local branch exists with the same name. https://github.com/microsoft/vscode-pull-request-github/issues/7702
24+
- Visual Label not provided for "Title" and "Description" field. https://github.com/microsoft/vscode-pull-request-github/issues/7595
25+
- VSCode unresponsive during GitHub Pull Requests PR checkout (large number of files changed). https://github.com/microsoft/vscode-pull-request-github/issues/6952
26+
- extension explodes and kicks back out to GITHUB: LOGIN when non github repos are in working directory (specifically codeberg). https://github.com/microsoft/vscode-pull-request-github/issues/6945
27+
328
## 0.118.2
429

530
### Fixes

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3681,7 +3681,7 @@
36813681
"displayName": "%languageModelTools.github-pull-request_issue_fetch.displayName%",
36823682
"modelDescription": "Get a GitHub issue/PR's details as a JSON object.",
36833683
"icon": "$(info)",
3684-
"canBeReferencedInPrompt": false,
3684+
"canBeReferencedInPrompt": true,
36853685
"inputSchema": {
36863686
"type": "object",
36873687
"properties": {
@@ -3937,7 +3937,7 @@
39373937
"displayName": "%languageModelTools.github-pull-request_suggest-fix.displayName%",
39383938
"modelDescription": "Summarize and suggest a fix for a GitHub issue.",
39393939
"icon": "$(info)",
3940-
"canBeReferencedInPrompt": false,
3940+
"canBeReferencedInPrompt": true,
39413941
"inputSchema": {
39423942
"type": "object",
39433943
"properties": {
@@ -3984,7 +3984,7 @@
39843984
"displayName": "%languageModelTools.github-pull-request_formSearchQuery.displayName%",
39853985
"modelDescription": "Converts natural language to a GitHub search query. Should ALWAYS be called before doing a search.",
39863986
"icon": "$(search)",
3987-
"canBeReferencedInPrompt": false,
3987+
"canBeReferencedInPrompt": true,
39883988
"inputSchema": {
39893989
"type": "object",
39903990
"properties": {
@@ -4028,7 +4028,7 @@
40284028
"displayName": "%languageModelTools.github-pull-request_doSearch.displayName%",
40294029
"modelDescription": "Execute a GitHub search given a well formed GitHub search query. Call github-pull-request_formSearchQuery first to get good search syntax and pass the exact result in as the 'query'.",
40304030
"icon": "$(search)",
4031-
"canBeReferencedInPrompt": false,
4031+
"canBeReferencedInPrompt": true,
40324032
"inputSchema": {
40334033
"type": "object",
40344034
"properties": {
@@ -4074,7 +4074,7 @@
40744074
"displayName": "%languageModelTools.github-pull-request_renderIssues.displayName%",
40754075
"modelDescription": "Render issue items from an issue search in a markdown table. The markdown table will be displayed directly to the user by the tool. No further display should be done after this!",
40764076
"icon": "$(paintcan)",
4077-
"canBeReferencedInPrompt": false,
4077+
"canBeReferencedInPrompt": true,
40784078
"inputSchema": {
40794079
"type": "object",
40804080
"properties": {

src/common/uri.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,17 @@ export namespace DataUri {
205205
const iconsFolder = 'userIcons';
206206

207207
function iconFilename(user: IAccount | ITeam): string {
208-
return `${reviewerId(user)}.jpg`;
208+
// Include avatarUrl hash to invalidate cache when URL changes
209+
const baseId = reviewerId(user);
210+
if (user.avatarUrl) {
211+
// Create a simple hash of the URL to detect changes
212+
const urlHash = user.avatarUrl.split('').reduce((a, b) => {
213+
a = ((a << 5) - a) + b.charCodeAt(0);
214+
return a & a;
215+
}, 0);
216+
return `${baseId}_${Math.abs(urlHash)}.jpg`;
217+
}
218+
return `${baseId}.jpg`;
209219
}
210220

211221
function cacheLocation(context: vscode.ExtensionContext): vscode.Uri {
@@ -291,7 +301,6 @@ export namespace DataUri {
291301
const startingCacheSize = cacheLogOrder.length;
292302

293303
const results = await Promise.all(users.map(async (user) => {
294-
295304
const imageSourceUrl = user.avatarUrl;
296305
if (imageSourceUrl === undefined) {
297306
return undefined;
@@ -311,6 +320,9 @@ export namespace DataUri {
311320
cacheMiss = true;
312321
const doFetch = async () => {
313322
const response = await fetch(imageSourceUrl.toString());
323+
if (!response.ok) {
324+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
325+
}
314326
const buffer = await response.arrayBuffer();
315327
await writeAvatarToCache(context, user, new Uint8Array(buffer));
316328
innerImageContents = Buffer.from(buffer);

src/github/copilotRemoteAgent.ts

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const CONTINUE_WITHOUT_PUSHING = vscode.l10n.t('Ignore changes');
4242
const CONTINUE_AND_DO_NOT_ASK_AGAIN = vscode.l10n.t('Continue and don\'t ask again');
4343

4444
const CONTINUE_TRUNCATION = vscode.l10n.t('Continue with truncation');
45+
const DELEGATE_MODAL_DETAILS = vscode.l10n.t('The agent will work asynchronously to create a pull request with your requested changes.');
4546

4647
const COPILOT = '@copilot';
4748

@@ -72,23 +73,25 @@ export namespace SessionIdForPr {
7273

7374
export class CopilotRemoteAgentManager extends Disposable {
7475
async chatParticipantImpl(request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) {
75-
const startSession = async (prompt: string, history: ReadonlyArray<vscode.ChatRequestTurn | vscode.ChatResponseTurn>, source: string, chatSummary?: { prompt?: string; history?: string }) => {
76+
const startSession = async (source: string) => {
7677
/* __GDPR__
7778
"copilot.remoteagent.editor.invoke" : {
7879
"promptLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
7980
"historyLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
80-
"hasPromptSummary" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
81-
"hasHistorySummary" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
81+
"promptSummaryLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
82+
"historySummaryLength" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
8283
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
8384
}
8485
*/
85-
const promptSummary = chatSummary?.prompt;
86-
const historySummary = chatSummary?.history;
86+
const prompt = request.prompt;
87+
const history = context.history;
88+
const promptSummary = context.chatSummary?.prompt;
89+
const historySummary = context.chatSummary?.history;
8790
this.telemetry.sendTelemetryEvent('copilot.remoteagent.editor.invoke', {
8891
promptLength: prompt.length.toString(),
8992
historyLength: history?.length.toString(),
90-
hasPromptSummary: String(!!promptSummary),
91-
hasHistorySummary: String(!!historySummary),
93+
promptSummaryLength: promptSummary?.length.toString() || '0',
94+
historySummaryLength: historySummary?.length.toString() || '0',
9295
source,
9396
});
9497
stream.progress(vscode.l10n.t('Delegating to coding agent'));
@@ -109,9 +112,48 @@ export class CopilotRemoteAgentManager extends Disposable {
109112
return result.number;
110113
};
111114

115+
const handleConfirmationData = async () => {
116+
const results: Array<{ step: string; accepted: boolean }> = [];
117+
results.push(...(request.acceptedConfirmationData?.map(data => ({ step: data.step, accepted: true })) ?? []));
118+
results.push(...((request.rejectedConfirmationData ?? []).filter(data => !results.some(r => r.step === data.step)).map(data => ({ step: data.step, accepted: false }))));
119+
for (const data of results) {
120+
switch (data.step) {
121+
case 'create':
122+
if (!data.accepted) {
123+
stream.markdown(vscode.l10n.t('Coding agent request cancelled.'));
124+
return {};
125+
}
126+
const number = await startSession('chat');
127+
if (!number) {
128+
return {};
129+
}
130+
const pullRequest = await this.findPullRequestById(number, true);
131+
if (!pullRequest) {
132+
stream.warning(vscode.l10n.t('Could not find the associated pull request {0} for this chat session.', number));
133+
return {};
134+
}
135+
const uri = await toOpenPullRequestWebviewUri({ owner: pullRequest.remote.owner, repo: pullRequest.remote.repositoryName, pullRequestNumber: pullRequest.number });
136+
const plaintextBody = marked.parse(pullRequest.body, { renderer: new PlainTextRenderer(true), smartypants: true }).trim();
137+
const card = new vscode.ChatResponsePullRequestPart(uri, pullRequest.title, plaintextBody, pullRequest.author.specialDisplayName ?? pullRequest.author.login, `#${pullRequest.number}`);
138+
stream.push(card);
139+
stream.markdown(vscode.l10n.t('GitHub Copilot coding agent has begun working on your request. Follow its progress in the associated chat and pull request.'));
140+
vscode.window.showChatSession(COPILOT_SWE_AGENT, String(number), { viewColumn: vscode.ViewColumn.Active });
141+
break;
142+
default:
143+
stream.warning(`Unknown confirmation step: ${data.step}\n\n`);
144+
break;
145+
}
146+
}
147+
return {};
148+
};
149+
150+
if (request.acceptedConfirmationData || request.rejectedConfirmationData) {
151+
return await handleConfirmationData();
152+
}
153+
112154
if (context.chatSessionContext?.isUntitled) {
113155
/* Generate new coding agent session from an 'untitled' session */
114-
const number = await startSession(request.prompt, context.history, 'untitledChatSession');
156+
const number = await startSession('untitledChatSession');
115157
if (!number) {
116158
return {};
117159
}
@@ -175,25 +217,13 @@ export class CopilotRemoteAgentManager extends Disposable {
175217
} else {
176218
/* @copilot invoked from a 'normal' chat */
177219

178-
// TODO(jospicer): Use confirmations to guide users
179-
180-
const number = await startSession(request.prompt, context.history, 'chat', context.chatSummary); // TODO(jospicer): 'All of the chat messages so far in the current chat session. Currently, only chat messages for the current participant are included'
181-
if (!number) {
182-
return {};
183-
}
184-
const pullRequest = await this.findPullRequestById(number, true);
185-
if (!pullRequest) {
186-
stream.warning(vscode.l10n.t('Could not find the associated pull request {0} for this chat session.', number));
187-
return {};
188-
}
189-
190-
const uri = await toOpenPullRequestWebviewUri({ owner: pullRequest.remote.owner, repo: pullRequest.remote.repositoryName, pullRequestNumber: pullRequest.number });
191-
const plaintextBody = marked.parse(pullRequest.body, { renderer: new PlainTextRenderer(true), smartypants: true }).trim();
220+
stream.confirmation(
221+
vscode.l10n.t('Delegate to coding agent'),
222+
DELEGATE_MODAL_DETAILS,
223+
{ step: 'create' },
224+
['Delegate', 'Cancel']
225+
);
192226

193-
const card = new vscode.ChatResponsePullRequestPart(uri, pullRequest.title, plaintextBody, pullRequest.author.specialDisplayName ?? pullRequest.author.login, `#${pullRequest.number}`);
194-
stream.push(card);
195-
stream.markdown(vscode.l10n.t('GitHub Copilot coding agent has begun working on your request. Follow its progress in the associated chat and pull request.'));
196-
vscode.window.showChatSession(COPILOT_SWE_AGENT, String(number), { viewColumn: vscode.ViewColumn.Active });
197227
}
198228
}
199229

@@ -549,7 +579,7 @@ export class CopilotRemoteAgentManager extends Disposable {
549579

550580
let autoPushAndCommit = false;
551581
const message = vscode.l10n.t('Copilot coding agent will continue your work in \'{0}\'.', repoName);
552-
const detail = vscode.l10n.t('Your chat context will be used to continue work in a new pull request.');
582+
const detail = DELEGATE_MODAL_DETAILS;
553583
if (source !== 'prompt' && hasChanges && CopilotRemoteAgentConfig.getAutoCommitAndPushEnabled()) {
554584
// Pending changes modal
555585
const modalResult = await vscode.window.showInformationMessage(

src/github/folderRepositoryManager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,13 +1303,13 @@ export class FolderRepositoryManager extends Disposable {
13031303
* Pull request defaults in the query, like owner and repository variables, will be resolved.
13041304
*/
13051305
async getIssues(
1306-
query?: string,
1306+
query?: string, options: IPullRequestsPagingOptions = { fetchNextPage: false, fetchOnePagePerRepo: false }
13071307
): Promise<ItemsResponseResult<IssueModel> | undefined> {
13081308
if (this.gitHubRepositories.length === 0) {
13091309
return undefined;
13101310
}
13111311
try {
1312-
const data = await this.fetchPagedData<Issue>({ fetchNextPage: false, fetchOnePagePerRepo: false }, `issuesKey${query}`, PagedDataType.IssueSearch, PRType.All, query);
1312+
const data = await this.fetchPagedData<Issue>(options, `issuesKey${query}`, PagedDataType.IssueSearch, PRType.All, query);
13131313
const mappedData: ItemsResponseResult<IssueModel> = {
13141314
items: [],
13151315
hasMorePages: data.hasMorePages,

src/github/githubRepository.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as buffer from 'buffer';
7-
import { ApolloQueryResult, DocumentNode, FetchResult, MutationOptions, NetworkStatus, QueryOptions } from 'apollo-boost';
7+
import { ApolloQueryResult, DocumentNode, FetchResult, MutationOptions, NetworkStatus, OperationVariables, QueryOptions } from 'apollo-boost';
88
import LRUCache from 'lru-cache';
99
import * as vscode from 'vscode';
1010
import { AuthenticationError, AuthProvider, GitHubServerType, isSamlError } from '../common/authentication';
@@ -172,6 +172,7 @@ export class GitHubRepository extends Disposable {
172172
// eslint-disable-next-line rulesdir/no-any-except-union-method-signature
173173
private _queriesSchema: any;
174174
private _areQueriesLimited: boolean = false;
175+
get areQueriesLimited(): boolean { return this._areQueriesLimited; }
175176

176177
private _onDidAddPullRequest: vscode.EventEmitter<PullRequestModel> = this._register(new vscode.EventEmitter());
177178
public readonly onDidAddPullRequest: vscode.Event<PullRequestModel> = this._onDidAddPullRequest.event;
@@ -277,7 +278,7 @@ export class GitHubRepository extends Disposable {
277278
}
278279
}
279280

280-
query = async <T>(query: QueryOptions, ignoreSamlErrors: boolean = false, legacyFallback?: { query: DocumentNode }): Promise<ApolloQueryResult<T>> => {
281+
query = async <T>(query: QueryOptions, ignoreSamlErrors: boolean = false, legacyFallback?: { query: DocumentNode, variables?: OperationVariables }): Promise<ApolloQueryResult<T>> => {
281282
const gql = this.authMatchesServer && this.hub && this.hub.graphql;
282283
if (!gql) {
283284
const logValue = (query.query.definitions[0] as { name: { value: string } | undefined }).name?.value;
@@ -299,6 +300,7 @@ export class GitHubRepository extends Disposable {
299300
Logger.error(`Error querying GraphQL API (${logInfo}): ${e.message}${gqlErrors ? `. ${gqlErrors.map(error => error.extensions?.code).join(',')}` : ''}`, this.id);
300301
if (legacyFallback) {
301302
query.query = legacyFallback.query;
303+
query.variables = legacyFallback.variables;
302304
return this.query(query, ignoreSamlErrors);
303305
}
304306

@@ -426,7 +428,7 @@ export class GitHubRepository extends Disposable {
426428
}
427429

428430
if (oldHub !== this._hub) {
429-
if (this._areQueriesLimited || this._credentialStore.areScopesOld(this.remote.authProviderId)) {
431+
if (this._areQueriesLimited || this._credentialStore.areScopesOld(this.remote.authProviderId) || (this.remote.authProviderId === AuthProvider.githubEnterprise)) {
430432
this._areQueriesLimited = true;
431433
this._queriesSchema = mergeQuerySchemaWithShared(sharedSchema.default as any, limitedSchema.default as any);
432434
} else {
@@ -1330,6 +1332,14 @@ export class GitHubRepository extends Disposable {
13301332
first: 100,
13311333
after: after,
13321334
},
1335+
}, false, {
1336+
query: schema.GetAssignableUsers,
1337+
variables: {
1338+
owner: remote.owner,
1339+
name: remote.repositoryName,
1340+
first: 100,
1341+
after: after,
1342+
}
13331343
});
13341344

13351345
} else {

src/github/pullRequestModel.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,16 +1099,20 @@ export class PullRequestModel extends IssueModel<PullRequest> implements IPullRe
10991099
*/
11001100
async requestReview(reviewers: IAccount[], teamReviewers: ITeam[], union: boolean = false): Promise<void> {
11011101
const { mutate, schema } = await this.githubRepository.ensure();
1102+
const input: { pullRequestId: string, teamIds: string[], userIds: string[], botIds?: string[], union: boolean } = {
1103+
pullRequestId: this.graphNodeId,
1104+
teamIds: teamReviewers.map(t => t.id),
1105+
userIds: reviewers.filter(r => r.accountType !== AccountType.Bot).map(r => r.id),
1106+
union
1107+
};
1108+
if (!this.githubRepository.areQueriesLimited) {
1109+
input.botIds = reviewers.filter(r => r.accountType === AccountType.Bot).map(r => r.id);
1110+
}
1111+
11021112
const { data } = await mutate<GetReviewRequestsResponse>({
11031113
mutation: schema.AddReviewers,
11041114
variables: {
1105-
input: {
1106-
pullRequestId: this.graphNodeId,
1107-
teamIds: teamReviewers.map(t => t.id),
1108-
userIds: reviewers.filter(r => r.accountType !== AccountType.Bot).map(r => r.id),
1109-
botIds: reviewers.filter(r => r.accountType === AccountType.Bot).map(r => r.id),
1110-
union
1111-
},
1115+
input
11121116
},
11131117
});
11141118

0 commit comments

Comments
 (0)