Skip to content

Commit

Permalink
fix: fetch tags on repo cached by the CI
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdlg committed Jan 15, 2020
1 parent 28b5480 commit 6b5b02e
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
17 changes: 12 additions & 5 deletions lib/git.js
Expand Up @@ -86,15 +86,22 @@ async function isRefExists(ref, execaOpts) {
}

/**
* Unshallow the git repository if necessary and fetch all the tags.
* Fetch all the tags from a branch. Unshallow if necessary.
* This will update the local branch from the latest on the remote if:
* - The branch is not the one that triggered the CI
* - The CI created a detached head
*
* Otherwise it just calls `git fetch` without specifying the `refspec` option to avoid overwritting the head commit set by the CI.
*
* The goal is to retrieve the informations on all the release branches without "disturbing" the CI, leaving the trigger branch or the detached head intact.
*
* @param {String} repositoryUrl The remote repository URL.
* @param {String} branch The repository branch to fetch.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
const isLocalExists =
(await execa('git', ['rev-parse', '--verify', branch], {...execaOpts, reject: false})).exitCode === 0;
const isDetachedHead =
(await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {...execaOpts, reject: false})).stdout === 'HEAD';

try {
await execa(
Expand All @@ -103,7 +110,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
'fetch',
'--unshallow',
'--tags',
...(branch === ciBranch && isLocalExists
...(branch === ciBranch && !isDetachedHead
? [repositoryUrl]
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
],
Expand All @@ -115,7 +122,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
[
'fetch',
'--tags',
...(branch === ciBranch && isLocalExists
...(branch === ciBranch && !isDetachedHead
? [repositoryUrl]
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
],
Expand Down
31 changes: 30 additions & 1 deletion test/git.test.js
Expand Up @@ -32,6 +32,7 @@ import {
gitDetachedHeadFromBranch,
gitAddNote,
gitGetNote,
gitFetch,
} from './helpers/git-utils';

test('Get the last commit sha', async t => {
Expand Down Expand Up @@ -99,7 +100,7 @@ test('Fetch all tags on a detached head repository', async t => {
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', async t => {
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});
Expand All @@ -124,6 +125,34 @@ test('Fetch all tags on a repository with a detached head from branch', async t
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();
Expand Down
10 changes: 10 additions & 0 deletions test/helpers/git-utils.js
Expand Up @@ -109,6 +109,16 @@ export async function gitCheckout(branch, create, execaOpts) {
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], execaOpts);
}

/**
* Fetch current git repository.
*
* @param {String} repositoryUrl The repository remote URL.
* @param {Object} [execaOpts] Options to pass to `execa`.
*/
export async function gitFetch(repositoryUrl, execaOpts) {
await execa('git', ['fetch', repositoryUrl], execaOpts);
}

/**
* Get the HEAD sha.
*
Expand Down

0 comments on commit 6b5b02e

Please sign in to comment.