Skip to content

Commit a3deab3

Browse files
committed
Use milestoneMap to map references in comments and descriptions
1 parent 97485a6 commit a3deab3

2 files changed

Lines changed: 168 additions & 73 deletions

File tree

src/githubHelper.ts

Lines changed: 168 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ export class GithubHelper {
6464
githubRepo: string;
6565
githubTimeout?: number;
6666
gitlabHelper: GitlabHelper;
67-
userProjectRegex: RegExp;
6867
repoId?: number;
6968
delayInMs: number;
7069
useIssuesForAllMergeRequests: boolean;
@@ -85,8 +84,6 @@ export class GithubHelper {
8584
this.githubRepo = githubSettings.repo;
8685
this.githubTimeout = githubSettings.timeout;
8786
this.gitlabHelper = gitlabHelper;
88-
// regex for converting user from GitLab to GitHub
89-
this.userProjectRegex = utils.generateUserProjectRegex();
9087
this.delayInMs = 2000;
9188
this.useIssuesForAllMergeRequests = useIssuesForAllMergeRequests;
9289
}
@@ -1097,67 +1094,188 @@ export class GithubHelper {
10971094

10981095
// TODO fix unexpected type coercion risk
10991096
/**
1100-
* Converts issue body and issue comments from GitLab to GitHub. That means:
1101-
* - (optionally) add a line at the beginning indicating which original user
1102-
* created the issue or the comment and when - because the GitHub API creates
1103-
* everything as the API user (use useIssueImportAPI: true in settings to use
1104-
* the issue import preview API instead which allows setting the date)
1105-
* - Change username from GitLab to GitHub in "mentions" (@username)
1097+
* Converts issue body or issue comments from GitLab to GitHub. That means:
1098+
* - (optionally) Adds a line at the beginning indicating which original user created the
1099+
* issue or the comment and when - because the GitHub API creates everything
1100+
* as the API user
1101+
* - Changes username from GitLab to GitHub in "mentions" (@username)
1102+
* - Changes milestone references to links
1103+
* - Changes MR references to PR references, taking into account the changes
1104+
* in indexing due to GitHub PRs using following the same numbering as
1105+
* issues
1106+
* - Changes issue numbers (necessary e.g. if dummy GH issues were not
1107+
* created for deleted GL issues).
1108+
*
1109+
* FIXME: conversion should be deactivated depending on the context in the
1110+
* markdown, e.g. strike-through text for labels, or code blocks for all
1111+
* references.
1112+
*
11061113
* @param str Body of the GitLab note
1107-
* @param item GitLab note
1114+
* @param item GitLab item to which the note belongs
11081115
* @param add_line Set to true to add the line with author and creation date
11091116
*/
1110-
11111117
async convertIssuesAndComments(
11121118
str: string,
1113-
item: any,
1119+
item: GitLabIssue | GitLabMergeRequest | GitLabNote | MilestoneImport,
11141120
add_line: boolean = true
1115-
) {
1121+
): Promise<string> {
1122+
// A note on implementation:
1123+
// We don't convert project names once at the beginning because otherwise
1124+
// we would have to check whether "text#23" refers to issue 23 or not, and
1125+
// so on for MRs, milestones, etc.
1126+
// Instead we consider either project#issue or " #issue" with non-word char
1127+
// before the #, and we do the same for MRs, labels and milestones.
1128+
11161129
const repoLink = `${this.githubUrl}/${this.githubOwner}/${this.githubRepo}`;
1117-
if (
1118-
(!settings.usermap || Object.keys(settings.usermap).length === 0) &&
1119-
(!settings.projectmap || Object.keys(settings.projectmap).length === 0)
1120-
) {
1121-
return add_line
1122-
? GithubHelper.addMigrationLine(str, item, repoLink)
1123-
: str;
1124-
} else {
1125-
// - Replace userids as defined in settings.usermap.
1126-
// They all start with '@' in the issues but we have them without in usermap
1127-
// - Replace cross-project issue references. They are matched on org/project# so 'matched' ends with '#'
1128-
// They all have a '#' right after the project name in the issues but we have them without in projectmap
1129-
let strWithMigLine = add_line
1130-
? GithubHelper.addMigrationLine(str, item, repoLink)
1131-
: str;
1132-
1133-
strWithMigLine = strWithMigLine.replace(
1134-
this.userProjectRegex,
1135-
matched => {
1136-
if (matched.startsWith('@')) {
1137-
// this is a userid
1138-
return '@' + settings.usermap[matched.substr(1)];
1139-
} else if (matched.endsWith('#')) {
1140-
// this is a cross-project issue reference
1141-
return (
1142-
settings.projectmap[matched.substring(0, matched.length - 1)] +
1143-
'#'
1144-
);
1145-
} else {
1146-
// something went wrong, do nothing
1147-
return matched;
1130+
const hasUsermap =
1131+
settings.usermap !== null && Object.keys(settings.usermap).length > 0;
1132+
const hasProjectmap =
1133+
settings.projectmap !== null &&
1134+
Object.keys(settings.projectmap).length > 0;
1135+
1136+
if (add_line) str = GithubHelper.addMigrationLine(str, item, repoLink);
1137+
let reString = '';
1138+
1139+
//
1140+
// User name conversion
1141+
//
1142+
1143+
if (hasUsermap) {
1144+
reString = '@' + Object.keys(settings.usermap).join('|@');
1145+
str = str.replace(
1146+
new RegExp(reString, 'g'),
1147+
match => '@' + settings.usermap[match.substring(1)]
1148+
);
1149+
}
1150+
1151+
//
1152+
// Issue reference conversion
1153+
//
1154+
1155+
let issueReplacer = (match: string) => {
1156+
// TODO: issueMap
1157+
return '#' + match;
1158+
};
1159+
1160+
if (hasProjectmap) {
1161+
reString =
1162+
'(' + Object.keys(settings.projectmap).join(')#(\\d+)|(') + ')#(\\d+)';
1163+
str = str.replace(
1164+
new RegExp(reString, 'g'),
1165+
(_, p1, p2) => settings.projectmap[p1] + '#' + issueReplacer(p2)
1166+
);
1167+
}
1168+
reString = '(?<=\\W)#(\\d+)';
1169+
str = str.replace(new RegExp(reString, 'g'), (_, p1) => issueReplacer(p1));
1170+
1171+
//
1172+
// Milestone reference replacement
1173+
//
1174+
1175+
let milestoneReplacer = (
1176+
number: string = '',
1177+
title: string = '',
1178+
repo: string = ''
1179+
) => {
1180+
let milestone: Partial<SimpleMilestone> = {};
1181+
if (this.milestoneMap) {
1182+
if (number) {
1183+
milestone = this.milestoneMap.get(parseInt(number)) ?? {
1184+
number: parseInt(number),
1185+
title: `GitHub milestone ${number} not in map`,
1186+
};
1187+
} else if (title) {
1188+
for (let m of this.milestoneMap.values()) {
1189+
if (m.title === title) {
1190+
milestone = m;
1191+
break;
1192+
}
11481193
}
11491194
}
1195+
}
1196+
if (milestone) {
1197+
const repoLink = `${this.githubUrl}/${this.githubOwner}/${
1198+
repo || this.githubRepo
1199+
}`;
1200+
return `[${milestone.title}](${repoLink}/milestone/${milestone.number})`;
1201+
}
1202+
console.log(
1203+
`\tERROR: Milestone ${number}, "${title}" not found among migrated milestones.`
11501204
);
1205+
return number || title || 'No milestone'; // milestone numbers are always > 0
1206+
};
11511207

1152-
strWithMigLine = await utils.migrateAttachments(
1153-
strWithMigLine,
1154-
this.repoId,
1155-
settings.s3,
1156-
this.gitlabHelper
1208+
if (hasProjectmap) {
1209+
// Replace: project%"Milestone"
1210+
reString =
1211+
'(' +
1212+
Object.keys(settings.projectmap).join(')%(".*?")|(') +
1213+
')%(".*?")';
1214+
str = str.replace(new RegExp(reString, 'g'), (_, p1, p2) =>
1215+
milestoneReplacer('', p2, settings.projectmap[p1])
11571216
);
11581217

1159-
return strWithMigLine;
1218+
// Replace: project%nn
1219+
reString =
1220+
'(' + Object.keys(settings.projectmap).join(')%(\\d+)|(') + ')%(\\d+)';
1221+
str = str.replace(new RegExp(reString, 'g'), (_, p1, p2) =>
1222+
milestoneReplacer(p2, '', settings.projectmap[p1])
1223+
);
11601224
}
1225+
// Replace: %"Milestone"
1226+
reString = '(?<=\\W)%"(.*?)"';
1227+
str = str.replace(new RegExp(reString, 'g'), (_, p1) =>
1228+
milestoneReplacer('', p1)
1229+
);
1230+
1231+
// Replace: %nn
1232+
reString = '(?<=\\W)%(\\d+)';
1233+
str = str.replace(new RegExp(reString, 'g'), (_, p1) =>
1234+
milestoneReplacer(p1, '')
1235+
);
1236+
1237+
//
1238+
// Label reference conversion
1239+
//
1240+
1241+
// FIXME: strike through in markdown is done as in: ~this text~
1242+
// These regexes will capture ~this as a label. If it is among the migrated
1243+
// labels, then it will be linked.
1244+
1245+
let labelReplacer = (label: string) => {};
1246+
1247+
// // Single word named label
1248+
// if (hasProjectmap) {
1249+
// const reChunk = '~([^~\\s\\.,;:\'"!@()\\\\\\[\\]])+(?=[^~\\w])';
1250+
// reString =
1251+
// '('
1252+
// + Object.keys(settings.projectmap).join(')' + reChunk + '|(')
1253+
// + ')'
1254+
// + reChunk;
1255+
// str = str.replace(new RegExp(reString, 'g'),
1256+
// (_, p1, p2) => )
1257+
1258+
// TODO
1259+
// } else {
1260+
// ...
1261+
// }
1262+
1263+
// // Quoted named label
1264+
// reString = '~"([^~"]|\\w)+"(?=[^~\\w])';
1265+
1266+
//
1267+
// MR reference conversion
1268+
//
1269+
// TODO
1270+
1271+
str = await utils.migrateAttachments(
1272+
str,
1273+
this.repoId,
1274+
settings.s3,
1275+
this.gitlabHelper
1276+
);
1277+
1278+
return str;
11611279
}
11621280

11631281
// ----------------------------------------------------------------------------

src/utils.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import settings from '../settings';
21
import { S3Settings } from './settings';
32
import * as mime from 'mime-types';
43
import * as path from 'path';
@@ -10,28 +9,6 @@ export const sleep = (milliseconds: number) => {
109
return new Promise(resolve => setTimeout(resolve, milliseconds));
1110
};
1211

13-
/**
14-
* Generate regular expression which finds userid and cross-project issue references
15-
* from usermap and projectmap
16-
*/
17-
export const generateUserProjectRegex = () => {
18-
let reString = '';
19-
if (settings.usermap !== null && Object.keys(settings.usermap).length > 0) {
20-
reString = '@' + Object.keys(settings.usermap).join('|@');
21-
}
22-
if (
23-
settings.projectmap !== null &&
24-
Object.keys(settings.projectmap).length > 0
25-
) {
26-
if (reString.length > 0) {
27-
reString += '|';
28-
}
29-
reString += Object.keys(settings.projectmap).join('#|') + '#';
30-
}
31-
32-
return new RegExp(reString, 'g');
33-
};
34-
3512
// Creates new attachments and replaces old links
3613
export const migrateAttachments = async (
3714
body: string,

0 commit comments

Comments
 (0)