fix: fetch all release branches on CI
This commit is contained in:
		
							parent
							
								
									e63e753cf0
								
							
						
					
					
						commit
						b729183b4a
					
				
							
								
								
									
										4
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								index.js
									
									
									
									
									
								
							| @ -18,7 +18,7 @@ const {extractErrors, makeTag} = require('./lib/utils'); | |||||||
| const getGitAuthUrl = require('./lib/get-git-auth-url'); | const getGitAuthUrl = require('./lib/get-git-auth-url'); | ||||||
| const getBranches = require('./lib/branches'); | const getBranches = require('./lib/branches'); | ||||||
| const getLogger = require('./lib/get-logger'); | const getLogger = require('./lib/get-logger'); | ||||||
| const {fetch, verifyAuth, isBranchUpToDate, getGitHead, tag, push} = require('./lib/git'); | const {verifyAuth, isBranchUpToDate, getGitHead, tag, push} = require('./lib/git'); | ||||||
| const getError = require('./lib/get-error'); | const getError = require('./lib/get-error'); | ||||||
| const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants'); | const {COMMIT_NAME, COMMIT_EMAIL} = require('./lib/definitions/constants'); | ||||||
| 
 | 
 | ||||||
| @ -53,8 +53,6 @@ async function run(context, plugins) { | |||||||
|   // Verify config
 |   // Verify config
 | ||||||
|   await verify(context); |   await verify(context); | ||||||
| 
 | 
 | ||||||
|   await fetch({cwd, env}); |  | ||||||
| 
 |  | ||||||
|   context.branches = await getBranches(context); |   context.branches = await getBranches(context); | ||||||
|   context.branch = context.branches.find(({name}) => name === ciBranch); |   context.branch = context.branches.find(({name}) => name === ciBranch); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,20 +3,25 @@ const AggregateError = require('aggregate-error'); | |||||||
| const pEachSeries = require('p-each-series'); | const pEachSeries = require('p-each-series'); | ||||||
| const DEFINITIONS = require('../definitions/branches'); | const DEFINITIONS = require('../definitions/branches'); | ||||||
| const getError = require('../get-error'); | const getError = require('../get-error'); | ||||||
| const {verifyBranchName} = require('../git'); | const {fetch, verifyBranchName} = require('../git'); | ||||||
| const expand = require('./expand'); | const expand = require('./expand'); | ||||||
| const getTags = require('./get-tags'); | const getTags = require('./get-tags'); | ||||||
| const normalize = require('./normalize'); | const normalize = require('./normalize'); | ||||||
| 
 | 
 | ||||||
| module.exports = async context => { | module.exports = async context => { | ||||||
|   const branches = await getTags( |   const {cwd, env} = context; | ||||||
|  | 
 | ||||||
|  |   const remoteBranches = await expand( | ||||||
|     context, |     context, | ||||||
|     await expand( |     context.options.branches.map(branch => (isString(branch) || isRegExp(branch) ? {name: branch} : branch)) | ||||||
|       context, |  | ||||||
|       context.options.branches.map(branch => (isString(branch) || isRegExp(branch) ? {name: branch} : branch)) |  | ||||||
|     ) |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  |   await pEachSeries(remoteBranches, async ({name}) => { | ||||||
|  |     await fetch(name, {cwd, env}); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   const branches = await getTags(context, remoteBranches); | ||||||
|  | 
 | ||||||
|   const errors = []; |   const errors = []; | ||||||
|   const branchesByType = Object.entries(DEFINITIONS).reduce( |   const branchesByType = Object.entries(DEFINITIONS).reduce( | ||||||
|     (branchesByType, [type, {filter}]) => ({[type]: branches.filter(filter), ...branchesByType}), |     (branchesByType, [type, {filter}]) => ({[type]: branches.filter(filter), ...branchesByType}), | ||||||
|  | |||||||
							
								
								
									
										31
									
								
								lib/git.js
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								lib/git.js
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| const {trimStart, matches, pick, memoize} = require('lodash'); | const {matches, pick, memoize} = require('lodash'); | ||||||
| const gitLogParser = require('git-log-parser'); | const gitLogParser = require('git-log-parser'); | ||||||
| const getStream = require('get-stream'); | const getStream = require('get-stream'); | ||||||
| const execa = require('execa'); | const execa = require('execa'); | ||||||
| @ -63,9 +63,9 @@ async function getCommits(from, to, execaOpts) { | |||||||
|  * @throws {Error} If the `git` command fails. |  * @throws {Error} If the `git` command fails. | ||||||
|  */ |  */ | ||||||
| async function getBranches(execaOpts) { | async function getBranches(execaOpts) { | ||||||
|   return (await execa.stdout('git', ['branch', '--list', '--no-color'], execaOpts)) |   return (await execa.stdout('git', ['ls-remote', '--heads', 'origin'], execaOpts)) | ||||||
|     .split('\n') |     .split('\n') | ||||||
|     .map(branch => trimStart(branch, '*').trim()) |     .map(branch => branch.match(/^.+refs\/heads\/(.+)$/)[1]) | ||||||
|     .filter(Boolean); |     .filter(Boolean); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -126,13 +126,30 @@ async function isRefExists(ref, execaOpts) { | |||||||
| /** | /** | ||||||
|  * Unshallow the git repository if necessary and fetch all the tags. |  * Unshallow the git repository if necessary and fetch all the tags. | ||||||
|  * |  * | ||||||
|  |  * @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(execaOpts) { | async function fetch(branch, execaOpts) { | ||||||
|  |   const isLocalExists = | ||||||
|  |     (await execa('git', ['rev-parse', '--verify', branch], {...execaOpts, reject: false})).code === 0; | ||||||
|  | 
 | ||||||
|   try { |   try { | ||||||
|     await execa('git', ['fetch', '--unshallow', '--tags'], execaOpts); |     await execa( | ||||||
|  |       'git', | ||||||
|  |       [ | ||||||
|  |         'fetch', | ||||||
|  |         '--unshallow', | ||||||
|  |         '--tags', | ||||||
|  |         ...(isLocalExists ? [] : ['origin', `+refs/heads/${branch}:refs/heads/${branch}`]), | ||||||
|  |       ], | ||||||
|  |       execaOpts | ||||||
|  |     ); | ||||||
|   } catch (error) { |   } catch (error) { | ||||||
|     await execa('git', ['fetch', '--tags'], execaOpts); |     await execa( | ||||||
|  |       'git', | ||||||
|  |       ['fetch', '--tags', ...(isLocalExists ? [] : ['origin', `+refs/heads/${branch}:refs/heads/${branch}`])], | ||||||
|  |       execaOpts | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -181,7 +198,7 @@ async function isGitRepo(execaOpts) { | |||||||
|  * Verify the write access authorization to remote repository with push dry-run. |  * Verify the write access authorization to remote repository with push dry-run. | ||||||
|  * |  * | ||||||
|  * @param {String} repositoryUrl The remote repository URL. |  * @param {String} repositoryUrl The remote repository URL. | ||||||
|  * @param {String} branch The repositoru branch for which to verify write access. |  * @param {String} branch The repository branch for which to verify write access. | ||||||
|  * @param {Object} [execaOpts] Options to pass to `execa`. |  * @param {Object} [execaOpts] Options to pass to `execa`. | ||||||
|  * |  * | ||||||
|  * @throws {Error} if not authorized to push. |  * @throws {Error} if not authorized to push. | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ test('Enforce ranges with branching release workflow', async t => { | |||||||
|     {name: 'beta', prerelease: true, tags: []}, |     {name: 'beta', prerelease: true, tags: []}, | ||||||
|     {name: 'alpha', prerelease: true, tags: []}, |     {name: 'alpha', prerelease: true, tags: []}, | ||||||
|   ]; |   ]; | ||||||
|   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches}); |   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []}); | ||||||
| 
 | 
 | ||||||
|   let result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range})); |   let result = (await getBranches({options: {branches}})).map(({name, range}) => ({name, range})); | ||||||
|   t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master'); |   t.is(getBranch(result, '1.0.x').range, '>=1.0.0 <1.0.0', 'Cannot release on 1.0.x before a releasing on master'); | ||||||
| @ -145,7 +145,7 @@ test('Throw SemanticReleaseError for invalid configurations', async t => { | |||||||
|     {name: 'alpha', prerelease: 'alpha', tags: []}, |     {name: 'alpha', prerelease: 'alpha', tags: []}, | ||||||
|     {name: 'preview', prerelease: 'alpha', tags: []}, |     {name: 'preview', prerelease: 'alpha', tags: []}, | ||||||
|   ]; |   ]; | ||||||
|   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches}); |   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []}); | ||||||
|   const errors = [...(await t.throws(getBranches({options: {branches}})))]; |   const errors = [...(await t.throws(getBranches({options: {branches}})))]; | ||||||
| 
 | 
 | ||||||
|   t.is(errors[0].name, 'SemanticReleaseError'); |   t.is(errors[0].name, 'SemanticReleaseError'); | ||||||
| @ -172,7 +172,7 @@ test('Throw SemanticReleaseError for invalid configurations', async t => { | |||||||
| 
 | 
 | ||||||
| test('Throw a SemanticReleaseError if there is duplicate branches', async t => { | test('Throw a SemanticReleaseError if there is duplicate branches', async t => { | ||||||
|   const branches = [{name: 'master', tags: []}, {name: 'master', tags: []}]; |   const branches = [{name: 'master', tags: []}, {name: 'master', tags: []}]; | ||||||
|   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches}); |   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []}); | ||||||
| 
 | 
 | ||||||
|   const errors = [...(await t.throws(getBranches({options: {branches}})))]; |   const errors = [...(await t.throws(getBranches({options: {branches}})))]; | ||||||
| 
 | 
 | ||||||
| @ -184,7 +184,7 @@ test('Throw a SemanticReleaseError if there is duplicate branches', async t => { | |||||||
| 
 | 
 | ||||||
| test('Throw a SemanticReleaseError for each invalid branch name', async t => { | test('Throw a SemanticReleaseError for each invalid branch name', async t => { | ||||||
|   const branches = [{name: '~master', tags: []}, {name: '^master', tags: []}]; |   const branches = [{name: '~master', tags: []}, {name: '^master', tags: []}]; | ||||||
|   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches}); |   const getBranches = proxyquire('../../lib/branches', {'./get-tags': () => branches, './expand': () => []}); | ||||||
| 
 | 
 | ||||||
|   const errors = [...(await t.throws(getBranches({options: {branches}})))]; |   const errors = [...(await t.throws(getBranches({options: {branches}})))]; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,24 +1,32 @@ | |||||||
| import test from 'ava'; | import test from 'ava'; | ||||||
| import expand from '../../lib/branches/expand'; | import expand from '../../lib/branches/expand'; | ||||||
| import {gitRepo, gitCommits, gitCheckout} from '../helpers/git-utils'; | import {gitRepo, gitCommits, gitCheckout, gitPush} from '../helpers/git-utils'; | ||||||
| 
 | 
 | ||||||
| test('Expand branches defined with globs', async t => { | test('Expand branches defined with globs', async t => { | ||||||
|   const {cwd} = await gitRepo(); |   const {cwd, repositoryUrl} = await gitRepo(true); | ||||||
|   await gitCommits(['First'], {cwd}); |   await gitCommits(['First'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'master', {cwd}); | ||||||
|   await gitCheckout('1.1.x', true, {cwd}); |   await gitCheckout('1.1.x', true, {cwd}); | ||||||
|   await gitCommits(['Second'], {cwd}); |   await gitCommits(['Second'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, '1.1.x', {cwd}); | ||||||
|   await gitCheckout('1.x.x', true, {cwd}); |   await gitCheckout('1.x.x', true, {cwd}); | ||||||
|   await gitCommits(['Third'], {cwd}); |   await gitCommits(['Third'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, '1.x.x', {cwd}); | ||||||
|   await gitCheckout('2.x', true, {cwd}); |   await gitCheckout('2.x', true, {cwd}); | ||||||
|   await gitCommits(['Fourth'], {cwd}); |   await gitCommits(['Fourth'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, '2.x', {cwd}); | ||||||
|   await gitCheckout('next', true, {cwd}); |   await gitCheckout('next', true, {cwd}); | ||||||
|   await gitCommits(['Fifth'], {cwd}); |   await gitCommits(['Fifth'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'next', {cwd}); | ||||||
|   await gitCheckout('pre/foo', true, {cwd}); |   await gitCheckout('pre/foo', true, {cwd}); | ||||||
|   await gitCommits(['Sixth'], {cwd}); |   await gitCommits(['Sixth'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'pre/foo', {cwd}); | ||||||
|   await gitCheckout('pre/bar', true, {cwd}); |   await gitCheckout('pre/bar', true, {cwd}); | ||||||
|   await gitCommits(['Seventh'], {cwd}); |   await gitCommits(['Seventh'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'pre/bar', {cwd}); | ||||||
|   await gitCheckout('beta', true, {cwd}); |   await gitCheckout('beta', true, {cwd}); | ||||||
|   await gitCommits(['Eighth'], {cwd}); |   await gitCommits(['Eighth'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'beta', {cwd}); | ||||||
| 
 | 
 | ||||||
|   const branches = [ |   const branches = [ | ||||||
|     // Should match all maintenance type branches
 |     // Should match all maintenance type branches
 | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ test('Unshallow and fetch repository', async t => { | |||||||
|   // Verify the shallow clone contains only one commit
 |   // Verify the shallow clone contains only one commit
 | ||||||
|   t.is((await gitGetCommits(undefined, {cwd})).length, 1); |   t.is((await gitGetCommits(undefined, {cwd})).length, 1); | ||||||
| 
 | 
 | ||||||
|   await fetch({cwd}); |   await fetch('master', {cwd}); | ||||||
| 
 | 
 | ||||||
|   // Verify the shallow clone contains all the commits
 |   // Verify the shallow clone contains all the commits
 | ||||||
|   t.is((await gitGetCommits(undefined, {cwd})).length, 2); |   t.is((await gitGetCommits(undefined, {cwd})).length, 2); | ||||||
| @ -66,10 +66,15 @@ test('Unshallow and fetch repository', async t => { | |||||||
| 
 | 
 | ||||||
| test('Do not throw error when unshallow a complete repository', async t => { | 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
 |   // Create a git repository, set the current working directory at the root of the repo
 | ||||||
|   const {cwd} = await gitRepo(true); |   const {cwd, repositoryUrl} = await gitRepo(true); | ||||||
|   // Add commits to the master branch
 |  | ||||||
|   await gitCommits(['First'], {cwd}); |   await gitCommits(['First'], {cwd}); | ||||||
|   await t.notThrows(fetch({cwd})); |   await gitPush(repositoryUrl, 'master', {cwd}); | ||||||
|  |   await gitCheckout('second-branch', true, {cwd}); | ||||||
|  |   await gitCommits(['Second'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'second-branch', {cwd}); | ||||||
|  | 
 | ||||||
|  |   await t.notThrows(fetch('master', {cwd})); | ||||||
|  |   await t.notThrows(fetch('second-branch', {cwd})); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('Fetch all tags on a detached head repository', async t => { | test('Fetch all tags on a detached head repository', async t => { | ||||||
| @ -84,7 +89,7 @@ test('Fetch all tags on a detached head repository', async t => { | |||||||
|   await gitPush(repositoryUrl, 'master', {cwd}); |   await gitPush(repositoryUrl, 'master', {cwd}); | ||||||
|   cwd = await gitDetachedHead(repositoryUrl, commit.hash); |   cwd = await gitDetachedHead(repositoryUrl, commit.hash); | ||||||
| 
 | 
 | ||||||
|   await fetch({cwd}); |   await fetch('master', {cwd}); | ||||||
| 
 | 
 | ||||||
|   t.deepEqual((await getTags({cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort()); |   t.deepEqual((await getTags({cwd})).sort(), ['v1.0.0', 'v1.0.1', 'v1.1.0'].sort()); | ||||||
| }); | }); | ||||||
| @ -122,12 +127,15 @@ test('Verify if a branch exists', async t => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| test('Get all branches', async t => { | test('Get all branches', async t => { | ||||||
|   const {cwd} = await gitRepo(); |   const {cwd, repositoryUrl} = await gitRepo(true); | ||||||
|   await gitCommits(['First'], {cwd}); |   await gitCommits(['First'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'master', {cwd}); | ||||||
|   await gitCheckout('second-branch', true, {cwd}); |   await gitCheckout('second-branch', true, {cwd}); | ||||||
|   await gitCommits(['Second'], {cwd}); |   await gitCommits(['Second'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'second-branch', {cwd}); | ||||||
|   await gitCheckout('third-branch', true, {cwd}); |   await gitCheckout('third-branch', true, {cwd}); | ||||||
|   await gitCommits(['Third'], {cwd}); |   await gitCommits(['Third'], {cwd}); | ||||||
|  |   await gitPush(repositoryUrl, 'third-branch', {cwd}); | ||||||
| 
 | 
 | ||||||
|   t.deepEqual((await getBranches({cwd})).sort(), ['master', 'second-branch', 'third-branch'].sort()); |   t.deepEqual((await getBranches({cwd})).sort(), ['master', 'second-branch', 'third-branch'].sort()); | ||||||
| }); | }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user