fix: fetch tags on repo cached by the CI

This commit is contained in:
Pierre Vanduynslager 2020-01-14 18:22:39 -05:00
parent 28b54800cd
commit 6b5b02ea75
3 changed files with 52 additions and 6 deletions

View File

@ -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} repositoryUrl The remote repository URL.
* @param {String} branch The repository branch to fetch. * @param {String} branch The repository branch to fetch.
* @param {Object} [execaOpts] Options to pass to `execa`. * @param {Object} [execaOpts] Options to pass to `execa`.
*/ */
async function fetch(repositoryUrl, branch, ciBranch, execaOpts) { async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
const isLocalExists = const isDetachedHead =
(await execa('git', ['rev-parse', '--verify', branch], {...execaOpts, reject: false})).exitCode === 0; (await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {...execaOpts, reject: false})).stdout === 'HEAD';
try { try {
await execa( await execa(
@ -103,7 +110,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
'fetch', 'fetch',
'--unshallow', '--unshallow',
'--tags', '--tags',
...(branch === ciBranch && isLocalExists ...(branch === ciBranch && !isDetachedHead
? [repositoryUrl] ? [repositoryUrl]
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]), : ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
], ],
@ -115,7 +122,7 @@ async function fetch(repositoryUrl, branch, ciBranch, execaOpts) {
[ [
'fetch', 'fetch',
'--tags', '--tags',
...(branch === ciBranch && isLocalExists ...(branch === ciBranch && !isDetachedHead
? [repositoryUrl] ? [repositoryUrl]
: ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]), : ['--update-head-ok', repositoryUrl, `+refs/heads/${branch}:refs/heads/${branch}`]),
], ],

View File

@ -32,6 +32,7 @@ import {
gitDetachedHeadFromBranch, gitDetachedHeadFromBranch,
gitAddNote, gitAddNote,
gitGetNote, gitGetNote,
gitFetch,
} from './helpers/git-utils'; } from './helpers/git-utils';
test('Get the last commit sha', async t => { test('Get the last commit sha', async t => {
@ -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()); 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(); let {cwd, repositoryUrl} = await gitRepo();
await gitCommits(['First'], {cwd}); await gitCommits(['First'], {cwd});
@ -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()); 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 => { test('Verify if a branch exists', async t => {
// Create a git repository, set the current working directory at the root of the repo // Create a git repository, set the current working directory at the root of the repo
const {cwd} = await gitRepo(); const {cwd} = await gitRepo();

View File

@ -109,6 +109,16 @@ export async function gitCheckout(branch, create, execaOpts) {
await execa('git', create ? ['checkout', '-b', branch] : ['checkout', branch], 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. * Get the HEAD sha.
* *