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.
499 lines
18 KiB
JavaScript
499 lines
18 KiB
JavaScript
import { existsSync } from "node:fs";
|
|
import path from "node:path";
|
|
import test from "ava";
|
|
import { temporaryDirectory } from "tempy";
|
|
import {
|
|
addNote,
|
|
fetch,
|
|
fetchNotes,
|
|
getBranches,
|
|
getGitHead,
|
|
getTagHead,
|
|
getTags,
|
|
getTagsNotes,
|
|
isBranchUpToDate,
|
|
isGitRepo,
|
|
isRefExists,
|
|
push,
|
|
repoUrl,
|
|
tag,
|
|
verifyAuth,
|
|
verifyTagName,
|
|
} from "../lib/git.js";
|
|
import {
|
|
gitAddConfig,
|
|
gitAddNote,
|
|
gitCheckout,
|
|
gitCommits,
|
|
gitCommitTag,
|
|
gitDetachedHead,
|
|
gitDetachedHeadFromBranch,
|
|
gitFetch,
|
|
gitGetCommits,
|
|
gitGetNote,
|
|
gitPush,
|
|
gitRemoteTagHead,
|
|
gitRepo,
|
|
gitShallowClone,
|
|
gitTagVersion,
|
|
initGit,
|
|
} from "./helpers/git-utils.js";
|
|
|
|
test("Get the last commit sha", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
const result = await getGitHead({ cwd });
|
|
|
|
t.is(result, commits[0].hash);
|
|
});
|
|
|
|
test("Throw error if the last commit sha cannot be found", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
|
|
await t.throwsAsync(getGitHead({ cwd }));
|
|
});
|
|
|
|
test("Unshallow and fetch repository", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
// Add commits to the master branch
|
|
await gitCommits(["First", "Second"], { cwd });
|
|
// Create a shallow clone with only 1 commit
|
|
cwd = await gitShallowClone(repositoryUrl);
|
|
|
|
// Verify the shallow clone contains only one commit
|
|
t.is((await gitGetCommits(undefined, { cwd })).length, 1);
|
|
|
|
await fetch(repositoryUrl, "master", "master", { cwd });
|
|
|
|
// Verify the shallow clone contains all the commits
|
|
t.is((await gitGetCommits(undefined, { cwd })).length, 2);
|
|
});
|
|
|
|
test("Do not throw error when unshallow a complete repository", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
await gitCheckout("second-branch", true, { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "second-branch", { cwd });
|
|
|
|
await t.notThrowsAsync(fetch(repositoryUrl, "master", "master", { cwd }));
|
|
await t.notThrowsAsync(fetch(repositoryUrl, "second-branch", "master", { cwd }));
|
|
});
|
|
|
|
test("Fetch all tags on a detached head repository", async (t) => {
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitTagVersion("v1.0.1", undefined, { cwd });
|
|
const [commit] = await gitCommits(["Third"], { cwd });
|
|
await gitTagVersion("v1.1.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
|
|
|
|
await fetch(repositoryUrl, "master", "master", { cwd });
|
|
|
|
t.deepEqual((await getTags("master", { cwd })).sort(), ["v1.0.0", "v1.0.1", "v1.1.0"].sort());
|
|
});
|
|
|
|
test("Fetch all tags on a repository with a detached head from branch (CircleCI)", async (t) => {
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitTagVersion("v1.0.1", undefined, { cwd });
|
|
const [commit] = await gitCommits(["Third"], { cwd });
|
|
await gitTagVersion("v1.1.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
await gitCheckout("other-branch", true, { cwd });
|
|
await gitPush(repositoryUrl, "other-branch", { cwd });
|
|
await gitCheckout("master", false, { cwd });
|
|
await gitCommits(["Fourth"], { cwd });
|
|
await gitTagVersion("v2.0.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
cwd = await gitDetachedHeadFromBranch(repositoryUrl, "other-branch", commit.hash);
|
|
|
|
await fetch(repositoryUrl, "master", "other-branch", { cwd });
|
|
await fetch(repositoryUrl, "other-branch", "other-branch", { cwd });
|
|
|
|
t.deepEqual((await getTags("other-branch", { cwd })).sort(), ["v1.0.0", "v1.0.1", "v1.1.0"].sort());
|
|
t.deepEqual((await getTags("master", { cwd })).sort(), ["v1.0.0", "v1.0.1", "v1.1.0", "v2.0.0"].sort());
|
|
});
|
|
|
|
test("Fetch all tags on a detached head repository with outdated cached repo (GitLab CI)", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo();
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitTagVersion("v1.0.1", undefined, { cwd });
|
|
let [commit] = await gitCommits(["Third"], { cwd });
|
|
await gitTagVersion("v1.1.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
// Create a clone (as first CI run would)
|
|
const cloneCwd = await gitShallowClone(repositoryUrl);
|
|
await gitFetch(repositoryUrl, { cwd: cloneCwd });
|
|
await gitCheckout(commit.hash, false, { cwd: cloneCwd });
|
|
|
|
// Push tag to remote
|
|
[commit] = await gitCommits(["Fourth"], { cwd });
|
|
await gitTagVersion("v1.2.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
// Fetch on the cached repo and make detached head, leaving master outdated
|
|
await fetch(repositoryUrl, "master", "master", { cwd: cloneCwd });
|
|
await gitCheckout(commit.hash, false, { cwd: cloneCwd });
|
|
|
|
t.deepEqual((await getTags("master", { cwd: cloneCwd })).sort(), ["v1.0.0", "v1.0.1", "v1.1.0", "v1.2.0"].sort());
|
|
});
|
|
|
|
test("Verify if a branch exists", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
await gitCommits(["First"], { cwd });
|
|
// Create the new branch 'other-branch' from master
|
|
await gitCheckout("other-branch", true, { cwd });
|
|
// Add commits to the 'other-branch' branch
|
|
await gitCommits(["Second"], { cwd });
|
|
|
|
t.true(await isRefExists("master", { cwd }));
|
|
t.true(await isRefExists("other-branch", { cwd }));
|
|
t.falsy(await isRefExists("next", { cwd }));
|
|
});
|
|
|
|
test("Get all branches", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
await gitCheckout("second-branch", true, { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "second-branch", { cwd });
|
|
await gitCheckout("third-branch", true, { cwd });
|
|
await gitCommits(["Third"], { cwd });
|
|
await gitPush(repositoryUrl, "third-branch", { cwd });
|
|
|
|
t.deepEqual((await getBranches(repositoryUrl, { cwd })).sort(), ["master", "second-branch", "third-branch"].sort());
|
|
});
|
|
|
|
test("Return empty array if there are no branches", async (t) => {
|
|
const { cwd, repositoryUrl } = await initGit(true);
|
|
t.deepEqual(await getBranches(repositoryUrl, { cwd }), []);
|
|
});
|
|
|
|
test("Get the commit sha for a given tag", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
// Create the tag corresponding to version 1.0.0
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
|
|
t.is(await getTagHead("v1.0.0", { cwd }), commits[0].hash);
|
|
});
|
|
|
|
test("Verify auth succeeds when the remote accepts the dry-run push and tag push", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
await t.notThrowsAsync(verifyAuth(repositoryUrl, { cwd }));
|
|
});
|
|
|
|
test("Verify auth ignores non-fast-forward and rejected push errors", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
const staleRepo = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
await t.notThrowsAsync(verifyAuth(repositoryUrl, { cwd: staleRepo }));
|
|
});
|
|
|
|
test("Return git remote repository url from config", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add remote.origin.url config
|
|
await gitAddConfig("remote.origin.url", "git@hostname.com:owner/package.git", { cwd });
|
|
|
|
t.is(await repoUrl({ cwd }), "git@hostname.com:owner/package.git");
|
|
});
|
|
|
|
test("Return git remote repository url set while cloning", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
await gitCommits(["First"], { cwd });
|
|
// Create a clone
|
|
cwd = await gitShallowClone(repositoryUrl);
|
|
|
|
t.is(await repoUrl({ cwd }), repositoryUrl);
|
|
});
|
|
|
|
test("Return falsy if git repository url is not set", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
|
|
t.falsy(await repoUrl({ cwd }));
|
|
});
|
|
|
|
test("Add tag on head commit", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
const commits = await gitCommits(["Test commit"], { cwd });
|
|
|
|
await tag("tag_name", "HEAD", { cwd });
|
|
|
|
await t.is(await gitCommitTag(commits[0].hash, { cwd }), "tag_name");
|
|
});
|
|
|
|
test("Push tag to remote repository", async (t) => {
|
|
// Create a git repository with a remote, set the current working directory at the root of the repo
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
const commits = await gitCommits(["Test commit"], { cwd });
|
|
|
|
await tag("tag_name", "HEAD", { cwd });
|
|
await push(repositoryUrl, { cwd });
|
|
|
|
t.is(await gitRemoteTagHead(repositoryUrl, "tag_name", { cwd }), commits[0].hash);
|
|
});
|
|
|
|
test("Push tag to remote repository with remote branch ahead", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
const temporaryRepo = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Second"], { cwd: temporaryRepo });
|
|
await gitPush("origin", "master", { cwd: temporaryRepo });
|
|
|
|
await tag("tag_name", "HEAD", { cwd });
|
|
await push(repositoryUrl, { cwd });
|
|
|
|
t.is(await gitRemoteTagHead(repositoryUrl, "tag_name", { cwd }), commits[0].hash);
|
|
});
|
|
|
|
test('Return "true" if in a Git repository', async (t) => {
|
|
// Create a git repository with a remote, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo(true);
|
|
|
|
t.true(await isGitRepo({ cwd }));
|
|
});
|
|
|
|
test("Return falsy if not in a Git repository", async (t) => {
|
|
const cwd = temporaryDirectory();
|
|
|
|
t.falsy(await isGitRepo({ cwd }));
|
|
});
|
|
|
|
test('Return "true" for valid tag names', async (t) => {
|
|
t.true(await verifyTagName("1.0.0"));
|
|
t.true(await verifyTagName("v1.0.0"));
|
|
t.true(await verifyTagName("tag_name"));
|
|
t.true(await verifyTagName("tag/name"));
|
|
});
|
|
|
|
test("Return falsy for invalid tag names", async (t) => {
|
|
t.falsy(await verifyTagName("?1.0.0"));
|
|
t.falsy(await verifyTagName("*1.0.0"));
|
|
t.falsy(await verifyTagName("[1.0.0]"));
|
|
t.falsy(await verifyTagName("1.0.0.."));
|
|
});
|
|
|
|
test("Throws error if obtaining the tags fails", async (t) => {
|
|
const cwd = temporaryDirectory();
|
|
|
|
await t.throwsAsync(getTags("master", { cwd }));
|
|
});
|
|
|
|
test('Return "true" if repository is up to date', async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
t.true(await isBranchUpToDate(repositoryUrl, "master", { cwd }));
|
|
});
|
|
|
|
test("Return true if local repository is ahead of remote", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
const localCwd = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Third"], { cwd: localCwd });
|
|
|
|
t.true(await isBranchUpToDate(repositoryUrl, "master", { cwd: localCwd }));
|
|
});
|
|
|
|
test("Return falsy if local repository is behind remote", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
const localCwd = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Second", "Third"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
t.falsy(await isBranchUpToDate(repositoryUrl, "master", { cwd: localCwd }));
|
|
});
|
|
|
|
test("Return falsy if local and remote repository are diverged i.e. both have commits the other doesn't", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
const localCwd = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Second", "Third"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
await gitCommits(["Fourth"], { cwd: localCwd });
|
|
|
|
t.falsy(await isBranchUpToDate(repositoryUrl, "master", { cwd: localCwd }));
|
|
});
|
|
|
|
test("Return falsy if detached head repository is not up to date", async (t) => {
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
|
|
const [commit] = await gitCommits(["First"], { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
|
|
await fetch(repositoryUrl, "master", "master", { cwd });
|
|
|
|
t.falsy(await isBranchUpToDate(repositoryUrl, "master", { cwd }));
|
|
});
|
|
|
|
test("Get a commit note", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
|
|
await gitAddNote(JSON.stringify({ note: "note" }), "v1.0.0", { cwd });
|
|
const tagsNotes = await getTagsNotes({ cwd });
|
|
|
|
t.deepEqual(tagsNotes.get("v1.0.0"), { note: "note" });
|
|
});
|
|
|
|
test("Return undefined if there is no commit note", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
|
|
const tagsNotes = await getTagsNotes({ cwd });
|
|
|
|
t.deepEqual(tagsNotes.get("v1.0.0"), undefined);
|
|
});
|
|
|
|
test("Return undefined if a commit note is invalid", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
await gitCommits(["First"], { cwd });
|
|
await gitTagVersion("v1.0.0", undefined, { cwd });
|
|
|
|
await gitAddNote("non-json note", "v1.0.0", { cwd });
|
|
|
|
const tagsNotes = await getTagsNotes({ cwd });
|
|
|
|
t.deepEqual(tagsNotes.get("v1.0.0"), undefined);
|
|
});
|
|
|
|
test("Add a commit note", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
await addNote({ note: "note" }, commits[0].hash, { cwd });
|
|
|
|
t.is(await gitGetNote(commits[0].hash, { cwd }), '{"note":"note"}');
|
|
});
|
|
|
|
test("Overwrite a commit note", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
const { cwd } = await gitRepo();
|
|
// Add commits to the master branch
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
await addNote({ note: "note" }, commits[0].hash, { cwd });
|
|
await addNote({ note: "note2" }, commits[0].hash, { cwd });
|
|
|
|
t.is(await gitGetNote(commits[0].hash, { cwd }), '{"note":"note2"}');
|
|
});
|
|
|
|
test("Unshallow and fetch repository with notes", async (t) => {
|
|
// Create a git repository, set the current working directory at the root of the repo
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
// Add commits to the master branch
|
|
const commits = await gitCommits(["First", "Second"], { cwd });
|
|
await gitAddNote(JSON.stringify({ note: "note" }), commits[0].hash, { cwd });
|
|
// Create a shallow clone with only 1 commit
|
|
cwd = await gitShallowClone(repositoryUrl);
|
|
|
|
// Verify the shallow clone doesn't contains the note
|
|
await t.throwsAsync(gitGetNote(commits[0].hash, { cwd }));
|
|
|
|
await fetch(repositoryUrl, "master", "master", { cwd });
|
|
await fetchNotes(repositoryUrl, { cwd });
|
|
|
|
// Verify the shallow clone contains the note
|
|
t.is(await gitGetNote(commits[0].hash, { cwd }), '{"note":"note"}');
|
|
});
|
|
|
|
test("Fetch all notes on a detached head repository", async (t) => {
|
|
let { cwd, repositoryUrl } = await gitRepo();
|
|
|
|
await gitCommits(["First"], { cwd });
|
|
const [commit] = await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
await gitAddNote(JSON.stringify({ note: "note" }), commit.hash, { cwd });
|
|
cwd = await gitDetachedHead(repositoryUrl, commit.hash);
|
|
|
|
await fetch(repositoryUrl, "master", "master", { cwd });
|
|
await fetchNotes(repositoryUrl, { cwd });
|
|
|
|
t.is(await gitGetNote(commit.hash, { cwd }), '{"note":"note"}');
|
|
});
|
|
|
|
test("Does not execute a `repositoryUrl` injected as an `--upload-pack` git option", async (t) => {
|
|
const { cwd } = await gitRepo();
|
|
await gitCommits(["First"], { cwd });
|
|
const marker = path.join(temporaryDirectory(), "upload-pack-rce");
|
|
// A `repositoryUrl` beginning with `-` would be parsed by git as a
|
|
// command-line option rather than a positional argument. `--upload-pack`
|
|
// lets the injected value run an arbitrary binary when fetching refs.
|
|
const repositoryUrl = `--upload-pack=touch ${marker}`;
|
|
|
|
await t.throwsAsync(isBranchUpToDate(repositoryUrl, "master", { cwd }));
|
|
|
|
t.false(existsSync(marker));
|
|
});
|
|
|
|
test("Does not execute a `repositoryUrl` injected as a `--receive-pack` git option", async (t) => {
|
|
const { cwd } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
const marker = path.join(temporaryDirectory(), "receive-pack-rce");
|
|
// `--receive-pack` is the push-side equivalent: it lets the injected value
|
|
// run an arbitrary binary when pushing to the (configured) remote.
|
|
const repositoryUrl = `--receive-pack=touch ${marker}`;
|
|
|
|
await t.throwsAsync(push(repositoryUrl, { cwd }));
|
|
|
|
t.false(existsSync(marker));
|
|
});
|