mirror of
https://github.com/semantic-release/semantic-release.git
synced 2026-10-25 15:39:35 +00:00
3e2a07107d
This PR refactors the `verifyAuth` function replacing the normal push
dry-run (which could result in a branch outdated error/notice) with a
tags dry-run push. It does this by separating the push permission check
operation in the `verifyAuth` logic from the `isBranchUpdated` logic
that lived in thesame execution block via a `try-catch` into standalone
operations; this branch status check only occured when the push
permission check fails.
### Changes Made
- Modify `verifyAuth` logic to do a tags dry run instead of a branch
dry-run push
- Separate `verifyAuth` to a standalone block to throw a
`"EGITNOPERMISSION"` if no push permission is found
- Improved the `isBranchUpdated` helper to ensure it checks for cases
where local branch is either behind, ahead or diverged of head remote
branch and return appropriate boolean response which in this case is...
- `false` - when local branch is behind remote branch
- `true` - when local branch is ahead of remote branch
- `false` - when local branch and remote branch have commits that the
other doesn't
- Make the branch up to date check a standalone process in the release
verification process returning `false` when branch is considered not
up-to-date - with a message
```js
if (!(await isBranchUpToDate(options.repositoryUrl, context.branch.name,
{ cwd, env }))) {
logger.log(
`The local branch ${context.branch.name} is behind the remote one,
therefore a new version won't be published.`
);
return false;
}
```
### Related Issue
Fixes #3437
BREAKING CHANGE: `semantic-release` now checks if your local branch is
up-to-date with your remote repo branch after of push permission
verification.
443 lines
12 KiB
JavaScript
443 lines
12 KiB
JavaScript
import test from "ava";
|
|
import getAuthUrl from "../lib/get-git-auth-url.js";
|
|
import { gitRepo } from "./helpers/git-utils.js";
|
|
|
|
const env = { GIT_ASKPASS: "echo", GIT_TERMINAL_PROMPT: 0 };
|
|
|
|
test('Return the same "git" formatted URL if "gitCredentials" is not defined', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"git@host.null:owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the same "https" formatted URL if "gitCredentials" is not defined', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "https://host.null/owner/repo.git" },
|
|
}),
|
|
"https://host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the same "file" URL if "gitCredentials" is not defined', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "file:///tmp/owner/repo.git" },
|
|
}),
|
|
"file:///tmp/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the same "file" URL even if credentials are present', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "file:///tmp/owner/repo.git" },
|
|
}),
|
|
"file:///tmp/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is not defined and repositoryUrl is a "git+https" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git+https://host.null/owner/repo.git" },
|
|
}),
|
|
"https://host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Do not add trailing ".git" if not present in the origian URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({ cwd, env, vranch: { name: "master" }, options: { repositoryUrl: "git@host.null:owner/repo" } }),
|
|
"git@host.null:owner/repo"
|
|
);
|
|
});
|
|
|
|
test('Handle "https" URL with group and subgroup', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "https://host.null/group/subgroup/owner/repo.git" },
|
|
}),
|
|
"https://host.null/group/subgroup/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Handle "git" URL with group and subgroup', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:group/subgroup/owner/repo.git" },
|
|
}),
|
|
"git@host.null:group/subgroup/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test("Convert shorthand URL", async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "semantic-release/semantic-release" },
|
|
}),
|
|
"https://github.com/semantic-release/semantic-release.git"
|
|
);
|
|
});
|
|
|
|
test("Convert GitLab shorthand URL", async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env,
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "gitlab:semantic-release/semantic-release" },
|
|
}),
|
|
"https://gitlab.com/semantic-release/semantic-release.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
options: { branch: "master", repositoryUrl: "host.null:owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user and with a custom port', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
options: { branch: "master", repositoryUrl: "host.null:6666:owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null:6666/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git" URL without user and with a custom port followed by a slash', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
options: { branch: "master", repositoryUrl: "host.null:6666:/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null:6666/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "https" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "https://host.null/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "http://host.null/owner/repo.git" },
|
|
}),
|
|
"http://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "http" URL with custom port', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
options: { branch: "master", repositoryUrl: "http://host.null:8080/owner/repo.git" },
|
|
}),
|
|
"http://user:pass@host.null:8080/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+https" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git+https://host.null/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "git+http" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git+http://host.null/owner/repo.git" },
|
|
}),
|
|
"http://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "http" formatted URL if "gitCredentials" is defined and repositoryUrl is a "ssh" URL', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
options: { branch: "master", repositoryUrl: "ssh://git@host.null:2222/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "GH_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GH_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITHUB_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GITHUB_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "GL_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GL_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://gitlab-ci-token:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "GITLAB_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GITLAB_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://gitlab-ci-token:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, BB_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://x-token-auth:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "BITBUCKET_TOKEN"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, BITBUCKET_TOKEN: "token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://x-token-auth:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "BB_TOKEN_BASIC_AUTH"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, BB_TOKEN_BASIC_AUTH: "username:token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://username:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "gitCredentials" is defined with "BITBUCKET_TOKEN_BASIC_AUTH"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, BITBUCKET_TOKEN_BASIC_AUTH: "username:token" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://username:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Return the "https" formatted URL if "GITHUB_ACTION" is set', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GITHUB_ACTION: "foo", GITHUB_TOKEN: "token" },
|
|
options: { branch: "master", repositoryUrl: "git@host.null:owner/repo.git" },
|
|
}),
|
|
"https://x-access-token:token@host.null/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Handle "https" URL with group and subgroup, with "GIT_CREDENTIALS"', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "https://host.null/group/subgroup/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/group/subgroup/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test('Handle "git" URL with group and subgroup, with "GIT_CREDENTIALS', async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl: "git@host.null:group/subgroup/owner/repo.git" },
|
|
}),
|
|
"https://user:pass@host.null/group/subgroup/owner/repo.git"
|
|
);
|
|
});
|
|
|
|
test("Do not add git credential to repositoryUrl if push is allowed", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
|
|
t.is(
|
|
await getAuthUrl({
|
|
cwd,
|
|
env: { ...env, GIT_CREDENTIALS: "user:pass" },
|
|
branch: { name: "master" },
|
|
options: { repositoryUrl },
|
|
}),
|
|
repositoryUrl
|
|
);
|
|
});
|