From 51dfc2748910890d30230503aa4e3f12c53476e9 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Thu, 25 Jul 2024 16:15:35 -0500 Subject: [PATCH 01/14] fix: ignore protocol mismatch when checking if repository is renamed --- lib/verify.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/verify.js b/lib/verify.js index 1e40780a..7469df94 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -166,7 +166,15 @@ export default async function verify(pluginConfig, context, { Octokit }) { status, data: { clone_url }, } = await octokit.request("GET /repos/{owner}/{repo}", { owner, repo }); - if (status !== 200 || repositoryUrl !== clone_url) { + + // compare only the / part of the two URLs in case one URL is HTTPS + // and the other is SSH + const getRepoPath = url => new URL(url).pathname.replace(/\.git$/, '') + try { + if (status !== 200 || getRepoPath(repositoryUrl) !== getRepoPath(clone_url)) { + errors.push(getError("EMISMATCHGITHUBURL")); + } + } catch (error) { errors.push(getError("EMISMATCHGITHUBURL")); } } From 365920ebce4500adc0a431872715494f59ff975b Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 09:43:38 -0500 Subject: [PATCH 02/14] fix: compare repositoryUrl to git_url instead of clone_url --- lib/verify.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index 7469df94..741e982c 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -30,12 +30,12 @@ const VALIDATORS = { isNonEmptyString(proxy) || (isPlainObject(proxy) && isNonEmptyString(proxy.host) && - isNumber(proxy.port)), + isNumber(proxy.port)) ), assets: isArrayOf( (asset) => isStringOrStringArray(asset) || - (isPlainObject(asset) && isStringOrStringArray(asset.path)), + (isPlainObject(asset) && isStringOrStringArray(asset.path)) ), successComment: canBeDisabled(isNonEmptyString), failTitle: canBeDisabled(isNonEmptyString), @@ -73,7 +73,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { getError(`EINVALID${option.toUpperCase()}`, { [option]: value }), ] : errors, - [], + [] ); if (githubApiUrl) { @@ -81,7 +81,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { } else if (githubUrl) { logger.log( "Verify GitHub authentication (%s)", - urlJoin(githubUrl, githubApiPathPrefix), + urlJoin(githubUrl, githubApiPathPrefix) ); } else { logger.log("Verify GitHub authentication"); @@ -101,7 +101,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { githubApiPathPrefix, githubApiUrl, proxy, - }), + }) ); // https://github.com/semantic-release/github/issues/182 @@ -159,22 +159,22 @@ export default async function verify(pluginConfig, context, { Octokit }) { githubApiPathPrefix, githubApiUrl, proxy, - }), + }) ); const { status, - data: { clone_url }, + data: { git_url }, } = await octokit.request("GET /repos/{owner}/{repo}", { owner, repo }); - // compare only the / part of the two URLs in case one URL is HTTPS - // and the other is SSH - const getRepoPath = url => new URL(url).pathname.replace(/\.git$/, '') - try { - if (status !== 200 || getRepoPath(repositoryUrl) !== getRepoPath(clone_url)) { - errors.push(getError("EMISMATCHGITHUBURL")); - } - } catch (error) { + const parsedRepositoryUrl = parseGithubUrl(repositoryUrl); + const parsedGitUrl = parseGithubUrl(git_url); + + if ( + status !== 200 || + parsedRepositoryUrl.owner !== parsedGitUrl.owner || + parsedRepositoryUrl.repo !== parsedGitUrl.repo + ) { errors.push(getError("EMISMATCHGITHUBURL")); } } From 1cd5b81e308884424149b1ec9ea6083c5a740645 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 09:52:39 -0500 Subject: [PATCH 03/14] fix: include values of repositoryUrl and git_url in EMISMATCHGITHUBURL --- lib/definitions/errors.js | 67 +++++++++++++++++++++++---------------- lib/verify.js | 2 +- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index 9dc7eca8..bd3fa597 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -21,7 +21,7 @@ export function EINVALIDASSETS({ assets }) { return { message: "Invalid `assets` option.", details: `The [assets option](${linkify( - "README.md#assets", + "README.md#assets" )}) must be an \`Array\` of \`Strings\` or \`Objects\` with a \`path\` property. Your configuration for the \`assets\` option is \`${stringify(assets)}\`.`, @@ -32,11 +32,11 @@ export function EINVALIDSUCCESSCOMMENT({ successComment }) { return { message: "Invalid `successComment` option.", details: `The [successComment option](${linkify( - "README.md#successcomment", + "README.md#successcomment" )}) if defined, must be a non empty \`String\`. Your configuration for the \`successComment\` option is \`${stringify( - successComment, + successComment )}\`.`, }; } @@ -45,11 +45,11 @@ export function EINVALIDFAILTITLE({ failTitle }) { return { message: "Invalid `failTitle` option.", details: `The [failTitle option](${linkify( - "README.md#failtitle", + "README.md#failtitle" )}) if defined, must be a non empty \`String\`. Your configuration for the \`failTitle\` option is \`${stringify( - failTitle, + failTitle )}\`.`, }; } @@ -58,11 +58,11 @@ export function EINVALIDFAILCOMMENT({ failComment }) { return { message: "Invalid `failComment` option.", details: `The [failComment option](${linkify( - "README.md#failcomment", + "README.md#failcomment" )}) if defined, must be a non empty \`String\`. Your configuration for the \`failComment\` option is \`${stringify( - failComment, + failComment )}\`.`, }; } @@ -71,7 +71,7 @@ export function EINVALIDLABELS({ labels }) { return { message: "Invalid `labels` option.", details: `The [labels option](${linkify( - "README.md#options", + "README.md#options" )}) if defined, must be an \`Array\` of non empty \`String\`. Your configuration for the \`labels\` option is \`${stringify(labels)}\`.`, @@ -82,11 +82,11 @@ export function EINVALIDASSIGNEES({ assignees }) { return { message: "Invalid `assignees` option.", details: `The [assignees option](${linkify( - "README.md#options", + "README.md#options" )}) must be an \`Array\` of non empty \`Strings\`. Your configuration for the \`assignees\` option is \`${stringify( - assignees, + assignees )}\`.`, }; } @@ -95,11 +95,11 @@ export function EINVALIDRELEASEDLABELS({ releasedLabels }) { return { message: "Invalid `releasedLabels` option.", details: `The [releasedLabels option](${linkify( - "README.md#options", + "README.md#options" )}) if defined, must be an \`Array\` of non empty \`String\`. Your configuration for the \`releasedLabels\` option is \`${stringify( - releasedLabels, + releasedLabels )}\`.`, }; } @@ -108,11 +108,11 @@ export function EINVALIDADDRELEASES({ addReleases }) { return { message: "Invalid `addReleases` option.", details: `The [addReleases option](${linkify( - "README.md#options", + "README.md#options" )}) if defined, must be one of \`false|top|bottom\`. Your configuration for the \`addReleases\` option is \`${stringify( - addReleases, + addReleases )}\`.`, }; } @@ -121,11 +121,11 @@ export function EINVALIDDRAFTRELEASE({ draftRelease }) { return { message: "Invalid `draftRelease` option.", details: `The [draftRelease option](${linkify( - "README.md#options", + "README.md#options" )}) if defined, must be a \`Boolean\`. Your configuration for the \`draftRelease\` option is \`${stringify( - draftRelease, + draftRelease )}\`.`, }; } @@ -139,11 +139,24 @@ By default the \`repositoryUrl\` option is retrieved from the \`repository\` pro }; } +export function EMISMATCHGITHUBURL({ repositoryUrl, git_url }) { + return { + message: "The git repository URL mismatches the GitHub URL.", + details: `The **semantic-release** \`repositoryUrl\` option must have the same repository name and owner as the GitHub repo. + +Your configuration for the \`repositoryUrl\` option is \`${stringify(repositoryUrl)}\` and the \`git_url\` of your GitHub repo is \`${stringify(git_url)}\`. + +By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. + +Note: If you have recently changed your GitHub repository name or owner, update the value in **semantic-release** \`repositoryUrl\` option and the \`repository\` property of your \`package.json\` respectively to match the new GitHub URL.`, + }; +} + export function EINVALIDPROXY({ proxy }) { return { message: "Invalid `proxy` option.", details: `The [proxy option](${linkify( - "README.md#proxy", + "README.md#proxy" )}) must be a \`String\` or an \`Objects\` with a \`host\` and a \`port\` property. Your configuration for the \`proxy\` option is \`${stringify(proxy)}\`.`, @@ -158,7 +171,7 @@ export function EMISSINGREPO({ owner, repo }) { By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. If you are using [GitHub Enterprise](https://enterprise.github.com) please make sure to configure the \`githubUrl\` and \`githubApiPathPrefix\` [options](${linkify( - "README.md#options", + "README.md#options" )}).`, }; } @@ -167,7 +180,7 @@ export function EGHNOPERMISSION({ owner, repo }) { return { message: `The GitHub token doesn't allow to push on the repository ${owner}/${repo}.`, details: `The user associated with the [GitHub token](${linkify( - "README.md#github-authentication", + "README.md#github-authentication" )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must allows to push to the repository ${owner}/${repo}. Please make sure the GitHub user associated with the token is an [owner](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#owner-access-on-a-repository-owned-by-a-user-account) or a [collaborator](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#collaborator-access-on-a-repository-owned-by-a-user-account) if the repository belong to a user account or has [write permissions](https://help.github.com/articles/managing-team-access-to-an-organization-repository) if the repository [belongs to an organization](https://help.github.com/articles/repository-permission-levels-for-an-organization).`, @@ -178,7 +191,7 @@ export function EINVALIDGHTOKEN({ owner, repo }) { return { message: "Invalid GitHub token.", details: `The [GitHub token](${linkify( - "README.md#github-authentication", + "README.md#github-authentication" )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must be a valid [personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) allowing to push to the repository ${owner}/${repo}. Please make sure to set the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable in your CI with the exact value of the GitHub personal token.`, @@ -189,7 +202,7 @@ export function ENOGHTOKEN({ owner, repo }) { return { message: "No GitHub token specified.", details: `A [GitHub personal token](${linkify( - "README.md#github-authentication", + "README.md#github-authentication" )}) must be created and set in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. Please make sure to create a [GitHub personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) and to set it in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. The token must allow to push to the repository ${owner}/${repo}.`, @@ -200,11 +213,11 @@ export function EINVALIDRELEASEBODYTEMPLATE({ releaseBodyTemplate }) { return { message: "Invalid `releaseBodyTemplate` option.", details: `The [releaseBodyTemplate option](${linkify( - "README.md#releaseBodyTemplate", + "README.md#releaseBodyTemplate" )}) must be a non empty \`String\`. Your configuration for the \`releaseBodyTemplate\` option is \`${stringify( - releaseBodyTemplate, + releaseBodyTemplate )}\`.`, }; } @@ -213,11 +226,11 @@ export function EINVALIDRELEASENAMETEMPLATE({ releaseNameTemplate }) { return { message: "Invalid `releaseNameTemplate` option.", details: `The [releaseNameTemplate option](${linkify( - "README.md#releaseNameTemplate", + "README.md#releaseNameTemplate" )}) must be a non empty \`String\`. Your configuration for the \`releaseNameTemplate\` option is \`${stringify( - releaseNameTemplate, + releaseNameTemplate )}\`.`, }; } @@ -226,11 +239,11 @@ export function EINVALIDDISCUSSIONCATEGORYNAME({ discussionCategoryName }) { return { message: "Invalid `discussionCategoryName` option.", details: `The [discussionCategoryName option](${linkify( - "README.md#discussionCategoryName", + "README.md#discussionCategoryName" )}) if defined, must be a non empty \`String\`. Your configuration for the \`discussionCategoryName\` option is \`${stringify( - discussionCategoryName, + discussionCategoryName )}\`.`, }; } diff --git a/lib/verify.js b/lib/verify.js index 63a9a05c..50a29a4b 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -175,7 +175,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { parsedRepositoryUrl.owner !== parsedGitUrl.owner || parsedRepositoryUrl.repo !== parsedGitUrl.repo ) { - errors.push(getError("EMISMATCHGITHUBURL")); + errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); } } From ed758eb4544c082950174bae203ec05315d1b267 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 11:12:27 -0500 Subject: [PATCH 04/14] chore: fix format --- lib/definitions/errors.js | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index bd3fa597..fe7559a9 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -21,7 +21,7 @@ export function EINVALIDASSETS({ assets }) { return { message: "Invalid `assets` option.", details: `The [assets option](${linkify( - "README.md#assets" + "README.md#assets", )}) must be an \`Array\` of \`Strings\` or \`Objects\` with a \`path\` property. Your configuration for the \`assets\` option is \`${stringify(assets)}\`.`, @@ -32,11 +32,11 @@ export function EINVALIDSUCCESSCOMMENT({ successComment }) { return { message: "Invalid `successComment` option.", details: `The [successComment option](${linkify( - "README.md#successcomment" + "README.md#successcomment", )}) if defined, must be a non empty \`String\`. Your configuration for the \`successComment\` option is \`${stringify( - successComment + successComment, )}\`.`, }; } @@ -45,11 +45,11 @@ export function EINVALIDFAILTITLE({ failTitle }) { return { message: "Invalid `failTitle` option.", details: `The [failTitle option](${linkify( - "README.md#failtitle" + "README.md#failtitle", )}) if defined, must be a non empty \`String\`. Your configuration for the \`failTitle\` option is \`${stringify( - failTitle + failTitle, )}\`.`, }; } @@ -58,11 +58,11 @@ export function EINVALIDFAILCOMMENT({ failComment }) { return { message: "Invalid `failComment` option.", details: `The [failComment option](${linkify( - "README.md#failcomment" + "README.md#failcomment", )}) if defined, must be a non empty \`String\`. Your configuration for the \`failComment\` option is \`${stringify( - failComment + failComment, )}\`.`, }; } @@ -71,7 +71,7 @@ export function EINVALIDLABELS({ labels }) { return { message: "Invalid `labels` option.", details: `The [labels option](${linkify( - "README.md#options" + "README.md#options", )}) if defined, must be an \`Array\` of non empty \`String\`. Your configuration for the \`labels\` option is \`${stringify(labels)}\`.`, @@ -82,11 +82,11 @@ export function EINVALIDASSIGNEES({ assignees }) { return { message: "Invalid `assignees` option.", details: `The [assignees option](${linkify( - "README.md#options" + "README.md#options", )}) must be an \`Array\` of non empty \`Strings\`. Your configuration for the \`assignees\` option is \`${stringify( - assignees + assignees, )}\`.`, }; } @@ -95,11 +95,11 @@ export function EINVALIDRELEASEDLABELS({ releasedLabels }) { return { message: "Invalid `releasedLabels` option.", details: `The [releasedLabels option](${linkify( - "README.md#options" + "README.md#options", )}) if defined, must be an \`Array\` of non empty \`String\`. Your configuration for the \`releasedLabels\` option is \`${stringify( - releasedLabels + releasedLabels, )}\`.`, }; } @@ -108,11 +108,11 @@ export function EINVALIDADDRELEASES({ addReleases }) { return { message: "Invalid `addReleases` option.", details: `The [addReleases option](${linkify( - "README.md#options" + "README.md#options", )}) if defined, must be one of \`false|top|bottom\`. Your configuration for the \`addReleases\` option is \`${stringify( - addReleases + addReleases, )}\`.`, }; } @@ -121,11 +121,11 @@ export function EINVALIDDRAFTRELEASE({ draftRelease }) { return { message: "Invalid `draftRelease` option.", details: `The [draftRelease option](${linkify( - "README.md#options" + "README.md#options", )}) if defined, must be a \`Boolean\`. Your configuration for the \`draftRelease\` option is \`${stringify( - draftRelease + draftRelease, )}\`.`, }; } @@ -156,7 +156,7 @@ export function EINVALIDPROXY({ proxy }) { return { message: "Invalid `proxy` option.", details: `The [proxy option](${linkify( - "README.md#proxy" + "README.md#proxy", )}) must be a \`String\` or an \`Objects\` with a \`host\` and a \`port\` property. Your configuration for the \`proxy\` option is \`${stringify(proxy)}\`.`, @@ -171,7 +171,7 @@ export function EMISSINGREPO({ owner, repo }) { By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. If you are using [GitHub Enterprise](https://enterprise.github.com) please make sure to configure the \`githubUrl\` and \`githubApiPathPrefix\` [options](${linkify( - "README.md#options" + "README.md#options", )}).`, }; } @@ -180,7 +180,7 @@ export function EGHNOPERMISSION({ owner, repo }) { return { message: `The GitHub token doesn't allow to push on the repository ${owner}/${repo}.`, details: `The user associated with the [GitHub token](${linkify( - "README.md#github-authentication" + "README.md#github-authentication", )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must allows to push to the repository ${owner}/${repo}. Please make sure the GitHub user associated with the token is an [owner](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#owner-access-on-a-repository-owned-by-a-user-account) or a [collaborator](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#collaborator-access-on-a-repository-owned-by-a-user-account) if the repository belong to a user account or has [write permissions](https://help.github.com/articles/managing-team-access-to-an-organization-repository) if the repository [belongs to an organization](https://help.github.com/articles/repository-permission-levels-for-an-organization).`, @@ -191,7 +191,7 @@ export function EINVALIDGHTOKEN({ owner, repo }) { return { message: "Invalid GitHub token.", details: `The [GitHub token](${linkify( - "README.md#github-authentication" + "README.md#github-authentication", )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must be a valid [personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) allowing to push to the repository ${owner}/${repo}. Please make sure to set the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable in your CI with the exact value of the GitHub personal token.`, @@ -202,7 +202,7 @@ export function ENOGHTOKEN({ owner, repo }) { return { message: "No GitHub token specified.", details: `A [GitHub personal token](${linkify( - "README.md#github-authentication" + "README.md#github-authentication", )}) must be created and set in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. Please make sure to create a [GitHub personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) and to set it in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. The token must allow to push to the repository ${owner}/${repo}.`, @@ -213,11 +213,11 @@ export function EINVALIDRELEASEBODYTEMPLATE({ releaseBodyTemplate }) { return { message: "Invalid `releaseBodyTemplate` option.", details: `The [releaseBodyTemplate option](${linkify( - "README.md#releaseBodyTemplate" + "README.md#releaseBodyTemplate", )}) must be a non empty \`String\`. Your configuration for the \`releaseBodyTemplate\` option is \`${stringify( - releaseBodyTemplate + releaseBodyTemplate, )}\`.`, }; } @@ -226,11 +226,11 @@ export function EINVALIDRELEASENAMETEMPLATE({ releaseNameTemplate }) { return { message: "Invalid `releaseNameTemplate` option.", details: `The [releaseNameTemplate option](${linkify( - "README.md#releaseNameTemplate" + "README.md#releaseNameTemplate", )}) must be a non empty \`String\`. Your configuration for the \`releaseNameTemplate\` option is \`${stringify( - releaseNameTemplate + releaseNameTemplate, )}\`.`, }; } @@ -239,11 +239,11 @@ export function EINVALIDDISCUSSIONCATEGORYNAME({ discussionCategoryName }) { return { message: "Invalid `discussionCategoryName` option.", details: `The [discussionCategoryName option](${linkify( - "README.md#discussionCategoryName" + "README.md#discussionCategoryName", )}) if defined, must be a non empty \`String\`. Your configuration for the \`discussionCategoryName\` option is \`${stringify( - discussionCategoryName + discussionCategoryName, )}\`.`, }; } From 373b68c03dbed7c16a9e1170c74cf45a0c6d94fd Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 12:40:16 -0500 Subject: [PATCH 05/14] fix: fix git repo check and tests --- lib/verify.js | 30 +- test/fail.test.js | 4 + test/integration.test.js | 93 ++-- test/success.test.js | 21 + test/verify.test.js | 929 +++++++++++++++++++++++++++------------ 5 files changed, 763 insertions(+), 314 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index 50a29a4b..b9644168 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -150,7 +150,8 @@ export default async function verify(pluginConfig, context, { Octokit }) { repo && githubToken && !errors.find(({ code }) => code === "EINVALIDPROXY") && - !errors.find(({ code }) => code === "EMISSINGREPO") + !errors.find(({ code }) => code === "EMISSINGREPO") && + !errors.find(({ code }) => code === "EINVALIDGHTOKEN") ) { const octokit = new Octokit( toOctokitOptions({ @@ -162,20 +163,25 @@ export default async function verify(pluginConfig, context, { Octokit }) { }), ); - const { - status, - data: { git_url }, - } = await octokit.request("GET /repos/{owner}/{repo}", { owner, repo }); + const { status, data } = await octokit.request( + "GET /repos/{owner}/{repo}", + { owner, repo }, + ); - const parsedRepositoryUrl = parseGithubUrl(repositoryUrl); - const parsedGitUrl = parseGithubUrl(git_url); + const git_url = data?.git_url; - if ( - status !== 200 || - parsedRepositoryUrl.owner !== parsedGitUrl.owner || - parsedRepositoryUrl.repo !== parsedGitUrl.repo - ) { + if (status !== 200) { errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); + } else { + const parsedRepositoryUrl = parseGithubUrl(repositoryUrl); + const parsedGitUrl = parseGithubUrl(git_url); + + if ( + parsedRepositoryUrl.owner !== parsedGitUrl.owner || + parsedRepositoryUrl.repo !== parsedGitUrl.repo + ) { + errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); + } } } diff --git a/test/fail.test.js b/test/fail.test.js index deb8b79d..9827b03d 100644 --- a/test/fail.test.js +++ b/test/fail.test.js @@ -115,6 +115,7 @@ test("Open a new issue with the list of errors and custom title and comment", as .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -182,6 +183,7 @@ test("Open a new issue with assignees and the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -254,6 +256,7 @@ test("Open a new issue without labels and the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -330,6 +333,7 @@ test("Update the first existing issue with the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( diff --git a/test/integration.test.js b/test/integration.test.js index b9f2f299..28b4dfdf 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -24,11 +24,16 @@ test("Verify GitHub auth", async (t) => { repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( t.context.m.verifyConditions( @@ -56,8 +61,11 @@ test("Verify GitHub auth with publish options", async (t) => { }; const fetch = fetchMock .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, + .get(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -91,11 +99,16 @@ test("Verify GitHub auth and assets config", async (t) => { publish: [{ path: "@semantic-release/npm" }], repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( t.context.m.verifyConditions( @@ -196,9 +209,16 @@ test("Publish a release with an array of assets", async (t) => { const fetch = fetchMock .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }) + .get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, @@ -288,9 +308,16 @@ test("Publish a release with release information in assets", async (t) => { const fetch = fetchMock .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }) + .get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, @@ -358,9 +385,16 @@ test("Update a release", async (t) => { const fetch = fetchMock .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }) + .get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ) .getOnce( `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, { id: releaseId }, @@ -426,10 +460,11 @@ test("Comment and add labels on PR included in the releases", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, + git_url: `https://github.com/${owner}/${repo}.git`, }, { - // TODO: why do we call the same endpoint twice? - repeat: 2, + // TODO: why do we call the same endpoint three times? + repeat: 3, }, ) .postOnce("https://api.github.local/graphql", { @@ -529,9 +564,10 @@ test("Open a new issue with the list of errors", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, + git_url: `https://github.com/${owner}/${repo}.git`, }, { - repeat: 2, + repeat: 3, }, ) .getOnce( @@ -625,9 +661,10 @@ test("Verify, release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, + git_url: `https://github.com/${owner}/${repo}.git`, }, { - repeat: 2, + repeat: 3, }, ) .postOnce( @@ -785,9 +822,10 @@ test("Verify, update release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, + git_url: `https://github.com/${owner}/${repo}.git`, }, { - repeat: 2, + repeat: 3, }, ) .getOnce( @@ -917,9 +955,10 @@ test("Verify and notify failure", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, + git_url: `https://github.com/${owner}/${repo}.git`, }, { - repeat: 2, + repeat: 3, }, ) .getOnce( diff --git a/test/success.test.js b/test/success.test.js index 8d87dccd..83c202eb 100644 --- a/test/success.test.js +++ b/test/success.test.js @@ -57,6 +57,7 @@ test("Add comment and labels to PRs associated with release commits and issues s .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${redirectedOwner}/${redirectedRepo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -418,6 +419,7 @@ test("Make multiple search queries if necessary", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .post("https://api.github.local/graphql", { data: { @@ -662,6 +664,7 @@ test("Do not add comment and labels for unrelated PR returned by search (compare .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -764,6 +767,7 @@ test("Do not add comment and labels if no PR is associated with release commits" .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -826,6 +830,7 @@ test("Do not add comment and labels if no commits is found for release", async ( .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -885,6 +890,7 @@ test("Do not add comment and labels to PR/issues from other repo", async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -985,6 +991,7 @@ test("Ignore missing and forbidden issues/PRs", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1154,6 +1161,7 @@ test("Add custom comment and labels", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1249,6 +1257,7 @@ test("Add custom label", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1339,6 +1348,7 @@ test("Comment on issue/PR without ading a label", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1432,6 +1442,7 @@ test("Editing the release to include all release links at the bottom", async (t) .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1536,6 +1547,7 @@ test("Editing the release to include all release links at the top", async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1637,6 +1649,7 @@ test("Editing the release to include all release links with no additional releas .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1727,6 +1740,7 @@ test("Editing the release to include all release links with no additional releas .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1810,6 +1824,7 @@ test("Editing the release to include all release links with no releases", async .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1895,6 +1910,7 @@ test("Editing the release with no ID in the release", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1985,6 +2001,7 @@ test("Ignore errors when adding comments and closing issues", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2118,6 +2135,7 @@ test("Close open issues when a release is successful", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2218,6 +2236,7 @@ test('Skip commention on issues/PR if "successComment" is "false"', async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -2269,6 +2288,7 @@ test('Skip closing issues if "failComment" is "false"', async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2320,6 +2340,7 @@ test('Skip closing issues if "failTitle" is "false"', async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { diff --git a/test/verify.test.js b/test/verify.test.js index 9bac5d0b..414ad3a5 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -27,11 +27,18 @@ test("Verify package, token and repository access", async (t) => { const labels = ["semantic-release"]; const discussionCategoryName = "Announcements"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -74,11 +81,18 @@ test('Verify package, token and repository access with "proxy", "asset", "discus const labels = null; const discussionCategoryName = null; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -116,11 +130,16 @@ test("Verify package, token and repository access and custom URL with prefix", a const githubUrl = "https://othertesturl.com:9090"; const githubApiPathPrefix = "prefix"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://othertesturl.com:9090/prefix/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://othertesturl.com:9090/prefix/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( verify( @@ -154,11 +173,18 @@ test("Verify package, token and repository access and custom URL without prefix" const env = { GH_TOKEN: "github_token" }; const githubUrl = "https://othertesturl.com:9090"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://othertesturl.com:9090/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -192,11 +218,18 @@ test("Verify package, token and repository access and shorthand repositoryUrl UR const env = { GH_TOKEN: "github_token" }; const githubUrl = "https://othertesturl.com:9090"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://othertesturl.com:9090/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -231,11 +264,14 @@ test("Verify package, token and repository with environment variables", async (t GH_PREFIX: "prefix", HTTP_PROXY: "https://localhost", }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + const fetch = fetchMock.sandbox().get( + `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, + { permissions: { push: true }, - }); + git_url: `https://github.com/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( verify( @@ -272,11 +308,14 @@ test("Verify package, token and repository access with alternative environment v GITHUB_PREFIX: "prefix", }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + const fetch = fetchMock.sandbox().get( + `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, + { permissions: { push: true }, - }); + git_url: `https://github.com/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( verify( @@ -306,11 +345,14 @@ test("Verify package, token and repository access with custom API URL", async (t const githubUrl = "https://othertesturl.com:9090"; const githubApiUrl = "https://api.othertesturl.com:9090"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.othertesturl.com:9090/repos/${owner}/${repo}`, { + const fetch = fetchMock.sandbox().get( + `https://api.othertesturl.com:9090/repos/${owner}/${repo}`, + { permissions: { push: true }, - }); + git_url: `https://github.com/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( verify( @@ -345,11 +387,14 @@ test("Verify package, token and repository access with API URL in environment va GITHUB_TOKEN: "github_token", }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.othertesturl.com:443/repos/${owner}/${repo}`, { + const fetch = fetchMock.sandbox().get( + `https://api.othertesturl.com:443/repos/${owner}/${repo}`, + { permissions: { push: true }, - }); + git_url: `https://github.com/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); await t.notThrowsAsync( verify( @@ -378,11 +423,18 @@ test('Verify "proxy" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const proxy = "https://locahost"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -410,11 +462,18 @@ test('Verify "proxy" is an object with "host" and "port" properties', async (t) const env = { GH_TOKEN: "github_token" }; const proxy = { host: "locahost", port: 80 }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -444,11 +503,18 @@ test('Verify "proxy" is a Boolean set to false', async (t) => { const env = { GH_TOKEN: "github_token" }; const proxy = false; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -476,11 +542,18 @@ test('Verify "assets" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = "file2.js"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -508,11 +581,18 @@ test('Verify "assets" is an Object with a path property', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = { path: "file2.js" }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -540,11 +620,18 @@ test('Verify "assets" is an Array of Object with a path property', async (t) => const env = { GH_TOKEN: "github_token" }; const assets = [{ path: "file1.js" }, { path: "file2.js" }]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -574,11 +661,18 @@ test('Verify "assets" is an Array of glob Arrays', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = [["dist/**", "!**/*.js"], "file2.js"]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -606,11 +700,18 @@ test('Verify "assets" is an Array of Object with a glob Arrays in path property' const env = { GH_TOKEN: "github_token" }; const assets = [{ path: ["dist/**", "!**/*.js"] }, { path: "file2.js" }]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -640,11 +741,18 @@ test('Verify "labels" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const labels = "semantic-release"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -672,11 +780,18 @@ test('Verify "assignees" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const assignees = "user"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -704,11 +819,18 @@ test('Verify "addReleases" is a valid string (top)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = "top"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -736,11 +858,18 @@ test('Verify "addReleases" is a valid string (bottom)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = "bottom"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -768,11 +897,18 @@ test('Verify "addReleases" is valid (false)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = false; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -800,11 +936,18 @@ test('Verify "draftRelease" is valid (true)', async (t) => { const env = { GH_TOKEN: "github_token" }; const draftRelease = true; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -832,11 +975,18 @@ test('Verify "draftRelease" is valid (false)', async (t) => { const env = { GH_TOKEN: "github_token" }; const draftRelease = false; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); await t.notThrowsAsync( verify( @@ -989,9 +1139,18 @@ test("Throw SemanticReleaseError if token doesn't have the push permission on th const fetch = fetchMock .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: false }, - }) + .get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: false, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ) .headOnce( "https://api.github.local/installation/repositories?per_page=1", 403, @@ -1030,7 +1189,10 @@ test("Do not throw SemanticReleaseError if token doesn't have the push permissio const fetch = fetchMock .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: false }, + permissions: { + push: false, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, }) .headOnce( "https://api.github.local/installation/repositories?per_page=1", @@ -1187,11 +1349,18 @@ test('Throw SemanticReleaseError if "assets" option is not a String or an Array const env = { GH_TOKEN: "github_token" }; const assets = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1224,11 +1393,18 @@ test('Throw SemanticReleaseError if "assets" option is an Array with invalid ele const env = { GH_TOKEN: "github_token" }; const assets = ["file.js", 42]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1261,11 +1437,18 @@ test('Throw SemanticReleaseError if "assets" option is an Object missing the "pa const env = { GH_TOKEN: "github_token" }; const assets = { name: "file.js" }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1298,11 +1481,18 @@ test('Throw SemanticReleaseError if "assets" option is an Array with objects mis const env = { GH_TOKEN: "github_token" }; const assets = [{ path: "lib/file.js" }, { name: "file.js" }]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1335,11 +1525,18 @@ test('Throw SemanticReleaseError if "successComment" option is not a String', as const env = { GH_TOKEN: "github_token" }; const successComment = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1372,11 +1569,18 @@ test('Throw SemanticReleaseError if "successComment" option is an empty String', const env = { GH_TOKEN: "github_token" }; const successComment = ""; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1409,11 +1613,18 @@ test('Throw SemanticReleaseError if "successComment" option is a whitespace Stri const env = { GH_TOKEN: "github_token" }; const successComment = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1446,11 +1657,18 @@ test('Throw SemanticReleaseError if "failTitle" option is not a String', async ( const env = { GH_TOKEN: "github_token" }; const failTitle = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1483,11 +1701,18 @@ test('Throw SemanticReleaseError if "failTitle" option is an empty String', asyn const env = { GH_TOKEN: "github_token" }; const failTitle = ""; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1520,11 +1745,18 @@ test('Throw SemanticReleaseError if "failTitle" option is a whitespace String', const env = { GH_TOKEN: "github_token" }; const failTitle = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1557,11 +1789,18 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is not a Str const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1594,11 +1833,18 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is an empty const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = ""; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1631,11 +1877,18 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is a whitesp const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1668,11 +1921,18 @@ test('Throw SemanticReleaseError if "failComment" option is not a String', async const env = { GH_TOKEN: "github_token" }; const failComment = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1705,11 +1965,18 @@ test('Throw SemanticReleaseError if "failComment" option is an empty String', as const env = { GH_TOKEN: "github_token" }; const failComment = ""; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1742,11 +2009,18 @@ test('Throw SemanticReleaseError if "failComment" option is a whitespace String' const env = { GH_TOKEN: "github_token" }; const failComment = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1779,11 +2053,18 @@ test('Throw SemanticReleaseError if "labels" option is not a String or an Array const env = { GH_TOKEN: "github_token" }; const labels = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1816,11 +2097,18 @@ test('Throw SemanticReleaseError if "labels" option is an Array with invalid ele const env = { GH_TOKEN: "github_token" }; const labels = ["label1", 42]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1853,11 +2141,18 @@ test('Throw SemanticReleaseError if "labels" option is a whitespace String', asy const env = { GH_TOKEN: "github_token" }; const labels = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1890,11 +2185,18 @@ test('Throw SemanticReleaseError if "assignees" option is not a String or an Arr const env = { GH_TOKEN: "github_token" }; const assignees = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1927,11 +2229,18 @@ test('Throw SemanticReleaseError if "assignees" option is an Array with invalid const env = { GH_TOKEN: "github_token" }; const assignees = ["user", 42]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -1964,11 +2273,18 @@ test('Throw SemanticReleaseError if "assignees" option is a whitespace String', const env = { GH_TOKEN: "github_token" }; const assignees = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2001,11 +2317,18 @@ test('Throw SemanticReleaseError if "releasedLabels" option is not a String or a const env = { GH_TOKEN: "github_token" }; const releasedLabels = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2038,11 +2361,18 @@ test('Throw SemanticReleaseError if "releasedLabels" option is an Array with inv const env = { GH_TOKEN: "github_token" }; const releasedLabels = ["label1", 42]; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2075,11 +2405,18 @@ test('Throw SemanticReleaseError if "releasedLabels" option is a whitespace Stri const env = { GH_TOKEN: "github_token" }; const releasedLabels = " \n \r "; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2112,11 +2449,18 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = "botom"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2149,11 +2493,18 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = true; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2186,11 +2537,18 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = 42; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2223,11 +2581,18 @@ test('Throw SemanticReleaseError if "draftRelease" option is not a valid boolean const env = { GH_TOKEN: "github_token" }; const draftRelease = "test"; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2259,11 +2624,18 @@ test('Throw SemanticReleaseError if "releaseBodyTemplate" option is an empty str const repo = "test_repo"; const env = { GH_TOKEN: "github_token" }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], @@ -2295,11 +2667,18 @@ test('Throw SemanticReleaseError if "releaseNameTemplate" option is an empty str const repo = "test_repo"; const env = { GH_TOKEN: "github_token" }; - const fetch = fetchMock - .sandbox() - .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { push: true }, - }); + const fetch = fetchMock.sandbox().get( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { + push: true, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ); const { errors: [error, ...errors], From 5c05d77ee0047f0c4ab4302c7364418355486a8d Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 12:43:13 -0500 Subject: [PATCH 06/14] test: fix mock git_urls --- test/integration.test.js | 10 +++++----- test/verify.test.js | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/integration.test.js b/test/integration.test.js index 28b4dfdf..ebcb6b51 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -460,7 +460,7 @@ test("Comment and add labels on PR included in the releases", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { // TODO: why do we call the same endpoint three times? @@ -564,7 +564,7 @@ test("Open a new issue with the list of errors", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 3, @@ -661,7 +661,7 @@ test("Verify, release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 3, @@ -822,7 +822,7 @@ test("Verify, update release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 3, @@ -955,7 +955,7 @@ test("Verify and notify failure", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 3, diff --git a/test/verify.test.js b/test/verify.test.js index 414ad3a5..56d93428 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -268,7 +268,7 @@ test("Verify package, token and repository with environment variables", async (t `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { permissions: { push: true }, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2 }, ); @@ -312,7 +312,7 @@ test("Verify package, token and repository access with alternative environment v `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { permissions: { push: true }, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2 }, ); @@ -349,7 +349,7 @@ test("Verify package, token and repository access with custom API URL", async (t `https://api.othertesturl.com:9090/repos/${owner}/${repo}`, { permissions: { push: true }, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2 }, ); @@ -391,7 +391,7 @@ test("Verify package, token and repository access with API URL in environment va `https://api.othertesturl.com:443/repos/${owner}/${repo}`, { permissions: { push: true }, - git_url: `https://github.com/${owner}/${repo}.git`, + git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2 }, ); From 2bb2d44392f230103109db3fa6a9cc65beeada99 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 12:57:31 -0500 Subject: [PATCH 07/14] test: add EMISMATCHGITHUBURL tests --- lib/verify.js | 68 +--- test/integration.test.js | 74 ++-- test/verify.test.js | 820 ++++++++++++++++++--------------------- 3 files changed, 420 insertions(+), 542 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index b9644168..34c95785 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -103,21 +103,30 @@ export default async function verify(pluginConfig, context, { Octokit }) { proxy, }), ); - - // https://github.com/semantic-release/github/issues/182 - // Do not check for permissions in GitHub actions, as the provided token is an installation access token. - // octokit.request("GET /repos/{owner}/{repo}", {repo, owner}) does not return the "permissions" key in that case. - // But GitHub Actions have all permissions required for @semantic-release/github to work - if (env.GITHUB_ACTION) { - return; - } - try { const { data: { permissions: { push }, + git_url, }, } = await octokit.request("GET /repos/{owner}/{repo}", { repo, owner }); + // Verify if Repository Name wasn't changed + const parsedGitUrl = parseGithubUrl(git_url); + if (owner !== parsedGitUrl.owner || repo !== parsedGitUrl.repo) { + errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); + } + + // https://github.com/semantic-release/github/issues/182 + // Do not check for permissions in GitHub actions, as the provided token is an installation access token. + // octokit.request("GET /repos/{owner}/{repo}", {repo, owner}) does not return the "permissions" key in that case. + // But GitHub Actions have all permissions required for @semantic-release/github to work + if (env.GITHUB_ACTION) { + if (errors.length > 0) { + throw new AggregateError(errors); + } + return; + } + if (!push) { // If authenticated as GitHub App installation, `push` will always be false. // We send another request to check if current authentication is an installation. @@ -144,47 +153,6 @@ export default async function verify(pluginConfig, context, { Octokit }) { } } - // Verify if Repository Name wasn't changed - if ( - owner && - repo && - githubToken && - !errors.find(({ code }) => code === "EINVALIDPROXY") && - !errors.find(({ code }) => code === "EMISSINGREPO") && - !errors.find(({ code }) => code === "EINVALIDGHTOKEN") - ) { - const octokit = new Octokit( - toOctokitOptions({ - githubToken, - githubUrl, - githubApiPathPrefix, - githubApiUrl, - proxy, - }), - ); - - const { status, data } = await octokit.request( - "GET /repos/{owner}/{repo}", - { owner, repo }, - ); - - const git_url = data?.git_url; - - if (status !== 200) { - errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); - } else { - const parsedRepositoryUrl = parseGithubUrl(repositoryUrl); - const parsedGitUrl = parseGithubUrl(git_url); - - if ( - parsedRepositoryUrl.owner !== parsedGitUrl.owner || - parsedRepositoryUrl.repo !== parsedGitUrl.repo - ) { - errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); - } - } - } - if (!githubToken) { errors.push(getError("ENOGHTOKEN", { owner, repo })); } diff --git a/test/integration.test.js b/test/integration.test.js index ebcb6b51..cfc545ce 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -24,16 +24,14 @@ test("Verify GitHub auth", async (t) => { repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( t.context.m.verifyConditions( @@ -99,16 +97,14 @@ test("Verify GitHub auth and assets config", async (t) => { publish: [{ path: "@semantic-release/npm" }], repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( t.context.m.verifyConditions( @@ -209,16 +205,12 @@ test("Publish a release with an array of assets", async (t) => { const fetch = fetchMock .sandbox() - .get( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { - push: true, - }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, }, - { repeat: 2 }, - ) + git_url: `https://api.github.local/${owner}/${repo}.git`, + }) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, @@ -308,16 +300,12 @@ test("Publish a release with release information in assets", async (t) => { const fetch = fetchMock .sandbox() - .get( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { - push: true, - }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, }, - { repeat: 2 }, - ) + git_url: `https://api.github.local/${owner}/${repo}.git`, + }) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, @@ -385,16 +373,12 @@ test("Update a release", async (t) => { const fetch = fetchMock .sandbox() - .get( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { - push: true, - }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, }, - { repeat: 2 }, - ) + git_url: `https://api.github.local/${owner}/${repo}.git`, + }) .getOnce( `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, { id: releaseId }, @@ -464,7 +448,7 @@ test("Comment and add labels on PR included in the releases", async (t) => { }, { // TODO: why do we call the same endpoint three times? - repeat: 3, + repeat: 2, }, ) .postOnce("https://api.github.local/graphql", { @@ -566,9 +550,7 @@ test("Open a new issue with the list of errors", async (t) => { full_name: `${owner}/${repo}`, git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, - { - repeat: 3, - }, + { repeat: 2 }, ) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -664,7 +646,7 @@ test("Verify, release and notify success", async (t) => { git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { - repeat: 3, + repeat: 2, }, ) .postOnce( @@ -825,7 +807,7 @@ test("Verify, update release and notify success", async (t) => { git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { - repeat: 3, + repeat: 2, }, ) .getOnce( @@ -958,7 +940,7 @@ test("Verify and notify failure", async (t) => { git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { - repeat: 3, + repeat: 2, }, ) .getOnce( diff --git a/test/verify.test.js b/test/verify.test.js index 56d93428..8feb3338 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -27,18 +27,14 @@ test("Verify package, token and repository access", async (t) => { const labels = ["semantic-release"]; const discussionCategoryName = "Announcements"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -81,18 +77,14 @@ test('Verify package, token and repository access with "proxy", "asset", "discus const labels = null; const discussionCategoryName = null; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -130,16 +122,14 @@ test("Verify package, token and repository access and custom URL with prefix", a const githubUrl = "https://othertesturl.com:9090"; const githubApiPathPrefix = "prefix"; - const fetch = fetchMock.sandbox().get( - `https://othertesturl.com:9090/prefix/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/prefix/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( verify( @@ -173,18 +163,14 @@ test("Verify package, token and repository access and custom URL without prefix" const env = { GH_TOKEN: "github_token" }; const githubUrl = "https://othertesturl.com:9090"; - const fetch = fetchMock.sandbox().get( - `https://othertesturl.com:9090/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -218,18 +204,14 @@ test("Verify package, token and repository access and shorthand repositoryUrl UR const env = { GH_TOKEN: "github_token" }; const githubUrl = "https://othertesturl.com:9090"; - const fetch = fetchMock.sandbox().get( - `https://othertesturl.com:9090/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -264,14 +246,14 @@ test("Verify package, token and repository with environment variables", async (t GH_PREFIX: "prefix", HTTP_PROXY: "https://localhost", }; - const fetch = fetchMock.sandbox().get( - `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, - { - permissions: { push: true }, + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, git_url: `htttps://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( verify( @@ -308,14 +290,14 @@ test("Verify package, token and repository access with alternative environment v GITHUB_PREFIX: "prefix", }; - const fetch = fetchMock.sandbox().get( - `https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, - { - permissions: { push: true }, + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, git_url: `htttps://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( verify( @@ -345,14 +327,14 @@ test("Verify package, token and repository access with custom API URL", async (t const githubUrl = "https://othertesturl.com:9090"; const githubApiUrl = "https://api.othertesturl.com:9090"; - const fetch = fetchMock.sandbox().get( - `https://api.othertesturl.com:9090/repos/${owner}/${repo}`, - { - permissions: { push: true }, + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.othertesturl.com:9090/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, git_url: `htttps://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( verify( @@ -387,14 +369,14 @@ test("Verify package, token and repository access with API URL in environment va GITHUB_TOKEN: "github_token", }; - const fetch = fetchMock.sandbox().get( - `https://api.othertesturl.com:443/repos/${owner}/${repo}`, - { - permissions: { push: true }, + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.othertesturl.com:443/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, git_url: `htttps://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); + }); await t.notThrowsAsync( verify( @@ -423,18 +405,14 @@ test('Verify "proxy" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const proxy = "https://locahost"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -462,18 +440,14 @@ test('Verify "proxy" is an object with "host" and "port" properties', async (t) const env = { GH_TOKEN: "github_token" }; const proxy = { host: "locahost", port: 80 }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -503,18 +477,14 @@ test('Verify "proxy" is a Boolean set to false', async (t) => { const env = { GH_TOKEN: "github_token" }; const proxy = false; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -542,18 +512,14 @@ test('Verify "assets" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = "file2.js"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -581,18 +547,14 @@ test('Verify "assets" is an Object with a path property', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = { path: "file2.js" }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -620,18 +582,14 @@ test('Verify "assets" is an Array of Object with a path property', async (t) => const env = { GH_TOKEN: "github_token" }; const assets = [{ path: "file1.js" }, { path: "file2.js" }]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -661,18 +619,14 @@ test('Verify "assets" is an Array of glob Arrays', async (t) => { const env = { GH_TOKEN: "github_token" }; const assets = [["dist/**", "!**/*.js"], "file2.js"]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -700,18 +654,14 @@ test('Verify "assets" is an Array of Object with a glob Arrays in path property' const env = { GH_TOKEN: "github_token" }; const assets = [{ path: ["dist/**", "!**/*.js"] }, { path: "file2.js" }]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -741,18 +691,14 @@ test('Verify "labels" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const labels = "semantic-release"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -780,18 +726,14 @@ test('Verify "assignees" is a String', async (t) => { const env = { GH_TOKEN: "github_token" }; const assignees = "user"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -819,18 +761,14 @@ test('Verify "addReleases" is a valid string (top)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = "top"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -858,18 +796,14 @@ test('Verify "addReleases" is a valid string (bottom)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = "bottom"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -897,18 +831,14 @@ test('Verify "addReleases" is valid (false)', async (t) => { const env = { GH_TOKEN: "github_token" }; const addReleases = false; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -936,18 +866,14 @@ test('Verify "draftRelease" is valid (true)', async (t) => { const env = { GH_TOKEN: "github_token" }; const draftRelease = true; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -975,18 +901,14 @@ test('Verify "draftRelease" is valid (false)', async (t) => { const env = { GH_TOKEN: "github_token" }; const draftRelease = false; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); await t.notThrowsAsync( verify( @@ -1024,13 +946,71 @@ test("Verify if run in GitHub Action", async (t) => { const labels = ["semantic-release"]; const discussionCategoryName = "Announcements"; + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }); + await t.notThrowsAsync( verify( { proxy, assets, successComment, failTitle, failComment, labels }, { env, options: { - repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, + repositoryUrl: `git+https://othertesturl.com:9090/${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +// https://github.com/semantic-release/github/issues/182 +test("Verify if run in GitHub Action and repo is renamed", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GITHUB_TOKEN: "v1.1234567890123456789012345678901234567890", + GITHUB_ACTION: "Release", + }; + const proxy = "https://localhost"; + const assets = [{ path: "lib/file.js" }, "file.js"]; + const successComment = "Test comment"; + const failTitle = "Test title"; + const failComment = "Test comment"; + const labels = ["semantic-release"]; + const discussionCategoryName = "Announcements"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + git_url: `https://api.github.local/${owner}/${repo}2.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { proxy, assets, successComment, failTitle, failComment, labels }, + { + env, + options: { + repositoryUrl: `git+https://othertesturl.com:9090/${owner}/${repo}.git`, }, logger: t.context.logger, }, @@ -1042,6 +1022,11 @@ test("Verify if run in GitHub Action", async (t) => { }, ), ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); }); test("Throw SemanticReleaseError for missing github token", async (t) => { @@ -1139,18 +1124,12 @@ test("Throw SemanticReleaseError if token doesn't have the push permission on th const fetch = fetchMock .sandbox() - .get( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { - push: false, - }, - git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, }, - ) + git_url: `https://api.github.local/${owner}/${repo}.git`, + }) .headOnce( "https://api.github.local/installation/repositories?per_page=1", 403, @@ -1253,6 +1232,79 @@ test("Throw SemanticReleaseError if the repository doesn't exist", async (t) => t.true(fetch.done()); }); +test("Don't throw an error if git_url differs from repositoryUrl but owner/repo is the same", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + git_url: `https://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ); + + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `git+https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test("Throw SemanticReleaseError if the repository is renamed", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + git_url: `https://api.github.local/${owner}/${repo}2.git`, + }, + { repeat: 2 }, + ); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); +}); + test("Throw error if github return any other errors", async (t) => { const owner = "test_user"; const repo = "test_repo"; @@ -1349,18 +1401,14 @@ test('Throw SemanticReleaseError if "assets" option is not a String or an Array const env = { GH_TOKEN: "github_token" }; const assets = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1393,18 +1441,14 @@ test('Throw SemanticReleaseError if "assets" option is an Array with invalid ele const env = { GH_TOKEN: "github_token" }; const assets = ["file.js", 42]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1437,18 +1481,14 @@ test('Throw SemanticReleaseError if "assets" option is an Object missing the "pa const env = { GH_TOKEN: "github_token" }; const assets = { name: "file.js" }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1481,18 +1521,14 @@ test('Throw SemanticReleaseError if "assets" option is an Array with objects mis const env = { GH_TOKEN: "github_token" }; const assets = [{ path: "lib/file.js" }, { name: "file.js" }]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1525,18 +1561,14 @@ test('Throw SemanticReleaseError if "successComment" option is not a String', as const env = { GH_TOKEN: "github_token" }; const successComment = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1569,18 +1601,14 @@ test('Throw SemanticReleaseError if "successComment" option is an empty String', const env = { GH_TOKEN: "github_token" }; const successComment = ""; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1613,18 +1641,14 @@ test('Throw SemanticReleaseError if "successComment" option is a whitespace Stri const env = { GH_TOKEN: "github_token" }; const successComment = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1657,18 +1681,14 @@ test('Throw SemanticReleaseError if "failTitle" option is not a String', async ( const env = { GH_TOKEN: "github_token" }; const failTitle = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1701,18 +1721,14 @@ test('Throw SemanticReleaseError if "failTitle" option is an empty String', asyn const env = { GH_TOKEN: "github_token" }; const failTitle = ""; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1745,18 +1761,14 @@ test('Throw SemanticReleaseError if "failTitle" option is a whitespace String', const env = { GH_TOKEN: "github_token" }; const failTitle = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1789,18 +1801,14 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is not a Str const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1833,18 +1841,14 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is an empty const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = ""; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1877,18 +1881,14 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is a whitesp const env = { GH_TOKEN: "github_token" }; const discussionCategoryName = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1921,18 +1921,14 @@ test('Throw SemanticReleaseError if "failComment" option is not a String', async const env = { GH_TOKEN: "github_token" }; const failComment = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -1965,18 +1961,14 @@ test('Throw SemanticReleaseError if "failComment" option is an empty String', as const env = { GH_TOKEN: "github_token" }; const failComment = ""; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2009,18 +2001,14 @@ test('Throw SemanticReleaseError if "failComment" option is a whitespace String' const env = { GH_TOKEN: "github_token" }; const failComment = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2053,18 +2041,14 @@ test('Throw SemanticReleaseError if "labels" option is not a String or an Array const env = { GH_TOKEN: "github_token" }; const labels = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2097,18 +2081,14 @@ test('Throw SemanticReleaseError if "labels" option is an Array with invalid ele const env = { GH_TOKEN: "github_token" }; const labels = ["label1", 42]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2141,18 +2121,14 @@ test('Throw SemanticReleaseError if "labels" option is a whitespace String', asy const env = { GH_TOKEN: "github_token" }; const labels = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2185,18 +2161,14 @@ test('Throw SemanticReleaseError if "assignees" option is not a String or an Arr const env = { GH_TOKEN: "github_token" }; const assignees = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2229,18 +2201,14 @@ test('Throw SemanticReleaseError if "assignees" option is an Array with invalid const env = { GH_TOKEN: "github_token" }; const assignees = ["user", 42]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2273,18 +2241,14 @@ test('Throw SemanticReleaseError if "assignees" option is a whitespace String', const env = { GH_TOKEN: "github_token" }; const assignees = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2317,18 +2281,14 @@ test('Throw SemanticReleaseError if "releasedLabels" option is not a String or a const env = { GH_TOKEN: "github_token" }; const releasedLabels = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2361,18 +2321,14 @@ test('Throw SemanticReleaseError if "releasedLabels" option is an Array with inv const env = { GH_TOKEN: "github_token" }; const releasedLabels = ["label1", 42]; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2405,18 +2361,14 @@ test('Throw SemanticReleaseError if "releasedLabels" option is a whitespace Stri const env = { GH_TOKEN: "github_token" }; const releasedLabels = " \n \r "; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2449,18 +2401,14 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = "botom"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2493,18 +2441,14 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = true; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2537,18 +2481,14 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( const env = { GH_TOKEN: "github_token" }; const addReleases = 42; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2581,18 +2521,14 @@ test('Throw SemanticReleaseError if "draftRelease" option is not a valid boolean const env = { GH_TOKEN: "github_token" }; const draftRelease = "test"; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2624,18 +2560,14 @@ test('Throw SemanticReleaseError if "releaseBodyTemplate" option is an empty str const repo = "test_repo"; const env = { GH_TOKEN: "github_token" }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], @@ -2667,18 +2599,14 @@ test('Throw SemanticReleaseError if "releaseNameTemplate" option is an empty str const repo = "test_repo"; const env = { GH_TOKEN: "github_token" }; - const fetch = fetchMock.sandbox().get( - `https://api.github.local/repos/${owner}/${repo}`, - { + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { permissions: { push: true, }, git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { - repeat: 2, - }, - ); + }); const { errors: [error, ...errors], From 4938358ad8fef5604b40c71cefdb9d967ae6baf9 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:02:20 -0500 Subject: [PATCH 08/14] chore: format code --- test/verify.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/verify.test.js b/test/verify.test.js index 8feb3338..c4596b28 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -1251,7 +1251,9 @@ test("Don't throw an error if git_url differs from repositoryUrl but owner/repo {}, { env, - options: { repositoryUrl: `git+https://github.com/${owner}/${repo}.git` }, + options: { + repositoryUrl: `git+https://github.com/${owner}/${repo}.git`, + }, logger: t.context.logger, }, { From b13c88ed621f7e59e0dacb89dd1eaaaf0e56c35f Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:06:32 -0500 Subject: [PATCH 09/14] fix: mistake that would cause error if run in github action --- lib/verify.js | 7 ++----- test/verify.test.js | 6 ------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index 34c95785..b0ca6626 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -105,10 +105,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { ); try { const { - data: { - permissions: { push }, - git_url, - }, + data: { permissions, git_url }, } = await octokit.request("GET /repos/{owner}/{repo}", { repo, owner }); // Verify if Repository Name wasn't changed const parsedGitUrl = parseGithubUrl(git_url); @@ -127,7 +124,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { return; } - if (!push) { + if (!permissions?.push) { // If authenticated as GitHub App installation, `push` will always be false. // We send another request to check if current authentication is an installation. // Note: we cannot check if the installation has all required permissions, it's diff --git a/test/verify.test.js b/test/verify.test.js index c4596b28..6110d18b 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -949,9 +949,6 @@ test("Verify if run in GitHub Action", async (t) => { const fetch = fetchMock .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { - push: false, - }, git_url: `https://api.github.local/${owner}/${repo}.git`, }); @@ -996,9 +993,6 @@ test("Verify if run in GitHub Action and repo is renamed", async (t) => { const fetch = fetchMock .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - permissions: { - push: false, - }, git_url: `https://api.github.local/${owner}/${repo}2.git`, }); From e1e8ec22dbc66bb4e93ad80751f504cd8efd7b9d Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:10:17 -0500 Subject: [PATCH 10/14] test: remove comment about repeat calls --- test/integration.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration.test.js b/test/integration.test.js index cfc545ce..e7b2a343 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -447,7 +447,6 @@ test("Comment and add labels on PR included in the releases", async (t) => { git_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { - // TODO: why do we call the same endpoint three times? repeat: 2, }, ) From 1996da57e91661d462cdb3b368d77fa0219f81cf Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:17:19 -0500 Subject: [PATCH 11/14] refactor: code cleanup --- lib/verify.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index b0ca6626..8aadd3a5 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -117,14 +117,7 @@ export default async function verify(pluginConfig, context, { Octokit }) { // Do not check for permissions in GitHub actions, as the provided token is an installation access token. // octokit.request("GET /repos/{owner}/{repo}", {repo, owner}) does not return the "permissions" key in that case. // But GitHub Actions have all permissions required for @semantic-release/github to work - if (env.GITHUB_ACTION) { - if (errors.length > 0) { - throw new AggregateError(errors); - } - return; - } - - if (!permissions?.push) { + if (!env.GITHUB_ACTION && !permissions?.push) { // If authenticated as GitHub App installation, `push` will always be false. // We send another request to check if current authentication is an installation. // Note: we cannot check if the installation has all required permissions, it's From 2102cc98163a897749c8268ba6f96b16a5f580c7 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:19:57 -0500 Subject: [PATCH 12/14] fix: check for rename even when authenticated as a GitHub App installation --- lib/verify.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/verify.js b/lib/verify.js index 8aadd3a5..5e334b7f 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -123,14 +123,12 @@ export default async function verify(pluginConfig, context, { Octokit }) { // Note: we cannot check if the installation has all required permissions, it's // up to the user to make sure it has if ( - await octokit + !(await octokit .request("HEAD /installation/repositories", { per_page: 1 }) - .catch(() => false) + .catch(() => false)) ) { - return; + errors.push(getError("EGHNOPERMISSION", { owner, repo })); } - - errors.push(getError("EGHNOPERMISSION", { owner, repo })); } } catch (error) { if (error.status === 401) { From b5996658a8f6f4b4081833254ef5ed50b01f7759 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Wed, 31 Jul 2024 13:23:29 -0500 Subject: [PATCH 13/14] test: test rename check for GitHub installation --- test/verify.test.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/verify.test.js b/test/verify.test.js index 6110d18b..29e8036f 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -1023,6 +1023,49 @@ test("Verify if run in GitHub Action and repo is renamed", async (t) => { t.true(fetch.done()); }); +test("Verify if token is a Github installation token and repo is renamed", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + git_url: `https://api.github.local/${owner}/${repo}2.git`, + }) + .headOnce( + "https://api.github.local/installation/repositories?per_page=1", + 200, + ); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); +}); + test("Throw SemanticReleaseError for missing github token", async (t) => { const { errors: [error, ...errors], From 46a44dde466ecffc6d92461bc334065c84baa197 Mon Sep 17 00:00:00 2001 From: Andy Edwards Date: Sun, 4 Aug 2024 11:14:05 -0500 Subject: [PATCH 14/14] fix: switch back to clone_url, test a lot of URL formats --- lib/definitions/errors.js | 4 +- lib/verify.js | 10 +- test/fail.test.js | 8 +- test/integration.test.js | 22 +-- test/success.test.js | 42 +++--- test/verify.test.js | 274 ++++++++++++++++++++------------------ 6 files changed, 187 insertions(+), 173 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index fe7559a9..74fa1ce2 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -139,12 +139,12 @@ By default the \`repositoryUrl\` option is retrieved from the \`repository\` pro }; } -export function EMISMATCHGITHUBURL({ repositoryUrl, git_url }) { +export function EMISMATCHGITHUBURL({ repositoryUrl, clone_url }) { return { message: "The git repository URL mismatches the GitHub URL.", details: `The **semantic-release** \`repositoryUrl\` option must have the same repository name and owner as the GitHub repo. -Your configuration for the \`repositoryUrl\` option is \`${stringify(repositoryUrl)}\` and the \`git_url\` of your GitHub repo is \`${stringify(git_url)}\`. +Your configuration for the \`repositoryUrl\` option is \`${stringify(repositoryUrl)}\` and the \`clone_url\` of your GitHub repo is \`${stringify(clone_url)}\`. By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. diff --git a/lib/verify.js b/lib/verify.js index 5e334b7f..b6ccba95 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -105,12 +105,14 @@ export default async function verify(pluginConfig, context, { Octokit }) { ); try { const { - data: { permissions, git_url }, + data: { permissions, clone_url }, } = await octokit.request("GET /repos/{owner}/{repo}", { repo, owner }); // Verify if Repository Name wasn't changed - const parsedGitUrl = parseGithubUrl(git_url); - if (owner !== parsedGitUrl.owner || repo !== parsedGitUrl.repo) { - errors.push(getError("EMISMATCHGITHUBURL", { repositoryUrl, git_url })); + const parsedCloneUrl = parseGithubUrl(clone_url); + if (owner !== parsedCloneUrl.owner || repo !== parsedCloneUrl.repo) { + errors.push( + getError("EMISMATCHGITHUBURL", { repositoryUrl, clone_url }), + ); } // https://github.com/semantic-release/github/issues/182 diff --git a/test/fail.test.js b/test/fail.test.js index 9827b03d..9b7caee9 100644 --- a/test/fail.test.js +++ b/test/fail.test.js @@ -115,7 +115,7 @@ test("Open a new issue with the list of errors and custom title and comment", as .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -183,7 +183,7 @@ test("Open a new issue with assignees and the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -256,7 +256,7 @@ test("Open a new issue without labels and the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -333,7 +333,7 @@ test("Update the first existing issue with the list of errors", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( diff --git a/test/integration.test.js b/test/integration.test.js index e7b2a343..030a7a54 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -30,7 +30,7 @@ test("Verify GitHub auth", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -63,7 +63,7 @@ test("Verify GitHub auth with publish options", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -103,7 +103,7 @@ test("Verify GitHub auth and assets config", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -209,7 +209,7 @@ test("Publish a release with an array of assets", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, @@ -304,7 +304,7 @@ test("Publish a release with release information in assets", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce( `https://api.github.local/repos/${owner}/${repo}/releases`, @@ -377,7 +377,7 @@ test("Update a release", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, @@ -444,7 +444,7 @@ test("Comment and add labels on PR included in the releases", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2, @@ -547,7 +547,7 @@ test("Open a new issue with the list of errors", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2 }, ) @@ -642,7 +642,7 @@ test("Verify, release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2, @@ -803,7 +803,7 @@ test("Verify, update release and notify success", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2, @@ -936,7 +936,7 @@ test("Verify and notify failure", async (t) => { { permissions: { push: true }, full_name: `${owner}/${repo}`, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }, { repeat: 2, diff --git a/test/success.test.js b/test/success.test.js index 83c202eb..6bdc20b5 100644 --- a/test/success.test.js +++ b/test/success.test.js @@ -57,7 +57,7 @@ test("Add comment and labels to PRs associated with release commits and issues s .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${redirectedOwner}/${redirectedRepo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -419,7 +419,7 @@ test("Make multiple search queries if necessary", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .post("https://api.github.local/graphql", { data: { @@ -664,7 +664,7 @@ test("Do not add comment and labels for unrelated PR returned by search (compare .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -767,7 +767,7 @@ test("Do not add comment and labels if no PR is associated with release commits" .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -830,7 +830,7 @@ test("Do not add comment and labels if no commits is found for release", async ( .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -890,7 +890,7 @@ test("Do not add comment and labels to PR/issues from other repo", async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -991,7 +991,7 @@ test("Ignore missing and forbidden issues/PRs", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1161,7 +1161,7 @@ test("Add custom comment and labels", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1257,7 +1257,7 @@ test("Add custom label", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1348,7 +1348,7 @@ test("Comment on issue/PR without ading a label", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1442,7 +1442,7 @@ test("Editing the release to include all release links at the bottom", async (t) .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1547,7 +1547,7 @@ test("Editing the release to include all release links at the top", async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1649,7 +1649,7 @@ test("Editing the release to include all release links with no additional releas .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1740,7 +1740,7 @@ test("Editing the release to include all release links with no additional releas .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1824,7 +1824,7 @@ test("Editing the release to include all release links with no releases", async .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -1910,7 +1910,7 @@ test("Editing the release with no ID in the release", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2001,7 +2001,7 @@ test("Ignore errors when adding comments and closing issues", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2135,7 +2135,7 @@ test("Close open issues when a release is successful", async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2236,7 +2236,7 @@ test('Skip commention on issues/PR if "successComment" is "false"', async (t) => .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .getOnce( `https://api.github.local/search/issues?q=${encodeURIComponent( @@ -2288,7 +2288,7 @@ test('Skip closing issues if "failComment" is "false"', async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { @@ -2340,7 +2340,7 @@ test('Skip closing issues if "failTitle" is "false"', async (t) => { .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { full_name: `${owner}/${repo}`, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .postOnce("https://api.github.local/graphql", { data: { diff --git a/test/verify.test.js b/test/verify.test.js index 29e8036f..37b3afb6 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -33,7 +33,7 @@ test("Verify package, token and repository access", async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -83,7 +83,7 @@ test('Verify package, token and repository access with "proxy", "asset", "discus permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -128,7 +128,7 @@ test("Verify package, token and repository access and custom URL with prefix", a permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -169,7 +169,7 @@ test("Verify package, token and repository access and custom URL without prefix" permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -210,7 +210,7 @@ test("Verify package, token and repository access and shorthand repositoryUrl UR permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -252,7 +252,7 @@ test("Verify package, token and repository with environment variables", async (t permissions: { push: true, }, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -296,7 +296,7 @@ test("Verify package, token and repository access with alternative environment v permissions: { push: true, }, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -333,7 +333,7 @@ test("Verify package, token and repository access with custom API URL", async (t permissions: { push: true, }, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -375,7 +375,7 @@ test("Verify package, token and repository access with API URL in environment va permissions: { push: true, }, - git_url: `htttps://api.github.local/${owner}/${repo}.git`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -411,7 +411,7 @@ test('Verify "proxy" is a String', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -446,7 +446,7 @@ test('Verify "proxy" is an object with "host" and "port" properties', async (t) permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -483,7 +483,7 @@ test('Verify "proxy" is a Boolean set to false', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -518,7 +518,7 @@ test('Verify "assets" is a String', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -553,7 +553,7 @@ test('Verify "assets" is an Object with a path property', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -588,7 +588,7 @@ test('Verify "assets" is an Array of Object with a path property', async (t) => permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -625,7 +625,7 @@ test('Verify "assets" is an Array of glob Arrays', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -660,7 +660,7 @@ test('Verify "assets" is an Array of Object with a glob Arrays in path property' permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -697,7 +697,7 @@ test('Verify "labels" is a String', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -732,7 +732,7 @@ test('Verify "assignees" is a String', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -767,7 +767,7 @@ test('Verify "addReleases" is a valid string (top)', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -802,7 +802,7 @@ test('Verify "addReleases" is a valid string (bottom)', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -837,7 +837,7 @@ test('Verify "addReleases" is valid (false)', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -872,7 +872,7 @@ test('Verify "draftRelease" is valid (true)', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -907,7 +907,7 @@ test('Verify "draftRelease" is valid (false)', async (t) => { permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -949,7 +949,7 @@ test("Verify if run in GitHub Action", async (t) => { const fetch = fetchMock .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); await t.notThrowsAsync( @@ -993,7 +993,7 @@ test("Verify if run in GitHub Action and repo is renamed", async (t) => { const fetch = fetchMock .sandbox() .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { - git_url: `https://api.github.local/${owner}/${repo}2.git`, + clone_url: `https://api.github.local/${owner}/${repo}2.git`, }); const { @@ -1034,7 +1034,7 @@ test("Verify if token is a Github installation token and repo is renamed", async permissions: { push: false, }, - git_url: `https://api.github.local/${owner}/${repo}2.git`, + clone_url: `https://api.github.local/${owner}/${repo}2.git`, }) .headOnce( "https://api.github.local/installation/repositories?per_page=1", @@ -1165,7 +1165,7 @@ test("Throw SemanticReleaseError if token doesn't have the push permission on th permissions: { push: false, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .headOnce( "https://api.github.local/installation/repositories?per_page=1", @@ -1208,7 +1208,7 @@ test("Do not throw SemanticReleaseError if token doesn't have the push permissio permissions: { push: false, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) .headOnce( "https://api.github.local/installation/repositories?per_page=1", @@ -1269,80 +1269,92 @@ test("Throw SemanticReleaseError if the repository doesn't exist", async (t) => t.true(fetch.done()); }); -test("Don't throw an error if git_url differs from repositoryUrl but owner/repo is the same", async (t) => { - const owner = "test_user"; - const repo = "test_repo"; - const env = { GH_TOKEN: "github_token" }; - - const fetch = fetchMock.sandbox().getOnce( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { push: true }, - git_url: `https://api.github.local/${owner}/${repo}.git`, - }, - { repeat: 2 }, - ); - - await t.notThrowsAsync( - verify( - {}, - { - env, - options: { - repositoryUrl: `git+https://github.com/${owner}/${repo}.git`, +const urlFormats = [ + (owner, repo) => `https://github.com/${owner}/${repo}.git`, + (owner, repo) => `git+https://github.com/${owner}/${repo}.git`, + (owner, repo) => `http://github.com/${owner}/${repo}.git`, + (owner, repo) => `git@github.com:${owner}/${repo}.git`, + (owner, repo) => `ssh://git@github.com/${owner}/${repo}.git`, + (owner, repo) => `git://github.com/${owner}/${repo}.git`, +]; + +for (const makeRepositoryUrl of urlFormats) { + for (const make_clone_url of urlFormats) { + const owner = "test_user"; + const repo = "test_repo"; + test(`Don't throw an error if clone_url differs from repositoryUrl but owner/repo is the same -- ${makeRepositoryUrl(owner, repo)} / ${make_clone_url(owner, repo)}`, async (t) => { + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + clone_url: make_clone_url(owner, repo), }, - logger: t.context.logger, - }, - { - Octokit: TestOctokit.defaults((options) => ({ - ...options, - request: { ...options.request, fetch }, - })), - }, - ), - ); - - t.true(fetch.done()); -}); - -test("Throw SemanticReleaseError if the repository is renamed", async (t) => { - const owner = "test_user"; - const repo = "test_repo"; - const env = { GH_TOKEN: "github_token" }; - - const fetch = fetchMock.sandbox().getOnce( - `https://api.github.local/repos/${owner}/${repo}`, - { - permissions: { push: true }, - git_url: `https://api.github.local/${owner}/${repo}2.git`, - }, - { repeat: 2 }, - ); + { repeat: 2 }, + ); + + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { + repositoryUrl: makeRepositoryUrl(owner, repo), + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); + }); - const { - errors: [error, ...errors], - } = await t.throwsAsync( - verify( - {}, - { - env, - options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, - logger: t.context.logger, - }, - { - Octokit: TestOctokit.defaults((options) => ({ - ...options, - request: { ...options.request, fetch }, - })), - }, - ), - ); + const repo2 = repo + "2"; + test(`Throw SemanticReleaseError if the repository is renamed -- ${makeRepositoryUrl(owner, repo)} / ${make_clone_url(owner, repo2)}`, async (t) => { + const env = { GH_TOKEN: "github_token" }; - t.is(errors.length, 0); - t.is(error.name, "SemanticReleaseError"); - t.is(error.code, "EMISMATCHGITHUBURL"); - t.true(fetch.done()); -}); + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + clone_url: make_clone_url(owner, repo2), + }, + { repeat: 2 }, + ); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: makeRepositoryUrl(owner, repo) }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); + }); + } +} test("Throw error if github return any other errors", async (t) => { const owner = "test_user"; @@ -1446,7 +1458,7 @@ test('Throw SemanticReleaseError if "assets" option is not a String or an Array permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1486,7 +1498,7 @@ test('Throw SemanticReleaseError if "assets" option is an Array with invalid ele permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1526,7 +1538,7 @@ test('Throw SemanticReleaseError if "assets" option is an Object missing the "pa permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1566,7 +1578,7 @@ test('Throw SemanticReleaseError if "assets" option is an Array with objects mis permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1606,7 +1618,7 @@ test('Throw SemanticReleaseError if "successComment" option is not a String', as permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1646,7 +1658,7 @@ test('Throw SemanticReleaseError if "successComment" option is an empty String', permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1686,7 +1698,7 @@ test('Throw SemanticReleaseError if "successComment" option is a whitespace Stri permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1726,7 +1738,7 @@ test('Throw SemanticReleaseError if "failTitle" option is not a String', async ( permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1766,7 +1778,7 @@ test('Throw SemanticReleaseError if "failTitle" option is an empty String', asyn permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1806,7 +1818,7 @@ test('Throw SemanticReleaseError if "failTitle" option is a whitespace String', permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1846,7 +1858,7 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is not a Str permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1886,7 +1898,7 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is an empty permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1926,7 +1938,7 @@ test('Throw SemanticReleaseError if "discussionCategoryName" option is a whitesp permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -1966,7 +1978,7 @@ test('Throw SemanticReleaseError if "failComment" option is not a String', async permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2006,7 +2018,7 @@ test('Throw SemanticReleaseError if "failComment" option is an empty String', as permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2046,7 +2058,7 @@ test('Throw SemanticReleaseError if "failComment" option is a whitespace String' permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2086,7 +2098,7 @@ test('Throw SemanticReleaseError if "labels" option is not a String or an Array permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2126,7 +2138,7 @@ test('Throw SemanticReleaseError if "labels" option is an Array with invalid ele permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2166,7 +2178,7 @@ test('Throw SemanticReleaseError if "labels" option is a whitespace String', asy permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2206,7 +2218,7 @@ test('Throw SemanticReleaseError if "assignees" option is not a String or an Arr permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2246,7 +2258,7 @@ test('Throw SemanticReleaseError if "assignees" option is an Array with invalid permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2286,7 +2298,7 @@ test('Throw SemanticReleaseError if "assignees" option is a whitespace String', permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2326,7 +2338,7 @@ test('Throw SemanticReleaseError if "releasedLabels" option is not a String or a permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2366,7 +2378,7 @@ test('Throw SemanticReleaseError if "releasedLabels" option is an Array with inv permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2406,7 +2418,7 @@ test('Throw SemanticReleaseError if "releasedLabels" option is a whitespace Stri permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2446,7 +2458,7 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2486,7 +2498,7 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2526,7 +2538,7 @@ test('Throw SemanticReleaseError if "addReleases" option is not a valid string ( permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2566,7 +2578,7 @@ test('Throw SemanticReleaseError if "draftRelease" option is not a valid boolean permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2605,7 +2617,7 @@ test('Throw SemanticReleaseError if "releaseBodyTemplate" option is an empty str permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const { @@ -2644,7 +2656,7 @@ test('Throw SemanticReleaseError if "releaseNameTemplate" option is an empty str permissions: { push: true, }, - git_url: `https://api.github.local/${owner}/${repo}.git`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }); const {