diff --git a/README.md b/README.md index 5e5cddb..a4a1c08 100644 --- a/README.md +++ b/README.md @@ -206,8 +206,18 @@ Set to `true` (default) to enable using the [GitHub preview API for importing is ### usePlaceholderIssuesForMissingIssues +NOTE: This setting requires `usePlaceholderIssuesForMissingMergeRequests` to be set to `false` (default). + If this is set to `true` (default) then the migration process will automatically create empty dummy issues for every 'missing' GitLab issue (if you deleted a GitLab issue for example). Those issues will be closed on Github and they ensure that the issue ids stay the same on both GitLab and Github. +### usePlaceholderIssuesForMissingMergeRequests + +NOTE: GitLab has distinct numbers for issues and merge requests whereas GitHub treats pull requests as issues. +Only use this setting if your GitLab repository does not have issues or you do not intend on migrating them. +This setting therefore requires `usePlaceholderIssuesForMissingIssues` to be set to `false`. + +If this is set to `true` then the migration process will automatically create empty dummy issues for every 'missing' GitLab merge request (if you deleted a GitLab merge request for example). Those issues will be closed on Github and they ensure that the issue ids stay the same on both GitLab and Github. + #### usePlaceholderMilestonesForMissingMilestones If this is set to `true` (default) then the migration process will automatically create empty dummy milestones for every 'missing' GitLab milestone (if you deleted a GitLab milestone for example). Those milestones will be closed on Github and they ensure that the milestone ids stay the same on both GitLab and Github. diff --git a/sample_settings.ts b/sample_settings.ts index 6781390..4b6db45 100644 --- a/sample_settings.ts +++ b/sample_settings.ts @@ -49,6 +49,7 @@ export default { useIssueImportAPI: true, usePlaceholderMilestonesForMissingMilestones: true, usePlaceholderIssuesForMissingIssues: true, + usePlaceholderIssuesForMissingMergeRequests: false, useReplacementIssuesForCreationFails: true, useIssuesForAllMergeRequests: false, filterByLabel: undefined, diff --git a/src/githubHelper.ts b/src/githubHelper.ts index 55cd82b..972ebaf 100644 --- a/src/githubHelper.ts +++ b/src/githubHelper.ts @@ -900,6 +900,15 @@ export class GithubHelper { async createPullRequestAndComments( mergeRequest: GitLabMergeRequest ): Promise { + // Use the issue creation for placeholder issues + if (mergeRequest.isPlaceholder) { + let issue = mergeRequest as unknown; + await this.createIssueAndComments(issue as GitLabIssue); + console.log(`Created placeholder issue for placeholder merge request #${mergeRequest.iid}`); + + return; + } + let pullRequestData = await this.createPullRequest(mergeRequest); // createPullRequest() returns an issue number if a PR could not be created and diff --git a/src/index.ts b/src/index.ts index 5c0c2d3..07090a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import { SimpleLabel, SimpleMilestone, } from './githubHelper'; -import { GitlabHelper, GitLabIssue, GitLabMilestone } from './gitlabHelper'; +import { GitlabHelper, GitLabIssue, GitLabMergeRequest, GitLabMilestone } from './gitlabHelper'; import settings from '../settings'; import { Octokit as GitHubApi } from '@octokit/rest'; @@ -156,6 +156,23 @@ function createPlaceholderIssue(expectedIdx: number): Partial { }; } +/** + * Creates dummy data for a placeholder issue + * + * @param expectedIdx Number of the GitLab issue + * @returns Data for the issue + */ +function createPlaceholderMergeRequest(expectedIdx: number): Partial { + return { + iid: expectedIdx, + title: `[PLACEHOLDER] - for merge request #${expectedIdx}`, + description: + 'This is to ensure that merge request numbers in GitLab and GitHub are the same', + state: 'closed', + isPlaceholder: true, + }; +} + // ---------------------------------------------------------------------------- /** @@ -549,6 +566,25 @@ async function transferMergeRequests() { // Issues are sometimes created from Gitlab merge requests. Avoid creating duplicates. let githubIssues = await githubHelper.getAllGithubIssues(); + if (settings.usePlaceholderIssuesForMissingMergeRequests) { + for (let i = 0; i < mergeRequests.length; i++) { + // GitLab issue internal Id (iid) + let expectedIdx = i + 1; + + // is there a gap in the GitLab merge requests? + // Create placeholder issues so that new GitHub issues will have the same + // issue number as in GitLab. If a placeholder is used it is because there + // was a gap in GitLab merge requests -- likely caused by a deleted GitLab merge request. + if (mergeRequests[i].iid !== expectedIdx) { + mergeRequests.splice(i, 0, createPlaceholderMergeRequest(expectedIdx) as GitLabMergeRequest); // HACK: remove type coercion + counters.nrOfPlaceholderIssues++; + console.log( + `Added placeholder issue for GitLab merge request #${expectedIdx}.` + ); + } + } + } + console.log( 'Transferring ' + mergeRequests.length.toString() + ' merge requests' ); @@ -568,11 +604,17 @@ async function transferMergeRequests() { let githubIssue = githubIssues.find( // allow for issues titled "Original Issue Name - [merged|closed]" i => { - // regex needs escaping in case merge request title contains special characters - const regex = new RegExp(escapeRegExp(mr.title.trim()) + ' - \\[(merged|closed)\\]'); - return regex.test(i.title.trim()) && i.body.includes(mr.web_url); + if (!mr.isPlaceholder) { + // regex needs escaping in case merge request title contains special characters + const regex = new RegExp(escapeRegExp(mr.title.trim()) + ' - \\[(merged|closed)\\]'); + return regex.test(i.title.trim()) && i.body.includes(mr.web_url); + } + else { + return i.title.trim() === mr.title.trim(); + } } ); + if (!githubRequest && !githubIssue) { if (settings.skipMergeRequestStates.includes(mr.state)) { console.log( diff --git a/src/settings.ts b/src/settings.ts index b44ce8c..998f283 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -24,6 +24,7 @@ export default interface Settings { useIssueImportAPI: boolean; usePlaceholderMilestonesForMissingMilestones: boolean; usePlaceholderIssuesForMissingIssues: boolean; + usePlaceholderIssuesForMissingMergeRequests: boolean; useReplacementIssuesForCreationFails: boolean; useIssuesForAllMergeRequests: boolean; filterByLabel?: string;