fix: fetch tags on repo cached by the CI
This commit is contained in:
		
							parent
							
								
									28b54800cd
								
							
						
					
					
						commit
						6b5b02ea75
					
				
							
								
								
									
										17
									
								
								lib/git.js
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								lib/git.js
									
									
									
									
									
								
							| @ -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}`]), | ||||||
|       ], |       ], | ||||||
|  | |||||||
| @ -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(); | ||||||
|  | |||||||
| @ -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. | ||||||
|  * |  * | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user