418 lines
15 KiB
JavaScript
418 lines
15 KiB
JavaScript
import test from "ava";
|
|
import { temporaryDirectory } from "tempy";
|
|
import {
|
|
addNote,
|
|
fetch,
|
|
fetchNotes,
|
|
getBranches,
|
|
getGitHead,
|
|
getNote,
|
|
getTagHead,
|
|
getTags,
|
|
isBranchUpToDate,
|
|
isGitRepo,
|
|
isRefExists,
|
|
push,
|
|
repoUrl,
|
|
tag,
|
|
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("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 falsy if repository is not up to date", async (t) => {
|
|
const { cwd, repositoryUrl } = await gitRepo(true);
|
|
await gitCommits(["First"], { cwd });
|
|
await gitCommits(["Second"], { cwd });
|
|
await gitPush(repositoryUrl, "master", { cwd });
|
|
|
|
t.true(await isBranchUpToDate(repositoryUrl, "master", { cwd }));
|
|
|
|
const temporaryRepo = await gitShallowClone(repositoryUrl);
|
|
await gitCommits(["Third"], { cwd: temporaryRepo });
|
|
await gitPush("origin", "master", { cwd: temporaryRepo });
|
|
|
|
t.falsy(await isBranchUpToDate(repositoryUrl, "master", { cwd }));
|
|
});
|
|
|
|
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
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
await gitAddNote(JSON.stringify({ note: "note" }), commits[0].hash, { cwd });
|
|
|
|
t.deepEqual(await getNote(commits[0].hash, { cwd }), { note: "note" });
|
|
});
|
|
|
|
test("Return empty object 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
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
t.deepEqual(await getNote(commits[0].hash, { cwd }), {});
|
|
});
|
|
|
|
test("Throw error if a commit note in 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
|
|
const commits = await gitCommits(["First"], { cwd });
|
|
|
|
await gitAddNote("non-json note", commits[0].hash, { cwd });
|
|
|
|
await t.throwsAsync(getNote(commits[0].hash, { cwd }));
|
|
});
|
|
|
|
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"}');
|
|
});
|