diff --git a/dist/cli/index.js b/dist/cli/index.js index 00de6da..84ee4a9 100755 --- a/dist/cli/index.js +++ b/dist/cli/index.js @@ -443,7 +443,10 @@ class PullRequestConfigsParser extends configs_parser_1.default { let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0]; if (backportBranch === undefined || backportBranch.trim() === "") { // for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects - const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-"); + const concatenatedCommits = originalPullRequest.commits.filter(c => c).map(c => c.slice(0, 7)).join("-"); + if (concatenatedCommits === "") { + throw new Error("Missing commits, stopping the backporting!"); + } backportBranch = `bp-${tb}-${concatenatedCommits}`; } else if (bpBranchNames.length == 1 && targetBranches.length > 1) { @@ -1053,7 +1056,11 @@ class GitHubMapper { } getSha(pr) { // if pr is open use latest commit sha otherwise use merge_commit_sha - return pr.state === "open" ? [pr.head.sha] : [pr.merge_commit_sha]; + const sha = pr.state === "open" ? pr.head.sha : pr.merge_commit_sha; + if (!sha) { + throw new Error("Trying to backport a single squashed/merged commit that does not exist! Aborting..."); + } + return [sha]; } async mapSourceRepo(pr) { return Promise.resolve({ @@ -1349,9 +1356,13 @@ class GitLabMapper { }; } getSha(mr) { - // if mr is merged, use merge_commit_sha otherwise use sha + // if mr is merged, use merge_commit_sha (or squash_commit_sha) otherwise use sha // what is the difference between sha and diff_refs.head_sha? - return this.isMerged(mr) ? [mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha] : [mr.sha]; + const sha = this.isMerged(mr) ? (mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha) : mr.sha; + if (!sha) { + throw new Error("Trying to backport a single squashed/merged commit that does not exist! Aborting..."); + } + return [sha]; } async mapSourceRepo(mr) { const project = await this.getProject(mr.source_project_id); diff --git a/dist/gha/index.js b/dist/gha/index.js index 8a83d68..7887f3f 100755 --- a/dist/gha/index.js +++ b/dist/gha/index.js @@ -408,7 +408,10 @@ class PullRequestConfigsParser extends configs_parser_1.default { let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0]; if (backportBranch === undefined || backportBranch.trim() === "") { // for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects - const concatenatedCommits = originalPullRequest.commits.map(c => c.slice(0, 7)).join("-"); + const concatenatedCommits = originalPullRequest.commits.filter(c => c).map(c => c.slice(0, 7)).join("-"); + if (concatenatedCommits === "") { + throw new Error("Missing commits, stopping the backporting!"); + } backportBranch = `bp-${tb}-${concatenatedCommits}`; } else if (bpBranchNames.length == 1 && targetBranches.length > 1) { @@ -1018,7 +1021,11 @@ class GitHubMapper { } getSha(pr) { // if pr is open use latest commit sha otherwise use merge_commit_sha - return pr.state === "open" ? [pr.head.sha] : [pr.merge_commit_sha]; + const sha = pr.state === "open" ? pr.head.sha : pr.merge_commit_sha; + if (!sha) { + throw new Error("Trying to backport a single squashed/merged commit that does not exist! Aborting..."); + } + return [sha]; } async mapSourceRepo(pr) { return Promise.resolve({ @@ -1314,9 +1321,13 @@ class GitLabMapper { }; } getSha(mr) { - // if mr is merged, use merge_commit_sha otherwise use sha + // if mr is merged, use merge_commit_sha (or squash_commit_sha) otherwise use sha // what is the difference between sha and diff_refs.head_sha? - return this.isMerged(mr) ? [mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha] : [mr.sha]; + const sha = this.isMerged(mr) ? (mr.squash_commit_sha ? mr.squash_commit_sha : mr.merge_commit_sha) : mr.sha; + if (!sha) { + throw new Error("Trying to backport a single squashed/merged commit that does not exist! Aborting..."); + } + return [sha]; } async mapSourceRepo(mr) { const project = await this.getProject(mr.source_project_id); diff --git a/src/service/configs/pullrequest/pr-configs-parser.ts b/src/service/configs/pullrequest/pr-configs-parser.ts index cb483bd..dd113d9 100644 --- a/src/service/configs/pullrequest/pr-configs-parser.ts +++ b/src/service/configs/pullrequest/pr-configs-parser.ts @@ -139,7 +139,10 @@ export default class PullRequestConfigsParser extends ConfigsParser { let backportBranch = bpBranchNames.length > 1 ? bpBranchNames[idx] : bpBranchNames[0]; if (backportBranch === undefined || backportBranch.trim() === "") { // for each commit takes the first 7 chars that are enough to uniquely identify them in most of the projects - const concatenatedCommits: string = originalPullRequest.commits!.map(c => c.slice(0, 7)).join("-"); + const concatenatedCommits: string = originalPullRequest.commits!.filter(c => c).map(c => c.slice(0, 7)).join("-"); + if (concatenatedCommits === "") { + throw new Error("Missing commits, stopping the backporting!"); + } backportBranch = `bp-${tb}-${concatenatedCommits}`; } else if (bpBranchNames.length == 1 && targetBranches.length > 1) { // multiple targets and single custom backport branch name we need to differentiate branch names diff --git a/src/service/git/github/github-mapper.ts b/src/service/git/github/github-mapper.ts index f501b5d..8b43995 100644 --- a/src/service/git/github/github-mapper.ts +++ b/src/service/git/github/github-mapper.ts @@ -37,7 +37,12 @@ export default class GitHubMapper implements GitResponseMapper { diff --git a/src/service/git/gitlab/gitlab-mapper.ts b/src/service/git/gitlab/gitlab-mapper.ts index 9efe2a0..fea93a0 100644 --- a/src/service/git/gitlab/gitlab-mapper.ts +++ b/src/service/git/gitlab/gitlab-mapper.ts @@ -25,6 +25,7 @@ export default class GitLabMapper implements GitResponseMapper { + return { number: mr.iid, author: mr.author.username, @@ -47,9 +48,14 @@ export default class GitLabMapper implements GitResponseMapper { diff --git a/test/service/runner/cli-gitlab-runner.test.ts b/test/service/runner/cli-gitlab-runner.test.ts index c3d15cb..85397a5 100644 --- a/test/service/runner/cli-gitlab-runner.test.ts +++ b/test/service/runner/cli-gitlab-runner.test.ts @@ -702,4 +702,15 @@ describe("cli runner", () => { // Not interested in all subsequent calls, already tested in other test cases }); + + test("throw error if missing squash commit", async () => { + addProcessArgs([ + "-tb", + "target", + "-pr", + "https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/6" + ]); + + await expect(() => runner.execute()).rejects.toThrow("Trying to backport a single squashed/merged commit that does not exist! Aborting..."); + }); }); \ No newline at end of file diff --git a/test/support/mock/git-client-mock-support.ts b/test/support/mock/git-client-mock-support.ts index 1a38493..4f31803 100644 --- a/test/support/mock/git-client-mock-support.ts +++ b/test/support/mock/git-client-mock-support.ts @@ -1,7 +1,7 @@ import LoggerServiceFactory from "@bp/service/logger/logger-service-factory"; import { Moctokit } from "@kie/mock-github"; import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER, GITHUB_GET_COMMIT } from "./github-data"; -import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER, MERGED_SQUASHED_MR_COMMITS, MERGED_NOT_SQUASHED_MR, MERGED_NOT_SQUASHED_MR_COMMITS } from "./gitlab-data"; +import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER, MERGED_SQUASHED_MR_COMMITS, MERGED_NOT_SQUASHED_MR, MERGED_NOT_SQUASHED_MR_COMMITS, UNDEFINED_COMMITS_MR } from "./gitlab-data"; import { CB_TARGET_OWNER, CB_REPO, CB_MERGED_PR_FIXTURE, CB_OPEN_PR_FIXTURE, CB_NOT_MERGED_PR_FIXTURE, CB_NOT_FOUND_PR_NUMBER, CB_MULT_COMMITS_PR_FIXTURE, CB_MULT_COMMITS_PR_COMMITS, CB_NEW_PR_URL, CB_NEW_PR_NUMBER, CODEBERG_GET_COMMIT } from "./codeberg-data"; // high number, for each test we are not expecting @@ -27,6 +27,8 @@ export const getAxiosMocked = (url: string) => { data = NESTED_NAMESPACE_MR; } else if (url.endsWith("merge_requests/5")) { data = MERGED_NOT_SQUASHED_MR; + } else if (url.endsWith("merge_requests/6")) { + data = UNDEFINED_COMMITS_MR; } else if (url.endsWith("projects/76316")) { data = PROJECT_EXAMPLE; } else if (url.endsWith("projects/1645")) { diff --git a/test/support/mock/gitlab-data.ts b/test/support/mock/gitlab-data.ts index 7adcf52..c168e0f 100644 --- a/test/support/mock/gitlab-data.ts +++ b/test/support/mock/gitlab-data.ts @@ -1055,4 +1055,139 @@ export const MERGED_NOT_SQUASHED_MR = { "user":{ "can_merge":true } +}; + +export const UNDEFINED_COMMITS_MR = { + "id":807106, + "iid":6, + "project_id":76316, + "title":"Update test.txt", + "description":"This is the body", + "state":"merged", + "created_at":"2023-06-28T14:32:40.943Z", + "updated_at":"2023-06-28T14:37:12.108Z", + "merged_by":{ + "id":14041, + "username":"superuser", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + }, + "merge_user":{ + "id":14041, + "username":"superuser", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + }, + "merged_at":"2023-06-28T14:37:11.667Z", + "closed_by":null, + "closed_at":null, + "target_branch":"main", + "source_branch":"feature", + "user_notes_count":0, + "upvotes":0, + "downvotes":0, + "author":{ + "id":14041, + "username":"superuser", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + }, + "assignees":[ + { + "id":14041, + "username":"superuser", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + } + ], + "assignee":{ + "id":14041, + "username":"superuser", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + }, + "reviewers":[ + { + "id":1404188, + "username":"superuser1", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + }, + { + "id":1404199, + "username":"superuser2", + "name":"Super User", + "state":"active", + "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", + "web_url":"https://my.gitlab.host.com/superuser" + } + ], + "source_project_id":76316, + "target_project_id":76316, + "labels":[ + "backport-prod" + ], + "draft":false, + "work_in_progress":false, + "milestone":null, + "merge_when_pipeline_succeeds":false, + "merge_status":"can_be_merged", + "detailed_merge_status":"not_open", + "sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644", + "merge_commit_sha":undefined, + "squash_commit_sha":undefined, + "discussion_locked":null, + "should_remove_source_branch":true, + "force_remove_source_branch":true, + "reference":"!2", + "references":{ + "short":"!2", + "relative":"!2", + "full":"superuser/backporting-example!6" + }, + "web_url":"https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/6", + "time_stats":{ + "time_estimate":0, + "total_time_spent":0, + "human_time_estimate":null, + "human_total_time_spent":null + }, + "squash":true, + "squash_on_merge":true, + "task_completion_status":{ + "count":0, + "completed_count":0 + }, + "has_conflicts":false, + "blocking_discussions_resolved":true, + "approvals_before_merge":null, + "subscribed":true, + "changes_count":"1", + "latest_build_started_at":null, + "latest_build_finished_at":null, + "first_deployed_to_production_at":null, + "pipeline":null, + "head_pipeline":null, + "diff_refs":{ + "base_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b", + "head_sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644", + "start_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b" + }, + "merge_error":null, + "first_contribution":false, + "user":{ + "can_merge":true + } }; \ No newline at end of file