Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor branch/tag resolution for git dependencies, fixes #3720 #3836

Merged
merged 1 commit into from
Jul 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 52 additions & 56 deletions __tests__/util/git.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
/* @flow */

jest.mock('../../src/util/child.js', () => {
const realChild = (require: any).requireActual('../../src/util/child.js');

realChild.spawn = jest.fn(() => Promise.resolve(''));

return realChild;
});

jest.mock('../../src/util/git/git-spawn.js', () => ({
spawn: jest.fn(([command]) => {
switch (command) {
case 'ls-remote':
return `ref: refs/heads/master HEAD
7a053e2ca07d19b2e2eebeeb0c27edaacfd67904 HEAD`;
case 'rev-list':
return Promise.resolve('7a053e2ca07d19b2e2eebeeb0c27edaacfd67904 Fix ...');
}
return Promise.resolve('');
}),
}));

import Config from '../../src/config.js';
import Git from '../../src/util/git.js';
import {spawn} from '../../src/util/child.js';
import {spawn as spawnGit} from '../../src/util/git/git-spawn.js';
import {NoopReporter} from '../../src/reporters/index.js';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
Expand Down Expand Up @@ -72,15 +78,6 @@ test('npmUrlToGitUrl', () => {
});
});

test('isCommitHash', () => {
expect(Git.isCommitHash('ca82a6dff817ec66f44312307202690a93763949')).toBeTruthy();
expect(Git.isCommitHash('abc12')).toBeTruthy();
expect(Git.isCommitHash('')).toBeFalsy();
expect(Git.isCommitHash('abc12_')).toBeFalsy();
expect(Git.isCommitHash('gccda')).toBeFalsy();
expect(Git.isCommitHash('abC12')).toBeFalsy();
});

test('secureGitUrl', async function(): Promise<void> {
const reporter = new NoopReporter();

Expand Down Expand Up @@ -109,44 +106,43 @@ test('secureGitUrl', async function(): Promise<void> {
expect(gitURL.repository).toEqual('https://github.com/yarnpkg/yarn.git');
});

test('parseRefs', () => {
expect(Git.parseRefs(`64b2c0cee9e829f73c5ad32b8cc8cb6f3bec65bb refs/tags/v4.2.2`)).toMatchObject({
'v4.2.2': '64b2c0cee9e829f73c5ad32b8cc8cb6f3bec65bb',
});

expect(
Git.parseRefs(`ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0
70e76d174b0c7d001d2cd608a16c94498496e92d refs/tags/v0.21.0^{}
de43f4a993d1e08cd930ee22ecb2bac727f53449 refs/tags/v0.21.0-pre`),
).toMatchObject({
'v0.21.0': '70e76d174b0c7d001d2cd608a16c94498496e92d',
'v0.21.0-pre': 'de43f4a993d1e08cd930ee22ecb2bac727f53449',
});

expect(
Git.parseRefs(`**********
This is a custom response header
as described in: https://github.com/yarnpkg/yarn/issues/3325
**********

ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0
70e76d174b0c7d001d2cd608a16c94498496e92d refs/tags/v0.21.0^{}
de43f4a993d1e08cd930ee22ecb2bac727f53449 refs/tags/v0.21.0-pre`),
).toMatchObject({
'v0.21.0': '70e76d174b0c7d001d2cd608a16c94498496e92d',
'v0.21.0-pre': 'de43f4a993d1e08cd930ee22ecb2bac727f53449',
});
test('resolveDefaultBranch', async () => {
const spawnGitMock = (spawnGit: any).mock;
const config = await Config.create();
const git = new Git(
config,
{
protocol: '',
hostname: undefined,
repository: '',
},
'',
);
expect(await git.resolveDefaultBranch()).toEqual({
sha: '7a053e2ca07d19b2e2eebeeb0c27edaacfd67904',
ref: 'refs/heads/master',
});
const lastCall = spawnGitMock.calls[spawnGitMock.calls.length - 1];
expect(lastCall[0]).toContain('ls-remote');
});

test('spawn', () => {
const spawnMock = (spawn: any).mock;

Git.spawn(['status']);

expect(spawnMock.calls[0][2].env).toMatchObject({
GIT_ASKPASS: '',
GIT_TERMINAL_PROMPT: 0,
GIT_SSH_COMMAND: 'ssh -oBatchMode=yes',
...process.env,
});
test('resolveCommit', async () => {
const spawnGitMock = (spawnGit: any).mock;
const config = await Config.create();
const git = new Git(
config,
{
protocol: '',
hostname: undefined,
repository: '',
},
'',
);
expect(await git.resolveCommit('7a053e2')).toEqual({
sha: '7a053e2ca07d19b2e2eebeeb0c27edaacfd67904',
ref: undefined,
});
const lastCall = spawnGitMock.calls[spawnGitMock.calls.length - 1];
expect(lastCall[0]).toContain('rev-list');
expect(lastCall[0]).toContain('7a053e2');
});
161 changes: 161 additions & 0 deletions __tests__/util/git/git-ref-resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* @flow */

import Config from '../../../src/config.js';
import type {ResolvedSha, GitRefResolvingInterface, GitRefs} from '../../../src/util/git/git-ref-resolver.js';
import {resolveVersion, isCommitSha, parseRefs} from '../../../src/util/git/git-ref-resolver.js';

class GitMock implements GitRefResolvingInterface {
resolveDefaultBranch(): Promise<ResolvedSha> {
return Promise.resolve({sha: '8a41a314e23dc566a6b7e73c757a10d13e3320cf', ref: 'refs/heads/main'});
}
resolveCommit(sha: string): Promise<?ResolvedSha> {
if (sha.startsWith('003ae60')) {
return Promise.resolve({sha: '003ae6063f23a4184736610361f14438a3257c83', ref: undefined});
}
return Promise.resolve(null);
}
}

test('resolveVersion', async () => {
const config = await Config.create();

const refs: GitRefs = new Map();
refs.set('refs/heads/1.1', 'eaa56cb34863810060abbec2d755ba51508afedc');
refs.set('refs/heads/3.3', '4cff93aa6e8270c3bec988af464d28a164bc3cb2');
refs.set('refs/heads/main', '8a41a314e23dc566a6b7e73c757a10d13e3320cf');
refs.set('refs/heads/both', '106c28537be070b98ca1effaef6a2bf6414e1e49');
refs.set('refs/tags/v1.1.0', '37d5ed001dc4402d5446911c4e1cb589449e7d8d');
refs.set('refs/tags/v2.2.0', 'e88209b9513544a22fc3f8660e3d829281dc2c9f');
refs.set('refs/tags/both', 'f0dbab0a4345a64f544af37e24fc8187176936a4');
const emptyRefs: GitRefs = new Map();
const git = new GitMock();

const resolve = version => resolveVersion({config, version, refs, git});

expect(await resolve('')).toEqual({
sha: '8a41a314e23dc566a6b7e73c757a10d13e3320cf',
ref: 'refs/heads/main',
});
expect(await resolve('003ae6063f23a4184736610361f14438a3257c83')).toEqual({
sha: '003ae6063f23a4184736610361f14438a3257c83',
ref: undefined,
});
expect(await resolve('003ae60')).toEqual({
sha: '003ae6063f23a4184736610361f14438a3257c83',
ref: undefined,
});
// Test uppercase
expect(await resolve('003AE60')).toEqual({
sha: '003ae6063f23a4184736610361f14438a3257c83',
ref: undefined,
});
expect(await resolve('4cff93aa6e8270c3bec988af464d28a164bc3cb2')).toEqual({
sha: '4cff93aa6e8270c3bec988af464d28a164bc3cb2',
ref: 'refs/heads/3.3',
});
expect(await resolve('4cff93a')).toEqual({
sha: '4cff93aa6e8270c3bec988af464d28a164bc3cb2',
ref: 'refs/heads/3.3',
});
expect(await resolve('main')).toEqual({
sha: '8a41a314e23dc566a6b7e73c757a10d13e3320cf',
ref: 'refs/heads/main',
});
expect(await resolve('1.1')).toEqual({
sha: 'eaa56cb34863810060abbec2d755ba51508afedc',
ref: 'refs/heads/1.1',
});
expect(await resolve('v1.1.0')).toEqual({
sha: '37d5ed001dc4402d5446911c4e1cb589449e7d8d',
ref: 'refs/tags/v1.1.0',
});
// not-existing sha
expect(await resolve('0123456')).toEqual(null);

// Test tags precedence over branches
expect(await resolve('both')).toEqual({
sha: 'f0dbab0a4345a64f544af37e24fc8187176936a4',
ref: 'refs/tags/both',
});
expect(await resolve('refs/heads/both')).toEqual({
sha: '106c28537be070b98ca1effaef6a2bf6414e1e49',
ref: 'refs/heads/both',
});
// Test no match
expect(await resolve('unknown')).toEqual(null);

// Test SemVer

// prefix space to force semver
expect(await resolve(' 1.1')).toEqual({
sha: '37d5ed001dc4402d5446911c4e1cb589449e7d8d',
ref: 'refs/tags/v1.1.0',
});
expect(await resolve('~1.1')).toEqual({
sha: '37d5ed001dc4402d5446911c4e1cb589449e7d8d',
ref: 'refs/tags/v1.1.0',
});
// test on tags first, should not match 3.3
expect(await resolve('*')).toEqual({
sha: 'e88209b9513544a22fc3f8660e3d829281dc2c9f',
ref: 'refs/tags/v2.2.0',
});
// Test * without tags, use default branch
expect(await resolveVersion({config, version: '*', refs: emptyRefs, git})).toEqual({
sha: '8a41a314e23dc566a6b7e73c757a10d13e3320cf',
ref: 'refs/heads/main',
});
});

test('isCommitSha', () => {
expect(isCommitSha('ca82a6dff817ec66f44312307202690a93763949')).toBeTruthy();
expect(isCommitSha('abc12')).toBeTruthy();
expect(isCommitSha('')).toBeFalsy();
expect(isCommitSha('abc12_')).toBeFalsy();
expect(isCommitSha('gccda')).toBeFalsy();
expect(isCommitSha('abC12')).toBeFalsy();
});

test('parseRefs', () => {
const parse = refs => {
const refsAsObject = {};
for (const [key, value] of parseRefs(refs).entries()) {
refsAsObject[key] = value;
}
return refsAsObject;
};

expect(parse(`64b2c0cee9e829f73c5ad32b8cc8cb6f3bec65bb refs/tags/v4.2.2`)).toMatchObject({
'refs/tags/v4.2.2': '64b2c0cee9e829f73c5ad32b8cc8cb6f3bec65bb',
});

expect(
parse(`ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0
70e76d174b0c7d001d2cd608a16c94498496e92d refs/tags/v0.21.0^{}
de43f4a993d1e08cd930ee22ecb2bac727f53449 refs/tags/v0.21.0-pre`),
).toMatchObject({
'refs/tags/v0.21.0': '70e76d174b0c7d001d2cd608a16c94498496e92d',
'refs/tags/v0.21.0-pre': 'de43f4a993d1e08cd930ee22ecb2bac727f53449',
});

expect(
parse(`ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/tag
70e76d174b0c7d001d2cd608a16c94498496e92d refs/merge-requests/38/head`),
).toMatchObject({
'refs/tags/tag': 'ebeb6eafceb61dd08441ffe086c77eb472842494',
});

expect(
parse(`**********
This is a custom response header
as described in: https://github.com/yarnpkg/yarn/issues/3325
**********

ebeb6eafceb61dd08441ffe086c77eb472842494 refs/tags/v0.21.0
70e76d174b0c7d001d2cd608a16c94498496e92d refs/tags/v0.21.0^{}
de43f4a993d1e08cd930ee22ecb2bac727f53449 refs/tags/v0.21.0-pre`),
).toMatchObject({
'refs/tags/v0.21.0': '70e76d174b0c7d001d2cd608a16c94498496e92d',
'refs/tags/v0.21.0-pre': 'de43f4a993d1e08cd930ee22ecb2bac727f53449',
});
});
25 changes: 25 additions & 0 deletions __tests__/util/git/git-spawn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* @flow */

jest.mock('../../../src/util/child.js', () => {
const realChild = (require: any).requireActual('../../../src/util/child.js');

realChild.spawn = jest.fn(() => Promise.resolve(''));

return realChild;
});

import {spawn as spawnGit} from '../../../src/util/git/git-spawn.js';
import {spawn} from '../../../src/util/child.js';

test('spawn', () => {
const spawnMock = (spawn: any).mock;

spawnGit(['status']);

expect(spawnMock.calls[0][2].env).toMatchObject({
GIT_ASKPASS: '',
GIT_TERMINAL_PROMPT: 0,
GIT_SSH_COMMAND: 'ssh -oBatchMode=yes',
...process.env,
});
});
3 changes: 1 addition & 2 deletions src/resolvers/exotics/hosted-git-resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ export default class HostedGitResolver extends ExoticResolver {
throw new Error(this.reporter.lang('hostedGitResolveError'));
}

const refs = Git.parseRefs(out);
return client.setRef(refs);
return client.setRefHosted(out);
}

async resolveOverHTTP(url: string): Promise<Manifest> {
Expand Down
Loading