|
// deno run --allow-read --allow-write ./jira.ts -i ./github-issues.json -u ./github-user-map.json -k ABC |
|
|
|
// Jira API Documentation |
|
// ------------------------------------------------------------------------------------------- |
|
// https://support.atlassian.com/jira-cloud-administration/docs/import-data-from-json/ |
|
/// |
|
|
|
import { parse } from 'https://deno.land/std/flags/mod.ts' |
|
import { errorLog, successLog, warningLog } from 'https://deno.land/x/colorlog/mod.ts' |
|
|
|
// Arguments |
|
// ------------------------------------------------------------------------------------------- |
|
|
|
const args = parse(Deno.args) |
|
|
|
if (args.h) { |
|
console.info('\n') |
|
console.info('OPTIONS:') |
|
console.info(' -h Help') |
|
console.info(' -i ./path_to_json_file.json Specify path to the file with GitHub issues JSON file.') |
|
console.info(' -k JIRA_PROJECT_KEY Specify Jira project key') |
|
console.info(' -u ./path_to_json_file.json Specify path to user map JSON file. Format:') |
|
console.info(` { |
|
"default": "default_jira_user_id", |
|
"github_username": "jira_user_id" |
|
}`) |
|
console.info(' "default" is used to fallback when the GitHub user is not in Jira') |
|
console.info(' -o ./output_file_path.json Optional. Specify and output path') |
|
console.info('\n') |
|
console.info('USAGE:') |
|
console.info( |
|
' deno run --allow-read --allow-write ./jira.ts -i ./github-issues.json -u ./github-user-map.json -k ABC', |
|
) |
|
console.info('\n') |
|
Deno.exit(1) |
|
} |
|
|
|
if (!args.i) { |
|
errorLog('\n\nError: Missing GitHub issues JSON file.\n') |
|
warningLog('Pass it with a -i parameter: -i ./path_to_json_file.json\n') |
|
warningLog('For more help run with -h\n') |
|
Deno.exit(1) |
|
} |
|
|
|
if (!args.k) { |
|
errorLog('\n\nError: Missing Jira Project Key\n') |
|
warningLog('Pass it with a -k parameter: -k ABC\n') |
|
warningLog('For more help run with -h\n') |
|
Deno.exit(1) |
|
} |
|
|
|
if (!args.u) { |
|
errorLog('\n\nError: Missing GitHub user map JSON file.\n') |
|
warningLog('Pass it with a -u parameter: -u ./path_to_json_file.json\n') |
|
warningLog('For more help run with -h\n') |
|
Deno.exit(1) |
|
} |
|
|
|
// Model |
|
// ------------------------------------------------------------------------------------------- |
|
|
|
interface UserMap { |
|
[key: string]: string |
|
} |
|
|
|
interface GitHubUser { |
|
login: string |
|
} |
|
|
|
interface GitHubLabel { |
|
name: string |
|
} |
|
|
|
interface GitHubComment { |
|
user: GitHubUser |
|
created_at: string |
|
body: string |
|
} |
|
|
|
interface GithubIssue { |
|
number: number |
|
url: string |
|
title: string |
|
user: GitHubUser |
|
labels: GitHubLabel[] |
|
comments: GitHubComment[] |
|
created_at: string |
|
body: string |
|
} |
|
|
|
// Utils |
|
// ------------------------------------------------------------------------------------------- |
|
|
|
function readJsonSync(filePath: string): unknown { |
|
const content = Deno.readTextFileSync(filePath) |
|
|
|
try { |
|
return JSON.parse(content) |
|
} catch (err) { |
|
err.message = `${filePath}: ${err.message}` |
|
throw err |
|
} |
|
} |
|
|
|
function mapUser(ghUsername: string, userMap: UserMap) { |
|
return userMap[ghUsername] ? userMap[ghUsername] : userMap['default'] |
|
} |
|
|
|
function createJiraLabels(ghLabels: GitHubLabel[]) { |
|
return ghLabels.map(label => label.name) |
|
} |
|
|
|
function createJiraCustomFields(ghIssueUrl: string) { |
|
return [ |
|
{ |
|
fieldName: 'GitHub Issue URL', |
|
fieldType: 'com.atlassian.jira.plugin.system.customfieldtypes:url', |
|
value: ghIssueUrl, |
|
}, |
|
] |
|
} |
|
|
|
function convertMarkdown(string: string | null) { |
|
// Jira documentation |
|
// https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=images |
|
return string |
|
? string |
|
.replaceAll(/(?:!\[(.*?)\]\((.*?)\))/g, '!$2!') |
|
.replaceAll(/(###### )/g, 'h6. ') |
|
.replaceAll(/(##### )/g, 'h5. ') |
|
.replaceAll(/(#### )/g, 'h4. ') |
|
.replaceAll(/(### )/g, 'h3. ') |
|
.replaceAll(/(## )/g, 'h2. ') |
|
.replaceAll(/(# )/g, 'h1. ') |
|
.replaceAll(/\*\*/g, '*') |
|
: '' |
|
} |
|
|
|
function createJiraComments(ghComments: GitHubComment[], userMap: UserMap) { |
|
return ghComments.map(comment => ({ |
|
body: convertMarkdown(comment.body), |
|
author: mapUser(comment.user.login, userMap), |
|
created: comment.created_at, |
|
})) |
|
} |
|
|
|
function createIssueType(ghLabels: GitHubLabel[]) { |
|
return ghLabels.find(label => label.name === 'type: Bug') !== undefined ? 'Bug' : 'Task' |
|
} |
|
|
|
function createJiraIssue(id: number, ghIssue: GithubIssue, userMap: UserMap) { |
|
return { |
|
externalId: `${id}`, |
|
issueType: createIssueType(ghIssue.labels), |
|
created: ghIssue.created_at, |
|
summary: ghIssue.title, |
|
description: convertMarkdown(ghIssue.body), |
|
reporter: mapUser(ghIssue.user.login, userMap), |
|
labels: createJiraLabels(ghIssue.labels), |
|
customFieldValues: createJiraCustomFields(ghIssue.url), |
|
comments: createJiraComments(ghIssue.comments, userMap), |
|
} |
|
} |
|
|
|
function createJiraProject(ghIssues: GithubIssue[], userMap: UserMap) { |
|
return { |
|
projects: [ |
|
{ |
|
name: 'GitHub Issues', |
|
key: args.k, |
|
issues: ghIssues.map((issue, i) => createJiraIssue(i + 1, issue, userMap)), |
|
}, |
|
], |
|
} |
|
} |
|
|
|
// Execution |
|
// ------------------------------------------------------------------------------------------- |
|
|
|
console.time('Execution time') |
|
|
|
const jiraProjectOutputFilename = args.o || './jira-project.json' |
|
|
|
const ghIssues = readJsonSync(args.i) as GithubIssue[] |
|
const userMap = readJsonSync(args.u) as UserMap |
|
|
|
if (!userMap.default || userMap.default.trim().length <= 1) { |
|
errorLog('\n\nError: Missing default Jira user ID\n') |
|
warningLog(`Edit ${args.u} file\n`) |
|
warningLog('For more help run with -h\n') |
|
Deno.exit(1) |
|
} |
|
|
|
const jiraProject = createJiraProject(ghIssues, userMap) |
|
|
|
await Deno.writeTextFile(jiraProjectOutputFilename, JSON.stringify(jiraProject)).then(() => { |
|
successLog(`Jira project is saved to ${jiraProjectOutputFilename}\n`) |
|
console.timeEnd('Execution time') |
|
}) |
This was very handy and works like a charm! Thanks, @mukhortov :)