未验证 提交 28c9cca5 编写于 作者: F Fatih Acet

Merge branch 'alpcanaydin/gitlab-vscode-extension-master'

# Conflicts:
#	src/extension.js
#	src/gitlab_service.js
#	src/status_bar.js
#	src/token_input.js
{
"env": {
"browser": false,
"commonjs": true,
"es6": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"rules": {
"no-const-assign": "warn",
"no-this-before-super": "warn",
"no-undef": "warn",
"no-unreachable": "warn",
"no-unused-vars": "warn",
"constructor-super": "warn",
"valid-typeof": "warn"
}
}
\ No newline at end of file
"extends": ["airbnb-base", "prettier"],
"plugins": ["import", "prettier"],
"rules": {
"no-console": "off",
"no-underscore-dangle": ["error", { "allow": ["_setGLToken"] }],
"import/no-unresolved": [2, { "ignore": ["vscode"] }],
"prettier/prettier": ["error"]
}
}
package-lock.json
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
}
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint"
]
}
\ No newline at end of file
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the extensions.json format
"recommendations": ["dbaeumer.vscode-eslint"]
}
// A launch configuration that launches the extension inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/test" ],
"stopOnEntry": false
}
]
}
\ No newline at end of file
"version": "0.1.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}"],
"stopOnEntry": false
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}",
"--extensionTestsPath=${workspaceRoot}/test"
],
"stopOnEntry": false
}
]
}
// Place your settings in this file to overwrite default and user settings.
{
}
\ No newline at end of file
"editor.formatOnSave": true,
"javascript.validate.enable": false,
"eslint.enable": true
}
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": [
"es6"
]
},
"exclude": [
"node_modules"
]
}
\ No newline at end of file
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"lib": ["es6"]
},
"exclude": ["node_modules"]
}
此差异已折叠。
......@@ -11,19 +11,9 @@
"engines": {
"vscode": "^1.19.0"
},
"categories": [
"Other"
],
"keywords": [
"git",
"gitlab",
"merge request",
"pipeline",
"ci cd"
],
"activationEvents": [
"*"
],
"categories": ["Other"],
"keywords": ["git", "gitlab", "merge request", "pipeline", "ci cd"],
"activationEvents": ["*"],
"bugs": {
"url": "https://gitlab.com/fatihacet/gitlab-vscode-extension/issues",
"email": "acetfatih@gmail.com"
......@@ -121,12 +111,18 @@
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "node ./node_modules/vscode/bin/test",
"eslint": "eslint --max-warnings 0 --ext .js .",
"format": "prettier --write '**/*.{js,json}'",
"publish": "vsce publish"
},
"devDependencies": {
"eslint": "^4.16.0",
"typescript": "^2.6.1",
"@types/mocha": "^2.2.42"
"@types/mocha": "^2.2.42",
"eslint": "^4.18.1",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-prettier": "^2.6.0",
"prettier": "^1.11.0",
"typescript": "^2.6.1"
},
"dependencies": {
"execa": "^0.9.0",
......
......@@ -7,14 +7,16 @@ async function validate() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return showInformationMessage('GitLab Workflow: No open file.');
showInformationMessage('GitLab Workflow: No open file.');
return;
}
const content = editor.document.getText();
const response = await gitLabService.validateCIConfig(content);
if (!response) {
return showInformationMessage('GitLab Workflow: Failed to validate CI configuration.');
showInformationMessage('GitLab Workflow: Failed to validate CI configuration.');
return;
}
const { status, errors, error } = response;
......
......@@ -2,22 +2,22 @@ const vscode = require('vscode');
const execa = require('execa');
const url = require('url');
const getWorkspaceRootPath = () => {
return vscode.workspace.workspaceFolders[0].uri.fsPath;
}
const getWorkspaceRootPath = () => vscode.workspace.workspaceFolders[0].uri.fsPath;
async function fetch(cmd) {
const [git, ...args] = cmd.split(' ');
return await execa.stdout(git, args, {
const output = await execa.stdout(git, args, {
cwd: getWorkspaceRootPath(),
});
return output;
}
async function fetchBranchName() {
const cmd = 'git rev-parse --abbrev-ref HEAD';
const output = await fetch(cmd);
return await fetch(cmd);
return output;
}
/**
......@@ -26,7 +26,7 @@ async function fetchBranchName() {
*
* Fixes #1 where local branch name is renamed and doesn't exists on remote but
* local branch still tracks another branch on remote.
*/
*/
async function fetchTrackingBranchName() {
const branchName = await fetchBranchName();
......@@ -38,7 +38,9 @@ async function fetchTrackingBranchName() {
return ref.replace('refs/heads/', '');
}
} catch (e) {
console.log(`Couldn't find tracking branch. Extension will fallback to branch name ${branchName}`);
console.log(
`Couldn't find tracking branch. Extension will fallback to branch name ${branchName}`,
);
}
return branchName;
......@@ -46,28 +48,56 @@ async function fetchTrackingBranchName() {
async function fetchLastCommitId() {
const cmd = 'git log --format=%H -n 1';
const output = await fetch(cmd);
return await fetch(cmd);
return output;
}
const parseGitRemote = remote => {
if (remote.startsWith('git@') || remote.startsWith('git://')) {
const match = new RegExp('^git(?:@|://)([^:/]+)(?::|:/|/)(.+)/(.+?)(?:.git)?$', 'i').exec(
remote,
);
if (!match) {
return null;
}
return ['git:', ...match.slice(1, 4)];
}
const { protocol = 'https:', hostname, pathname } = url.parse(remote);
if (!hostname || !pathname) {
return null;
}
const match = pathname.match(/\/(.+)\/(.*?)(?:.git)?$/);
if (!match) {
return null;
}
return [protocol, hostname, ...match.slice(1, 3)];
};
async function fetchGitRemote() {
let url = null;
let remoteUrl = null;
try {
const branchName = await fetchBranchName();
const remoteName = await fetch(`git config --get branch.${branchName}.remote`);
url = await fetch(`git ls-remote --get-url ${remoteName}`);
} catch(e) {
remoteUrl = await fetch(`git ls-remote --get-url ${remoteName}`);
} catch (err) {
try {
url = await fetch('git ls-remote --get-url');
remoteUrl = await fetch('git ls-remote --get-url');
} catch (e) {
const remote = await fetch('git remote');
url = await fetch(`git ls-remote --get-url ${remote}`);
remoteUrl = await fetch(`git ls-remote --get-url ${remote}`);
}
}
if (url) {
if (remoteUrl) {
const [schema, domain, namespace, project] = parseGitRemote(url);
return { schema, domain, namespace, project };
......@@ -76,31 +106,6 @@ async function fetchGitRemote() {
return null;
}
const parseGitRemote = (remote) => {
if (remote.startsWith('git@') || remote.startsWith('git://')) {
const match = new RegExp('^git(?:@|://)([^:/]+)(?::|:/|/)(.+)/(.+?)(?:.git)?$', 'i').exec(remote);
if (!match) {
return null;
}
return ['git:', ...match.slice(1, 4)];
} else {
const { protocol = 'https:', hostname, pathname } = url.parse(remote);
if (!hostname || !pathname) {
return null;
}
const match = pathname.match(/\/(.+)\/(.*?)(?:.git)?$/);
if (!match) {
return null;
}
return [protocol, hostname, ...match.slice(1, 3)];
}
}
exports.fetchBranchName = fetchBranchName;
exports.fetchTrackingBranchName = fetchTrackingBranchName;
exports.fetchLastCommitId = fetchLastCommitId;
......
......@@ -25,12 +25,16 @@ async function openLink(link) {
if (project) {
opn(link.replace('$userId', user.id).replace('$projectUrl', project.web_url));
} else {
vscode.window.showInformationMessage('GitLab Workflow: Failed to open file on web. No GitLab project.');
vscode.window.showInformationMessage(
'GitLab Workflow: Failed to open file on web. No GitLab project.',
);
}
} else {
vscode.window.showInformationMessage('GitLab Workflow: GitLab user not found. Check your Personal Access Token.');
vscode.window.showInformationMessage(
'GitLab Workflow: GitLab user not found. Check your Personal Access Token.',
);
}
};
}
async function showIssues() {
openLink('$projectUrl/issues?assignee_id=$userId');
......@@ -38,7 +42,7 @@ async function showIssues() {
async function showMergeRequests() {
openLink('$projectUrl/merge_requests?assignee_id=$userId');
};
}
async function openActiveFile() {
const editor = vscode.window.activeTextEditor;
......@@ -50,7 +54,7 @@ async function openActiveFile() {
const branchName = await gitService.fetchTrackingBranchName();
const workspaceFolder = vscode.workspace.getWorkspaceFolder(editor.document.uri);
const filePath = editor.document.uri.path.replace(`${workspaceFolder.uri.path}/`, '');
let fileUrl = `${currentProject.web_url}/blob/${branchName}/${filePath}`;
const fileUrl = `${currentProject.web_url}/blob/${branchName}/${filePath}`;
let anchor = '';
if (editor.selection) {
......@@ -64,7 +68,9 @@ async function openActiveFile() {
opn(`${fileUrl}${anchor}`);
} else {
vscode.window.showInformationMessage('GitLab Workflow: Failed to open file on web. No GitLab project.');
vscode.window.showInformationMessage(
'GitLab Workflow: Failed to open file on web. No GitLab project.',
);
}
} else {
vscode.window.showInformationMessage('GitLab Workflow: No open file.');
......@@ -81,7 +87,7 @@ async function openCurrentMergeRequest() {
async function openCreateNewIssue() {
openLink('$projectUrl/issues/new');
};
}
async function openCreateNewMr() {
const project = await gitLabService.fetchCurrentProject();
......@@ -91,9 +97,11 @@ async function openCreateNewMr() {
opn(`${project.web_url}/merge_requests/new?merge_request%5Bsource_branch%5D=${branchName}`);
} else {
vscode.window.showInformationMessage('GitLab Workflow: Failed to open file on web. No GitLab project.');
vscode.window.showInformationMessage(
'GitLab Workflow: Failed to open file on web. No GitLab project.',
);
}
};
}
async function openProjectPage() {
openLink('$projectUrl');
......@@ -118,8 +126,7 @@ async function compareCurrentBranch() {
try {
project = await gitLabService.fetchCurrentProject();
lastCommitId = await gitService.fetchLastCommitId();
}
catch(e) {
} catch (e) {
console.log('Failed to run compareCurrentBranch command', e);
}
......
......@@ -20,13 +20,14 @@ async function showPicker() {
label: 'Cancel last pipeline',
action: 'cancel',
},
]
];
const selected = await vscode.window.showQuickPick(items);
if (selected) {
if (selected.action === 'view') {
return openers.openCurrentPipeline();
openers.openCurrentPipeline();
return;
}
gitLabService.handlePipelineAction(selected.action);
......
......@@ -2,42 +2,20 @@ const vscode = require('vscode');
const opn = require('opn');
const gitLabService = require('./gitlab_service');
async function showIssueSearchInput() {
showSearchInputFor('issues');
}
async function showMergeRequestSearchInput() {
showSearchInputFor('merge_requests');
}
async function showSearchInputFor(noteableType) {
const query = await vscode.window.showInputBox({
ignoreFocusOut: true,
placeHolder: 'Search in title or description. (Check project page for advanced usage)',
});
const queryString = await parseQuery(query, noteableType);
const project = await gitLabService.fetchCurrentProject();
if (project) {
opn(`${project.web_url}/${noteableType}${queryString}`);
} else {
vscode.window.showErrorMessage('GitLab Workflow: No project found to search issues');
}
}
const parseQuery = (query, noteableType) => {
const params = {};
const tokens = query.replace(/: /g, ':') // Normalize spaces after tokens.
const tokens = query
.replace(/: /g, ':') // Normalize spaces after tokens.
.replace(/\s[a-z]*:/gi, t => `\n${t}`) // Get tokens and add new line.
.split('\n') // Create array from tokens.
.map(t => t.trim().split(':')) // Return new array with token and value arrays.
.map(t => t.trim().split(':')); // Return new array with token and value arrays.
// If there is no token it's a basic text search.
if (tokens.length === 1 && tokens[0][1] === undefined) {
// eslint-disable-next-line prefer-destructuring
params.search = tokens[0][0];
} else {
tokens.forEach((t) => {
tokens.forEach(t => {
const [token, value] = t;
switch (token) {
......@@ -86,7 +64,8 @@ const parseQuery = (query, noteableType) => {
if (value === 'me') {
params.scope = 'assigned-to-me';
} else {
const key = noteableType === 'merge_requests' ? 'assignee_username' : 'assignee_username[]';
const key =
noteableType === 'merge_requests' ? 'assignee_username' : 'assignee_username[]';
params[key] = value;
}
break;
......@@ -100,11 +79,35 @@ const parseQuery = (query, noteableType) => {
}
// URL encode keys and values and return a new array to build actual query string.
const queryParams = Object.keys(params).map(k =>
params[k] ? `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}` : ''
const queryParams = Object.keys(params).map(
k => (params[k] ? `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}` : ''),
);
return queryParams.length ? `?${queryParams.join('&')}` : '';
};
async function showSearchInputFor(noteableType) {
const query = await vscode.window.showInputBox({
ignoreFocusOut: true,
placeHolder: 'Search in title or description. (Check project page for advanced usage)',
});
const queryString = await parseQuery(query, noteableType);
const project = await gitLabService.fetchCurrentProject();
if (project) {
opn(`${project.web_url}/${noteableType}${queryString}`);
} else {
vscode.window.showErrorMessage('GitLab Workflow: No project found to search issues');
}
}
async function showIssueSearchInput() {
showSearchInputFor('issues');
}
async function showMergeRequestSearchInput() {
showSearchInputFor('merge_requests');
}
exports.showIssueSearchInput = showIssueSearchInput;
......
......@@ -25,8 +25,8 @@ const contextOptions = [
{
label: 'Snippet from selection',
type: 'selection',
}
]
},
];
async function createSnippet(project, editor, visibility, context) {
let content = '';
......
......@@ -10,15 +10,14 @@ const assert = require('assert');
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
const vscode = require('vscode');
const myExtension = require('../extension');
// const vscode = require('vscode');
// const myExtension = require('../extension');
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", function() {
// Defines a Mocha unit test
test("Something 1", function() {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});
\ No newline at end of file
suite('Extension Tests', () => {
// Defines a Mocha unit test
test('Something 1', () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});
......@@ -15,8 +15,8 @@ const testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.js (suite, test, etc.)
useColors: true // colored output from test results
ui: 'tdd', // the TDD UI is being used in extension.test.js (suite, test, etc.)
useColors: true, // colored output from test results
});
module.exports = testRunner;
\ No newline at end of file
module.exports = testRunner;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册