From: Stepan Riha Date: Fri, 23 Oct 2015 22:06:09 +0000 (-0500) Subject: Conversion to promises and general cleanup. X-Git-Url: http://git.marmaro.de/?a=commitdiff_plain;h=888bbeb227287f0ce532bc3ace102d69624c5d44;p=mantis2gitlab Conversion to promises and general cleanup. --- diff --git a/README.md b/README.md index 8ce9577..4b867b2 100755 --- a/README.md +++ b/README.md @@ -1,55 +1,207 @@ -# YouTrack2GitLab -Import YouTrack issues into GitLab. +# Mantis2GitLab + +Import Mantis issues into GitLab. ## Install ``` -npm install -g youtrack2gitlab +npm install -g mantis2gitlab ``` ## Usage ``` -yt2gl -i -u -g -p -t +m2gl -i options ``` ## Options ``` --i, --input - CSV file exported from YouTrack (Example: issues.csv) + -i, --input CSV file exported from Mantis (Example: issues.csv) [required] + -c, --config Configuration file (Example: config.json) [required] + -g, --gitlaburl GitLab URL hostname (Example: https://gitlab.com) [required] + -p, --project GitLab project name including namespace (Example: mycorp/myproj) [required] + -t, --token An admin user's private token (Example: a2r33oczFyQzq53t23Vj) [required] + -s, --sudo The username performing the import (Example: bob) [required] + -f, --from The first issue # to import (Example: 123) +``` + +## Config File + +In order to correctly map Mantis attributes you should create a JSON file and specify it with the **-c** switch. + +### Users + +This section maps Mantis `username` (Reporter, Assigned To, etc.) to a corresponding GitLab user name. + +``` +{ + "users": { + "mantisUserName1": { + "gl_username": "GitLabUserName1" + }, + "mantisUserName2": { + "gl_username": "GitLabUserName2" + } + } +} +``` + +### Mantis URL (optional) + +This setting defines the URL to the old mantis installation. When specified, Mantis cases imported in GitLab +will contain a back-link to their corresponding Mantis issue. + +``` +{ + "mantisUrl": "https://www.oldserver.com/mantis" +} +``` + +### Category Labels (optional) + +This section maps Mantis Categories to corresponding GitLab labels. + +``` +{ + "category_labels": { + "Admin UI": "area:Admin", + "Voter UI": "area:Voter", + "Server": "area:Service" + } +} +``` + +### Priority Labels (optional) + +This section maps Mantis Priorities to corresponding GitLab labels. +Note that the numeric priorities are used when exporting from SQL. + +``` +{ + "priority_labels": { + "20": "priority:low", + "low": "priority:low", + "40": "priority:high", + "high": "priority:high", + "50": "priority:urgent", + "urgent": "priority:urgent", + "60": "priority:immediate", + "immediate": "priority:immediate" + } +} +``` --u, --users - User mapping file (Example: users.json) +### Severity Labels (optional) --g, --gitlaburl - GitLab URL hostname (Example: gitlab.example.com) +This section maps Mantis Severities to corresponding GitLab labels. +Note that the numeric severities are used when exporting from SQL. --p, --project - GitLab project name including namespace (Example: mycorp/myproj) +``` +{ + "severity_labels": { + "10": "severity:feature", + "feature": "severity:feature", + "20": "severity:trivial", + "trivial": "severity:trivial", + "30": "severity:text", + "text": "severity:text", + "40": "severity:tweak", + "tweak": "severity:tweak", + "50": "severity:minor", + "minor": "severity:minor", + "60": "severity:major", + "major": "severity:major", + "70": "severity:crash", + "crash": "severity:crash", + "80": "severity:block", + "block": "severity:block" + } +} +``` + +### Closed Statuses (optional) --t, --token - An admin user's private token (Example: a2r33oczFyQzq53t23Vj) +This section maps which Mantis Statuses indicate that the issue is closed. +Note that the numeric severities are used when exporting from SQL. + +``` +{ + "closed_statuses": { + "80": true, + "resolved": true, + "90": true, + "closed": true + } +} ``` -## User Mapping File -In order to correctly map users you should create a JSON file with the following format and specify it with the **-u** switch: +## Exporting From Mantis + +The input to this script is a CSV file with the following columns: + + * `Id` - Will create a corresponding GitLab *Issue* + * `Summary` - Will create a corresponding GitLab *Title* + * `Category` - Will create a corresponding GitLab *Label* from `config.category_labels[Category]` + * `Priority` - Will create a corresponding GitLab *Label* from `config.priority_labels[Priority]` + * `Severity` - Will create a corresponding GitLab *Label* from `config.severity_labels[Severity]` + * `Created` - Will be included in the *Description* header + * `Updated` - Will be included in the *Description* header, if different from `Created` + * `Reporter` - Will be included in the *Description* header + * `Assigned To` - Will be included in the *Description* header + * `Description` - Will be included in the *Description* + * `Info` - Will be appended the *Description* + * `Notes` - Will be split on `"$$$$"` and appended the *Description* + +### Exporting from Mantis UI + +You can export a summary of the Mantis issues from the _View Issues_ page by clicking on the _Export CSV_ button. + +**Note:** This export will only include a subset of the issues and is not the recommended approach. + +### Exporting from database + +The following SQL query pulls all the supported columns from the Mantis database. Make sure you specify the correct `PROJECT_NAME`: ``` -[ - { - "yt_username": "USER'S USERNAME IN YOUTRACK", - "yt_name": "USER'S NAME IN YOUTRACK", - "gl_username": "USER'S USERNAME IN GITLAB", - "gl_private_token": "USER'S PRIVATE TOKEN IN GITLAB" - }, - … -] +SELECT + bug.id as Id, + project.name as Project, + bug.category as Category, + bug.summary as Summary, + bug.priority as Priority, + bug.severity as Severity, + bug.status as Status, + bug.date_submitted as Created, + bug.last_updated as Updated, + reporter.username as Reporter, + handler.username as "Assigned To", + bug_text.description as Description, + bug_text.additional_information as Info, + GROUP_CONCAT( + CONCAt('*', bugnote.date_submitted, ' - ', note_reporter.username, '* + +', bugnote_text.note) + ORDER BY bugnote.Id + SEPARATOR '$$$$' + ) as Notes +FROM + mantis_bug_table as bug + JOIN mantis_project_table project ON bug.project_id = project.id + JOIN mantis_bug_text_table bug_text ON bug.bug_text_id = bug_text.id + JOIN mantis_user_table as reporter ON bug.reporter_id = reporter.id + LEFT OUTER JOIN mantis_user_table as handler ON bug.handler_id = handler.id + LEFT OUTER JOIN mantis_bugnote_table as bugnote ON bugnote.bug_id = bug.id + LEFT OUTER JOIN mantis_bugnote_text_table as bugnote_text ON bugnote.bugnote_text_id = bugnote_text.id + LEFT OUTER JOIN mantis_user_table as note_reporter ON bugnote.reporter_id = note_reporter.id +WHERE + project.name = 'PROJECT_NAME' +GROUP BY bug.id +ORDER BY bug.id ``` ## Notes - Make sure the input CSV file only includes issues for the project you want to import. -- Make sure that all users have write access to the specified repository or some issues will fail to import. A safer approach is to set repository's **Visibility Level** to **Public** and revert it when the import process is complete. - In version 6.4.3, GitLab API does not support setting creation date of issues. So all imported issues will have a creation time of now. - In version 6.4.3, GitLab API fails to import issues with very long titles. - In version 6.4.3, GitLab does not allow issues to be deleted. So be careful when importing issues into an active project. @@ -60,14 +212,15 @@ In order to correctly map users you should create a JSON file with the following + Initial release ## Author -**Soheil Rashidi** +**Stepan Riha** -+ http://soheilrashidi.com -+ http://twitter.com/soheilpro -+ http://github.com/soheilpro ++ http://github.com/nonplus ## Copyright and License -Copyright 2014 Soheil Rashidi + +Based on https://github.com/soheilpro/youtrack2gitlab + +Copyright 2015 Stepan Riha Licensed under the The MIT License (the "License"); you may not use this work except in compliance with the License. diff --git a/m2gl.js b/m2gl.js index 042fa7e..67c0a3b 100755 --- a/m2gl.js +++ b/m2gl.js @@ -1,10 +1,11 @@ #!/usr/bin/env node -var fs = require('fs'); +var Q = require('q'); +var FS = require('q-io/fs'); var util = require('util'); var colors = require('colors'); var csv = require('csv'); -var rest = require('restler'); +var rest = require('restler-q'); var async = require('async'); var _ = require('lodash'); var argv = require('optimist') @@ -15,215 +16,326 @@ var argv = require('optimist') .alias('p', 'project') .alias('t', 'token') .alias('s', 'sudo') + .alias('f', 'from') .describe('i', 'CSV file exported from Mantis (Example: issues.csv)') .describe('c', 'Configuration file (Example: config.json)') .describe('g', 'GitLab URL hostname (Example: https://gitlab.com)') .describe('p', 'GitLab project name including namespace (Example: mycorp/myproj)') .describe('t', 'An admin user\'s private token (Example: a2r33oczFyQzq53t23Vj)') .describe('s', 'The username performing the import (Example: bob)') + .describe('f', 'The first issue # to import (Example: 123)') .argv; var inputFile = __dirname + '/' + argv.input; var configFile = __dirname + '/' + argv.config; +var fromIssueId = Number(argv.from||0); var gitlabAPIURLBase = argv.gitlaburl + '/api/v3'; var gitlabProjectName = argv.project; var gitlabAdminPrivateToken = argv.token; var gitlabSudo = argv.sudo; var config = {}; -getGitLabProject(gitlabProjectName, gitlabAdminPrivateToken, function(error, project) { - if (error) { - console.error('Error: Cannot get list of projects from gitlab: ' + gitlabAPIURLBase); - return; - } - - if (!project) { - console.error('Error: Cannot find GitLab project: ' + gitlabProjectName); - return; - } - - getGitLabUsers(gitlabAdminPrivateToken, function(error, gitlabUsers) { - if (error) { - console.error('Error: Cannot get list of users from gitlab: ' + gitlabAPIURLBase); - return; - } - - getConfig(configFile, function(error, cfg) { - if (error) { - console.error('Error: Cannot read config file: ' + configFile); - return; - } - - config = cfg; - - var users = config.users; - - setGitLabUserIds(users, gitlabUsers); +var gitLab = {}; +var promise = getConfig() + .then(readMantisIssues) + .then(getGitLabProject) + .then(getGitLabProjectMembers) + .then(mapGitLabUserIds) + .then(validateMantisIssues) + .then(getGitLabProjectIssues) + .then(importGitLabIssues) + ; + +promise.then(function() { + console.log(("Done!").bold.green); +}, function(err) { + console.error(err); +}); + +/** + * Read and parse config.json file - assigns config + */ +function getConfig() { + log_progress("Reading configuration..."); + return FS.read(configFile, {encoding: 'utf8'}) + .then(function(data) { + var config = JSON.parse(data); + config.users = _.extend({ + "": { + name: "Unknown", + gl_username: gitlabSudo + } + }, config.users); + return config; + }).then(function(cfg) { + config = cfg; + }, function() { + throw new Error('Cannot read config file: ' + configFile); + }); +} - readRows(inputFile, function(error, rows) { - if (error) { - console.error('Error: Cannot read input file: ' + inputFile); - return; - } +/** + * Read and parse import.csv file - assigns gitLab.mantisIssues + */ +function readMantisIssues() { + log_progress("Reading Mantis export file..."); + return FS.read(inputFile, {encoding: 'utf8'}).then(function(data) { + var rows = []; + var dfd = Q.defer(); - validate(rows, users, function(missingUsernames, missingNames) { - if (missingUsernames.length > 0 || missingNames.length > 0) { - for (var i = 0; i < missingUsernames.length; i++) - console.error('Error: Cannot map Mantis user with username: ' + missingUsernames[i]); + csv().from(data, {delimiter: ',', escape: '"', columns: true}) + .on('record', function(row, index) { rows.push(row) }) + .on('end', function(error, data) { + dfd.resolve(rows); + }); - for (var i = 0; i < missingNames.length; i++) - console.error('Error: Cannot map Mantis user with name: ' + missingNames[i]); + return dfd.promise + .then(function(rows) { + _.forEach(rows, function(row) { + row.Id = Number(row.Id); + }); - return; + if(fromIssueId) { + rows = _.filter(rows, function(row) { + return row.Id >= fromIssueId; + }) } - rows = _.sortBy(rows, function(row) { return Date.parse(row.Created); }); - - async.eachSeries(rows, function(row, callback) { - var issueId = row.Id; - var title = row.Summary; - var description = getDescription(row); - var assignee = getUserByMantisUsername(users, row["Assigned To"]); - var milestoneId = ''; - var labels = getLabels(row); - var author = getUserByMantisUsername(users, row.Reporter); - - insertIssue(project.id, title, description, assignee && assignee.gl_id, milestoneId, labels, author.gl_username, gitlabAdminPrivateToken, function(error, issue) { - setTimeout(callback, 1000); - - if (error) { - console.error((issueId + ': Failed to insert.').red, error); - return; - } - - if (isClosed(row)) { - closeIssue(issue, assignee.gl_private_token || gitlabAdminPrivateToken, function(error) { - if (error) - console.warn((issueId + ': Inserted successfully but failed to close. #' + issue.iid).yellow); - else - console.error((issueId + ': Inserted and closed successfully. #' + issue.iid).green); - }); - - return; - } - - console.log((issueId + ': Inserted successfully. #' + issue.iid).green); - }); - }); + return gitLab.mantisIssues = _.sortBy(rows, "Id"); + }, function(error) { + throw new Error('Cannot read input file: ' + inputFile + " - " + error); }); - }); - }); }); -}) +} -function getGitLabProject(name, privateToken, callback) { +/** + * Fetch project info from GitLab - assigns gitLab.project + */ +function getGitLabProject() { + log_progress("Fetching project from GitLab..."); var url = gitlabAPIURLBase + '/projects'; - var data = { per_page: 100, private_token: privateToken, sudo: gitlabSudo }; + var data = { per_page: 100, private_token: gitlabAdminPrivateToken, sudo: gitlabSudo }; - rest.get(url, {data: data}).on('complete', function(result, response) { - if (util.isError(result)) { - callback(result); - return; - } + return rest.get(url, {data: data}).then(function(result) { + + gitLab.project = _.find(result, { path_with_namespace : gitlabProjectName }) || null; - if (response.statusCode != 200) { - callback(result); - return; + if (!gitLab.project) { + throw new Error('Cannot find GitLab project: ' + gitlabProjectName); } - for (var i = 0; i < result.length; i++) { - if (result[i].path_with_namespace === name) { - callback(null, result[i]); - return; - } - }; + return gitLab.project; + }, function(error) { + throw new Error('Cannot get list of projects from gitlab: ' + url); + }); +} - callback(null, null); +/** + * Fetch project members from GitLab - assigns gitLab.gitlabUsers + */ +function getGitLabProjectMembers() { + log_progress("getGitLabProjectMembers"); + var url = gitlabAPIURLBase + '/projects/' + gitLab.project.id + "/members"; + var data = { per_page: 100, private_token: gitlabAdminPrivateToken, sudo: gitlabSudo }; + + return rest.get(url, {data: data}).then(function(result) { + return gitLab.gitlabUsers = result; + }, function(error) { + throw new Error('Cannot get list of users from gitlab: ' + url); }); } -function getGitLabUsers(privateToken, callback) { - var url = gitlabAPIURLBase + '/users'; - var data = { per_page: 100, private_token: privateToken, sudo: gitlabSudo }; +/** + * Sets config.users[].gl_id based gitLab.gitlabUsers + */ +function mapGitLabUserIds() { + var users = config.users, + gitlabUsers = gitLab.gitlabUsers; + _.forEach(users, function(user) { + user.gl_id = (_.find(gitlabUsers, { id: user.gl_username }) || {}).id; + }); +} - rest.get(url, {data: data}).on('complete', function(result, response) { - if (util.isError(result)) { - callback(result); - return; - } +/** + * Ensure that Mantise user names in gitLab.mantisIssues have corresponding GitLab user mapping + */ +function validateMantisIssues() { + log_progress("Validating Mantis Users..."); - if (response.statusCode != 200) { - callback(result); - return; - } + var mantisIssues = gitLab.mantisIssues; + var users = config.users; - callback(null, result); - }); -} + var missingUsernames = []; -function getConfig(configFile, callback) { - fs.readFile(configFile, {encoding: 'utf8'}, function(error, data) { - if (error) { - callback(error); - return; - } + for (var i = 0; i < mantisIssues.length; i++) { + var assignee = mantisIssues[i]["Assigned To"]; - var config = JSON.parse(data); - config.users = config.users || []; + if (!getUserByMantisUsername(assignee) && missingUsernames.indexOf(assignee) == -1) + missingUsernames.push(assignee); + } - callback(null, config); - }); -} + for (var i = 0; i < mantisIssues.length; i++) { + var reporter = mantisIssues[i].Reporter; -function setGitLabUserIds(users, gitlabUsers) { - for (var i = 0; i < users.length; i++) { - for (var j = 0; j < gitlabUsers.length; j++) { - if (users[i].gl_username === gitlabUsers[j].username) { - users[i].gl_id = gitlabUsers[j].id; - break; - } - } + if (!getUserByMantisUsername(reporter) && missingUsernames.indexOf(reporter) == -1) + missingUsernames.push(reporter); } -} -function readRows(inputFile, callback) { - fs.readFile(inputFile, {encoding: 'utf8'}, function(error, data) { - if (error) { - callback(error); - return; - } + if (missingUsernames.length > 0) { + for (var i = 0; i < missingUsernames.length; i++) + console.error('Error: Cannot map Mantis user with username: ' + missingUsernames[i]); - var rows = []; + throw new Error("User Validation Failed"); + } +} + +/** + * Import gitLab.mantisIssues into GitLab + * @returns {*} + */ +function importGitLabIssues() { + log_progress("Importing Mantis issues into GitLab from #" + fromIssueId + " ..."); + return _.reduce(gitLab.mantisIssues, function(p, mantisIssue) { + return p.then(function() { + return importIssue(mantisIssue); + }); + }, Q()); - csv().from(data, {delimiter: ',', escape: '"', columns: true}) - .on('record', function(row, index) { rows.push(row) }) - .on('end', function() { callback(null, rows) }); - }); } -function validate(rows, users, callback) { - var missingUsername = []; - var missingNames = []; +function importIssue(mantisIssue) { + var issueId = mantisIssue.Id; + var title = mantisIssue.Summary; + var description = getDescription(mantisIssue); + var assignee = getUserByMantisUsername(mantisIssue["Assigned To"]); + var milestoneId = ''; + var labels = getLabels(mantisIssue); + var author = getUserByMantisUsername(mantisIssue.Reporter); - for (var i = 0; i < rows.length; i++) { - var assignee = rows[i]["Assigned To"]; + log_progress("Importing: #" + issueId + " - " + title + " ..."); - if (!getUserByMantisUsername(users, assignee) && missingUsername.indexOf(assignee) == -1) - missingUsername.push(assignee); + var data = { + title: title, + description: description, + assignee_id: assignee && assignee.gl_id, + milestone_id: milestoneId, + labels: labels, + sudo: gitlabSudo, + private_token: gitlabAdminPrivateToken + }; + + return getIssue(gitLab.project.id, issueId) + .then(function(gitLabIssue) { + if (gitLabIssue) { + return updateIssue(gitLab.project.id, gitLabIssue.id, _.extend({ + state_event: isClosed(mantisIssue) ? 'close' : 'reopen' + }, data)) + .then(function() { + console.log(("#" + issueId + ": Updated successfully.").green); + }); + } else { + return insertSkippedIssues(issueId-1) + .then(function() { + return insertAndCloseIssue(issueId, data, isClosed(mantisIssue)); + }); + } + }); +} + +function insertSkippedIssues(issueId) { + if (gitLab.gitlabIssues[issueId]) { + return Q(); } - for (var i = 0; i < rows.length; i++) { - var reporter = rows[i].Reporter; + console.warn(("Skipping Missing Mantis Issue (<= #" + issueId + ") ...").yellow); - if (!getUserByMantisUsername(users, reporter) && missingNames.indexOf(reporter) == -1) - missingNames.push(reporter); + var data = { + title: "Skipped Mantis Issue", + sudo: gitlabSudo, + private_token: gitlabAdminPrivateToken + }; + + return insertAndCloseIssue(issueId, data, true, getSkippedIssueData) + .then(function() { + return insertSkippedIssues(issueId); + }); + + function getSkippedIssueData(gitLabIssue) { + var issueId = gitLabIssue.iid; + var description; + if (config.mantisUrl) { + description = "[Mantis Issue " + issueId + "](" + config.mantisUrl + "/view.php?id=" + issueId + ")"; + } else { + description = "Mantis Issue " + issueId; + } + return { + title: "Skipped Mantis Issue " + issueId, + description: "_Skipped " + description + "_" + }; } +} - callback(missingUsername, missingNames); +function insertAndCloseIssue(issueId, data, close, custom) { + + return insertIssue(gitLab.project.id, data).then(function(issue) { + gitLab.gitlabIssues[issue.iid] = issue; + if (close) { + return closeIssue(issue, custom && custom(issue)).then( + function() { + console.log((issueId + ': Inserted and closed successfully. #' + issue.iid).green); + }, function(error) { + console.warn((issueId + ': Inserted successfully but failed to close. #' + issue.iid).yellow); + }); + } + + console.log((issueId + ': Inserted successfully. #' + issue.iid).green); + }, function(error) { + console.error((issueId + ': Failed to insert.').red, error); + }); } -function getUserByMantisUsername(users, username) { - return (username && _.find(users, {username: username || null })) || null; +/** + * Fetch all existing project issues from GitLab - assigns gitLab.gitlabIssues + */ +function getGitLabProjectIssues() { + return getRemainingGitLabProjectIssues(0, 100) + .then(function(result) { + log_progress("Fetched " + result.length + " GitLab issues."); + var issues = _.indexBy(result, 'iid'); + return gitLab.gitlabIssues = issues; + }); +} + +/** + * Recursively fetch the remaining issues in the project + * @param page + * @param per_page + */ +function getRemainingGitLabProjectIssues(page, per_page) { + var from = page * per_page; + log_progress("Fetching Project Issues from GitLab [" + (from + 1) + "-" + (from + per_page) + "]..."); + var url = gitlabAPIURLBase + '/projects/' + gitLab.project.id + "/issues"; + var data = { + page: page, + per_page: per_page, + order_by: 'id', + private_token: gitlabAdminPrivateToken, sudo: gitlabSudo }; + + return rest.get(url, {data: data}).then(function(issues) { + if(issues.length < per_page) { + return issues; + } + return getRemainingGitLabProjectIssues(page+1, per_page) + .then(function(remainingIssues) { + return issues.concat(remainingIssues); + }); + }, function(error) { + throw new Error('Cannot get list of issues from gitlab: ' + url + " page=" + page); + }); +} + +function getUserByMantisUsername(username) { + return (username && config.users[username]) || config.users[""] || null; } function getDescription(row) { @@ -260,6 +372,10 @@ function getDescription(row) { description += "\n\n" + value; } + if (value = row.Notes) { + description += "\n\n" + value.split("$$$$").join("\n\n") + } + return description; } @@ -286,52 +402,55 @@ function isClosed(row) { return config.closed_statuses[row.Status]; } -function insertIssue(projectId, title, description, assigneeId, milestoneId, labels, creatorId, privateToken, callback) { +function getIssue(projectId, issueId) { + return Q(gitLab.gitlabIssues[issueId]); + // + //var url = gitlabAPIURLBase + '/projects/' + projectId + '/issues?iid=' + issueId; + //var data = { private_token: gitlabAdminPrivateToken, sudo: gitlabSudo }; + // + //return rest.get(url, {data: data}) + // .then(function(issues) { + // var issue = issues[0]; + // if(!issue) { + // throw new Error("Issue not found: " + issueId); + // } + // return issue; + // }); +} + +function insertIssue(projectId, data) { var url = gitlabAPIURLBase + '/projects/' + projectId + '/issues'; - var data = { - title: title, - description: description, - assignee_id: assigneeId, - milestone_id: milestoneId, - labels: labels, - sudo: creatorId, - private_token: privateToken - }; - rest.post(url, {data: data}).on('complete', function(result, response) { - if (util.isError(result)) { - callback(result); - return; - } + return rest.post(url, {data: data}) + .then(null, function(error) { + throw new Error('Failed to insert issue into GitLab: ' + url); + }); +} - if (response.statusCode != 201) { - callback(result); - return; - } +function updateIssue(projectId, issueId, data) { + var url = gitlabAPIURLBase + '/projects/' + projectId + '/issues/' + issueId; - callback(null, result); - }); + return rest.put(url, {data: data}) + .then(null, function(error) { + throw new Error('Failed to update issue in GitLab: ' + url + " " + JSON.stringify(error)); + }); } -function closeIssue(issue, privateToken, callback) { +function closeIssue(issue, custom) { var url = gitlabAPIURLBase + '/projects/' + issue.project_id + '/issues/' + issue.id; - var data = { + var data = _.extend({ state_event: 'close', - private_token: privateToken, + private_token: gitlabAdminPrivateToken, sudo: gitlabSudo - }; + }, custom); - rest.put(url, {data: data}).on('complete', function(result, response) { - if (util.isError(result)) { - callback(result); - return; - } + return rest.put(url, {data: data}) + .then(null, function(error) { + throw new Error('Failed to close issue in GitLab: ' + url); + }); +} - if (response.statusCode != 200) { - callback(result); - return; - } - callback(null); - }); -} +function log_progress(message) { + console.log(message.grey); +} \ No newline at end of file diff --git a/package.json b/package.json index 44158ef..22145a6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "csv": "~0.3.6", "lodash": "^3.10.1", "optimist": "~0.6.0", - "restler": "~3.1.0" + "q": "^1.4.1", + "q-io": "^1.13.1", + "restler": "~3.1.0", + "restler-q": "^0.1.1" }, "devDependencies": {}, "scripts": {