@@ -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 // ----------------------------------------------------------------------------
0 commit comments