提交 621c3968 编写于 作者: M Michael Aigner 提交者: Tomas Vik

feat(gitclone): add wiki repo clone support for git clone command

上级 3af084dc
......@@ -173,6 +173,11 @@
{
"command": "gl.checkoutMrBranch",
"title": "Checkout MR branch"
},
{
"command": "gl.cloneWiki",
"title": "Clone Wiki",
"category": "GitLab"
}
],
"menus": {
......@@ -280,6 +285,10 @@
{
"command": "gl.refreshSidebar",
"when": "gitlab:validState"
},
{
"command": "gl.cloneWiki",
"when": "!gitlab:noToken"
}
],
"view/title": [
......
......@@ -24,6 +24,8 @@ module.exports = {
showErrorMessage: jest.fn(),
createStatusBarItem: jest.fn(),
withProgress: jest.fn(),
showQuickPick: jest.fn(),
createQuickPick: jest.fn(),
},
commands: {
executeCommand: jest.fn(),
......
......@@ -32,6 +32,7 @@ export const USER_COMMANDS = {
SUBMIT_COMMENT_EDIT: 'gl.submitCommentEdit',
CREATE_COMMENT: 'gl.createComment',
CHECKOUT_MR_BRANCH: 'gl.checkoutMrBranch',
CLONE_WIKI: 'gl.cloneWiki',
};
/*
......@@ -46,4 +47,5 @@ export const VS_COMMANDS = {
DIFF: 'vscode.diff',
OPEN: 'vscode.open',
GIT_SHOW_OUTPUT: 'git.showOutput',
GIT_CLONE: 'git.clone',
};
import * as vscode from 'vscode';
import { cloneWiki } from './clone_wiki';
import { tokenService } from '../services/token_service';
import { RemoteSource } from '../api/git';
import { GitLabRemoteSourceProvider } from '../gitlab/clone/gitlab_remote_source_provider';
import { showQuickPick } from '../utils/show_quickpick';
jest.mock('../services/token_service');
jest.mock('../gitlab/clone/gitlab_remote_source_provider');
jest.mock('../utils/show_quickpick');
describe('cloneWiki', () => {
const wikiRemoteSource = {
name: `$(repo) gitlab-org/gitlab-vscode-extension`,
description: 'description',
url: [
'git@gitlab.com:gitlab-org/gitlab-vscode-extension.wiki.git',
'https://gitlab.com/gitlab-org/gitlab-vscode-extension.wiki.git',
],
};
let wikiRemoteSources: RemoteSource[];
let instanceUrls: string[];
const alwaysPickFirstOption = () => {
(vscode.window.showQuickPick as jest.Mock).mockImplementation(([option]) => option);
(showQuickPick as jest.Mock).mockImplementation(picker => picker.items[0]);
};
beforeEach(() => {
tokenService.getInstanceUrls = () => instanceUrls;
(GitLabRemoteSourceProvider as jest.Mock).mockImplementation(() => ({
getRemoteWikiSources: () => wikiRemoteSources,
}));
wikiRemoteSources = [wikiRemoteSource];
(vscode.window.createQuickPick as jest.Mock).mockImplementation(() => {
const picker = {
onDidChangeValue: jest.fn(),
items: [],
};
return picker;
});
});
it('skips selection of instance if there is only one', async () => {
instanceUrls = ['https://gitlab.com'];
alwaysPickFirstOption();
await cloneWiki();
expect(vscode.window.showQuickPick).toHaveBeenCalledTimes(1);
expect(vscode.window.createQuickPick).toHaveBeenCalledTimes(1);
});
it('asks for instance if there are multiple', async () => {
instanceUrls = ['https://gitlab.com', 'https://example.com'];
alwaysPickFirstOption();
await cloneWiki();
expect(vscode.window.showQuickPick).toHaveBeenCalledTimes(2);
expect(vscode.window.createQuickPick).toHaveBeenCalledTimes(1);
});
it('calls git.clone command with selected URL', async () => {
instanceUrls = ['https://gitlab.com'];
alwaysPickFirstOption();
await cloneWiki();
expect(vscode.commands.executeCommand).toBeCalledWith(
'git.clone',
'git@gitlab.com:gitlab-org/gitlab-vscode-extension.wiki.git',
);
});
});
import * as vscode from 'vscode';
import { tokenService } from '../services/token_service';
import { GitLabRemoteSourceProvider } from '../gitlab/clone/gitlab_remote_source_provider';
import { VS_COMMANDS } from '../command_names';
import { showQuickPick } from '../utils/show_quickpick';
interface RemoteSourceItem {
label: string;
url: string[];
description: string;
}
async function pickRemoteProvider(): Promise<GitLabRemoteSourceProvider | undefined> {
const instanceUrls = tokenService.getInstanceUrls();
const instanceItems = instanceUrls.map(u => ({
label: `$(project) ${u}`,
instance: u,
}));
if (instanceItems.length === 0) {
throw new Error('no GitLab instance found');
}
let selectedInstanceUrl;
if (instanceItems.length === 1) {
[selectedInstanceUrl] = instanceItems;
} else {
selectedInstanceUrl = await vscode.window.showQuickPick(instanceItems, {
ignoreFocusOut: true,
placeHolder: 'Select GitLab instance',
});
}
if (!selectedInstanceUrl) {
return undefined;
}
return new GitLabRemoteSourceProvider(selectedInstanceUrl.instance);
}
async function pickRemoteWikiSource(
provider: GitLabRemoteSourceProvider,
): Promise<RemoteSourceItem | undefined> {
const wikiPick = vscode.window.createQuickPick<RemoteSourceItem>();
wikiPick.ignoreFocusOut = true;
wikiPick.placeholder = 'Select GitLab project';
const getSourceItemsForQuery = async (query?: string) => {
const sources = await provider.getRemoteWikiSources(query);
return sources.map(s => ({
label: s.name,
url: s.url as string[],
description: s.description || '',
}));
};
wikiPick.onDidChangeValue(async value => {
wikiPick.items = await getSourceItemsForQuery(value);
});
wikiPick.items = await getSourceItemsForQuery();
const selectedSource = await showQuickPick(wikiPick);
return selectedSource;
}
export async function cloneWiki(): Promise<void> {
const provider = await pickRemoteProvider();
if (!provider) {
return;
}
const selectedSource = await pickRemoteWikiSource(provider);
if (!selectedSource) {
return;
}
const selectedUrl = await vscode.window.showQuickPick(selectedSource.url, {
ignoreFocusOut: true,
placeHolder: 'Select URL to clone from',
});
if (!selectedUrl) {
return;
}
await vscode.commands.executeCommand(VS_COMMANDS.GIT_CLONE, selectedUrl);
}
......@@ -31,6 +31,7 @@ const { hasCommentsDecorationProvider } = require('./review/has_comments_decorat
const { changeTypeDecorationProvider } = require('./review/change_type_decoration_provider');
const { checkVersion } = require('./utils/check_version');
const { checkoutMrBranch } = require('./commands/checkout_mr_branch');
const { cloneWiki } = require('./commands/clone_wiki');
vscode.gitLabWorkflow = {
sidebarDataProviders: [],
......@@ -90,6 +91,7 @@ const registerCommands = (context, outputChannel) => {
[USER_COMMANDS.SUBMIT_COMMENT_EDIT]: submitEdit,
[USER_COMMANDS.CREATE_COMMENT]: createComment,
[USER_COMMANDS.CHECKOUT_MR_BRANCH]: checkoutMrBranch,
[USER_COMMANDS.CLONE_WIKI]: cloneWiki,
[PROGRAMMATIC_COMMANDS.NO_IMAGE_REVIEW]: () =>
vscode.window.showInformationMessage("GitLab MR review doesn't support images yet."),
};
......
import { convertUrlToWikiUrl } from './gitlab_remote_source_provider';
describe('convertUrlToWikiUrl', () => {
test('should convert urls to wiki urls', () => {
expect(convertUrlToWikiUrl('git@gitlab.com:username/myproject.git')).toBe(
'git@gitlab.com:username/myproject.wiki.git',
);
expect(convertUrlToWikiUrl('https://gitlab.com/username/myproject.git')).toBe(
'https://gitlab.com/username/myproject.wiki.git',
);
expect(convertUrlToWikiUrl('https://gitlab.com/user.git./myproject.git')).toBe(
'https://gitlab.com/user.git./myproject.wiki.git',
);
expect(convertUrlToWikiUrl('https://gitlab.com/user.git./myproject')).toBe(
'https://gitlab.com/user.git./myproject',
);
expect(convertUrlToWikiUrl('wrong')).toBe('wrong');
});
});
......@@ -2,6 +2,15 @@ import { RemoteSource, RemoteSourceProvider } from '../../api/git';
import { GitLabNewService } from '../gitlab_new_service';
const SEARCH_LIMIT = 30;
const getProjectQueryAttributes = {
membership: true,
limit: SEARCH_LIMIT,
searchNamespaces: true,
};
export function convertUrlToWikiUrl(url: string): string {
return url.replace(/\.git$/, '.wiki.git');
}
export class GitLabRemoteSourceProvider implements RemoteSourceProvider {
name: string;
......@@ -17,18 +26,35 @@ export class GitLabRemoteSourceProvider implements RemoteSourceProvider {
this.gitlabService = new GitLabNewService(this.url);
}
async getRemoteSources(query?: string): Promise<RemoteSource[] | undefined> {
async getRemoteSources(query?: string): Promise<RemoteSource[]> {
const projects = await this.gitlabService.getProjects({
search: query,
membership: true,
limit: SEARCH_LIMIT,
searchNamespaces: true,
...getProjectQueryAttributes,
});
return projects?.map(project => ({
return projects.map(project => ({
name: `$(repo) ${project.fullPath}`,
description: project.description,
url: [project.sshUrlToRepo, project.httpUrlToRepo],
}));
}
async getRemoteWikiSources(query?: string): Promise<RemoteSource[]> {
const projects = await this.gitlabService.getProjects({
search: query,
...getProjectQueryAttributes,
});
const wikiprojects = projects.filter(project => project.wikiEnabled);
return wikiprojects.map(project => {
return {
name: `$(repo) ${project.fullPath}`,
description: project.description,
url: [
convertUrlToWikiUrl(project.sshUrlToRepo),
convertUrlToWikiUrl(project.httpUrlToRepo),
],
};
});
}
}
......@@ -39,4 +39,8 @@ export class GitLabProject {
get groupRestId(): number | undefined {
return this.gqlProject.group && getRestIdFromGraphQLId(this.gqlProject.group.id);
}
get wikiEnabled(): boolean {
return this.gqlProject.wikiEnabled;
}
}
......@@ -9,6 +9,7 @@ export const fragmentProjectDetails = gql`
sshUrlToRepo
fullPath
webUrl
wikiEnabled
group {
id
}
......@@ -88,6 +89,7 @@ export interface GqlProject {
fullPath: string;
webUrl: string;
group?: GqlGroup;
wikiEnabled: boolean;
}
export interface GqlProjectResult<T> {
......
......@@ -91,6 +91,7 @@ export const gqlProject: GqlProject = {
group: {
id: 'gid://gitlab/Group/9970',
},
wikiEnabled: false,
};
export const project = new GitLabProject(gqlProject);
import { QuickPick, QuickPickItem } from 'vscode';
export async function showQuickPick<T extends QuickPickItem>(
quickpick: QuickPick<T>,
): Promise<T | undefined> {
const result = await new Promise<T | undefined>(res => {
quickpick.onDidHide(() => res(undefined));
quickpick.onDidAccept(() => {
res(quickpick.selectedItems[0]);
quickpick.hide();
});
quickpick.show();
});
return result;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册