Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7ed7da9

Browse files
Merge pull request cangzhang#7 from cangzhang/feat/create-mr
2 parents 585327e + 5cd0050 commit 7ed7da9

File tree

6 files changed

+245
-78
lines changed

6 files changed

+245
-78
lines changed

‎package.json

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@
3737
"title": "Logout coding.net",
3838
"category": "Coding plugin"
3939
},
40+
{
41+
"command": "codingPlugin.newMrDesc",
42+
"title": "New Merge Request",
43+
"category": "Coding plugin",
44+
"icon": "$(add)"
45+
},
46+
{
47+
"command": "codingPlugin.createMr",
48+
"title": "Create Merge Request from current document",
49+
"category": "Coding plugin"
50+
},
4051
{
4152
"command": "codingPlugin.refresh",
4253
"title": "Refresh",
@@ -51,6 +62,11 @@
5162
],
5263
"menus": {
5364
"view/title": [
65+
{
66+
"command": "codingPlugin.newMrDesc",
67+
"when": "view == mrTreeView",
68+
"group": "navigation"
69+
},
5470
{
5571
"command": "codingPlugin.refresh",
5672
"when": "view == mrTreeView",
@@ -61,8 +77,13 @@
6177
"viewsWelcome": [
6278
{
6379
"view": "mrTreeView",
64-
"contents": "Please login first.\n[Login](command:codingPlugin.login)",
65-
"when": "!loggedIn"
80+
"contents": "Please open a repo hosted by [coding.net](https://coding.net).\n [Open folder](command:vscode.openFolder)",
81+
"when": "!hasTeam"
82+
},
83+
{
84+
"view": "mrTreeView",
85+
"contents": "Please login first.\n [Login](command:codingPlugin.login)",
86+
"when": "!loggedIn && hasTeam"
6687
},
6788
{
6889
"view": "mrTreeView",

‎src/codingServer.ts

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
IMRDetailResponse,
1212
IMRActivitiesResponse,
1313
IMRReviewersResponse,
14+
ICreateMRBody,
15+
ICreateMRResp,
16+
IBranchListResp,
1417
} from 'src/typings/respResult';
1518
import { PromiseAdapter, promiseFromEvent, parseQuery, parseCloneUrl } from 'src/common/utils';
1619
import { GitService } from 'src/common/gitService';
@@ -87,10 +90,7 @@ export class CodingServer {
8790
): Promise<ISessionData> {
8891
try {
8992
const repoInfo = this._context.workspaceState.get(`repoInfo`) as IRepoInfo;
90-
if (!repoInfo?.team) {
91-
throw new Error(`team not exist`);
92-
}
93-
93+
vscode.commands.executeCommand('setContext', 'hasTeam', !!repoInfo?.team);
9494
const result = await this.getUserInfo(repoInfo.team || ``, accessToken);
9595
const { data: userInfo } = result;
9696
const ret: ISessionData = {
@@ -209,7 +209,7 @@ export class CodingServer {
209209
public async getUserInfo(team: string, token: string = this._session?.accessToken || ``) {
210210
try {
211211
const result: CodingResponse = await got
212-
.get(`https://codingcorp.coding.net/api/current_user`, {
212+
.get(`https://${team||`codingcorp`}.coding.net/api/current_user`, {
213213
searchParams: {
214214
access_token: token,
215215
},
@@ -233,42 +233,35 @@ export class CodingServer {
233233

234234
public static async getRepoParams() {
235235
const urls = await GitService.getRemoteURLs();
236-
// TODO: multiple working repos
237-
const url = urls?.[0];
238-
return parseCloneUrl(url || ``);
236+
const result = urls?.map((i) => parseCloneUrl(i || ``));
237+
return result?.[0];
239238
}
240239

241240
public getApiPrefix() {
242241
const repoInfo = this._context.workspaceState.get(`repoInfo`) as IRepoInfo;
243242
if (!repoInfo?.team) {
243+
vscode.commands.executeCommand('setContext', 'hasTeam', false);
244244
throw new Error(`team not exist`);
245245
}
246+
247+
vscode.commands.executeCommand('setContext', 'hasTeam', true);
246248
return `https://${repoInfo.team}.coding.net/api/user/${this._session?.user?.team}/project/${repoInfo.project}/depot/${repoInfo.repo}`;
247249
}
248250

249251
public async getMRList(repo?: string, status?: string): Promise<CodingResponse> {
250252
try {
251-
const repoInfo = this._context.workspaceState.get(`repoInfo`) as IRepoInfo;
252-
if (!repoInfo?.team) {
253-
throw new Error(`team not exist`);
254-
}
255-
253+
const url = this.getApiPrefix();
256254
const result: CodingResponse = await got
257-
.get(
258-
`https://${repoInfo.team}.coding.net/api/user/${repoInfo.team}/project/${
259-
repoInfo.project
260-
}/depot/${repo || repoInfo.repo}/git/merges/query`,
261-
{
262-
searchParams: {
263-
status,
264-
sort: `action_at`,
265-
page: 1,
266-
PageSize: 9999,
267-
sortDirection: `DESC`,
268-
access_token: this._session?.accessToken,
269-
},
255+
.get(`${url}/git/merges/query`, {
256+
searchParams: {
257+
status,
258+
sort: `action_at`,
259+
page: 1,
260+
PageSize: 9999,
261+
sortDirection: `DESC`,
262+
access_token: this._session?.accessToken,
270263
},
271-
)
264+
})
272265
.json();
273266
return result;
274267
} catch (err) {
@@ -328,19 +321,19 @@ export class CodingServer {
328321
public async getMRDetail(iid: string) {
329322
try {
330323
const url = this.getApiPrefix();
331-
const diff: IMRDetailResponse = await got
324+
const resp: IMRDetailResponse = await got
332325
.get(`${url}/git/merge/${iid}/detail`, {
333326
searchParams: {
334327
access_token: this._session?.accessToken,
335328
},
336329
})
337330
.json();
338331

339-
if (diff.code) {
340-
return Promise.reject(diff);
332+
if (resp.code) {
333+
return Promise.reject(resp);
341334
}
342335

343-
return diff;
336+
return resp;
344337
} catch (err) {
345338
return Promise.reject(err);
346339
}
@@ -563,6 +556,45 @@ export class CodingServer {
563556
}
564557
}
565558

559+
public async createMR(data: ICreateMRBody) {
560+
try {
561+
const url = this.getApiPrefix();
562+
const resp: ICreateMRResp = await got.post(`${url}/git/merge`, {
563+
resolveBodyOnly: true,
564+
responseType: `json`,
565+
searchParams: {
566+
access_token: this._session?.accessToken,
567+
},
568+
form: data,
569+
});
570+
if (resp.code) {
571+
return Promise.reject(resp);
572+
}
573+
return resp;
574+
} catch (err) {
575+
return Promise.reject(err);
576+
}
577+
}
578+
579+
public async getBranchList() {
580+
try {
581+
const url = this.getApiPrefix();
582+
const resp: IBranchListResp = await got
583+
.get(`${url}/git/list_branches`, {
584+
searchParams: {
585+
access_token: this._session?.accessToken,
586+
},
587+
})
588+
.json();
589+
if (resp.code) {
590+
return Promise.reject(resp);
591+
}
592+
return resp;
593+
} catch (err) {
594+
return Promise.reject(err);
595+
}
596+
}
597+
566598
get loggedIn() {
567599
return this._loggedIn;
568600
}

‎src/common/gitService.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,49 @@
11
import * as vscode from 'vscode';
22
import * as cp from 'child_process';
3-
import { promisify } from 'util';
43

5-
import { GitExtension } from 'src/typings/git';
6-
7-
const exec = promisify(cp.exec);
4+
import { Git, GitExtension } from 'src/typings/git';
85

96
export class GitService {
7+
static git: Git;
8+
9+
static async init() {
10+
const extension = vscode.extensions.getExtension(
11+
`vscode.git`,
12+
) as vscode.Extension<GitExtension>;
13+
if (extension !== undefined) {
14+
const gitExtension = extension.isActive ? extension.exports : await extension.activate();
15+
const model = gitExtension.getAPI(1);
16+
GitService.git = model.git;
17+
}
18+
}
19+
1020
static async getRemoteURLs(): Promise<string[] | null> {
1121
try {
12-
const extension = vscode.extensions.getExtension(
13-
`vscode.git`,
14-
) as vscode.Extension<GitExtension>;
15-
if (extension !== undefined) {
16-
const gitExtension = extension.isActive ? extension.exports : await extension.activate();
17-
const model = gitExtension.getAPI(1);
18-
19-
if (vscode.workspace.workspaceFolders?.length) {
20-
const tasks = vscode.workspace.workspaceFolders.map((f) =>
21-
exec(`${model.git.path} config --get remote.origin.url`, {
22-
cwd: f.uri.path,
23-
}),
24-
);
25-
const result = await Promise.all(tasks);
26-
const urls = result.map(({ stdout, stderr }) => {
27-
return stdout.trim();
28-
});
29-
30-
return urls;
31-
}
22+
if (!GitService.git || !vscode.workspace.workspaceFolders?.length) {
23+
return null;
3224
}
25+
26+
const tasks = vscode.workspace.workspaceFolders.map(
27+
(f) =>
28+
new Promise((resolve) => {
29+
cp.exec(
30+
`${GitService.git.path} -C "${f.uri.path}" config --get remote.origin.url`,
31+
{
32+
cwd: f.uri.path,
33+
},
34+
(err, stdout, stderr) => {
35+
resolve({ stdout, stderr });
36+
},
37+
);
38+
}),
39+
);
40+
41+
const result = await Promise.all(tasks);
42+
const urls = result.map((o) => {
43+
return (o as { stdout: string; stderr: string }).stdout?.trim();
44+
});
45+
46+
return urls;
3347
} catch (err) {
3448
console.error(err);
3549
}

‎src/extension.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import { Panel } from 'src/panel';
66
import { IFileNode, MRTreeDataProvider } from 'src/tree/mrTree';
77
import { ReleaseTreeDataProvider } from 'src/tree/releaseTree';
88
import { IRepoInfo, IMRWebViewDetail, ISessionData } from 'src/typings/commonTypes';
9+
import { GitService } from 'src/common/gitService';
910

1011
export async function activate(context: vscode.ExtensionContext) {
12+
await GitService.init();
1113
const repoInfo = await CodingServer.getRepoParams();
1214

1315
if (!repoInfo?.team) {
16+
vscode.commands.executeCommand('setContext', 'hasTeam', false);
1417
vscode.window.showInformationMessage(`Please open a repo hosted by coding.net.`);
1518
} else {
19+
vscode.commands.executeCommand('setContext', 'hasTeam', true);
1620
context.workspaceState.update(`repoInfo`, repoInfo);
1721
}
1822

@@ -32,7 +36,7 @@ export async function activate(context: vscode.ExtensionContext) {
3236

3337
const mrDataProvider = new MRTreeDataProvider(context, codingSrv);
3438
const releaseDataProvider = new ReleaseTreeDataProvider(context);
35-
vscode.window.createTreeView(`mrTreeView`, {
39+
constmrTree=vscode.window.createTreeView(`mrTreeView`, {
3640
treeDataProvider: mrDataProvider,
3741
showCollapseAll: true,
3842
});
@@ -89,6 +93,71 @@ export async function activate(context: vscode.ExtensionContext) {
8993
mrDataProvider.refresh();
9094
}),
9195
);
96+
context.subscriptions.push(
97+
vscode.commands.registerCommand('codingPlugin.newMrDesc', async () => {
98+
const doc = await vscode.workspace.openTextDocument({
99+
language: `markdown`,
100+
});
101+
await vscode.window.showTextDocument(doc);
102+
}),
103+
);
104+
context.subscriptions.push(
105+
vscode.commands.registerCommand('codingPlugin.createMr', async () => {
106+
const editor = vscode.window.activeTextEditor;
107+
if (!editor) {
108+
return;
109+
}
110+
111+
let content = editor.document.getText().trimStart();
112+
if (!content) {
113+
return;
114+
}
115+
116+
const firstLineBreak = content.indexOf(`\n`);
117+
const defaultTitle = content.slice(0, firstLineBreak).trim();
118+
119+
const { data } = await codingSrv.getBranchList();
120+
const list = data.map((i) => ({
121+
label: i.name,
122+
description: ``,
123+
}));
124+
125+
const src = await vscode.window.showQuickPick(list, {
126+
placeHolder: `Please choose source branch`,
127+
});
128+
if (!src) return;
129+
130+
const des = await vscode.window.showQuickPick(list, {
131+
placeHolder: `Please choose target branch`,
132+
});
133+
if (!des) return;
134+
135+
const title = await vscode.window.showInputBox({
136+
placeHolder: `By default it's the first line of this document.`,
137+
prompt: `Please input title for this merge request.`,
138+
value: defaultTitle,
139+
});
140+
if (!title) {
141+
return;
142+
}
143+
if (title === defaultTitle) {
144+
content = content.slice(firstLineBreak + 1).trimStart() || ``;
145+
}
146+
147+
try {
148+
const newMr = await codingSrv.createMR({
149+
content,
150+
title,
151+
srcBranch: src.label,
152+
desBranch: des.label,
153+
});
154+
vscode.window.showInformationMessage(
155+
`Merge request ${newMr.data.merge_request.title} was created successfully.`,
156+
);
157+
mrDataProvider.refresh();
158+
} catch (err) {}
159+
}),
160+
);
92161
context.subscriptions.push(
93162
vscode.commands.registerCommand('codingPlugin.switchRepo', async () => {
94163
try {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /