Skip to content
Snippets Groups Projects
Commit 19e839fc authored by Tanuel's avatar Tanuel Committed by Rhys Arkins
Browse files

feat(composer): Add support for custom git repositories (#4055)

parent c678af36
No related branches found
No related tags found
No related merge requests found
......@@ -5,6 +5,38 @@ const semverComposer = require('../../versioning/composer');
export { extractPackageFile };
/**
* Parse the repositories field from a composer.json
*
* Entries with type vcs or git will be added to repositories,
* other entries will be added to registryUrls
*
* @param repoJson
* @param repositories
* @param registryUrls
*/
function parseRepositories(repoJson, repositories, registryUrls) {
try {
Object.entries(repoJson).forEach(([key, repo]) => {
const name = is.array(repoJson) ? repo.name : key;
switch (repo.type) {
case 'vcs':
case 'git':
// eslint-disable-next-line no-param-reassign
repositories[name] = repo;
break;
default:
registryUrls.push(repo);
}
});
} catch (e) /* istanbul ignore next */ {
logger.info(
{ repositories: repoJson },
'Error parsing composer.json repositories config'
);
}
}
async function extractPackageFile(content, fileName) {
logger.trace(`composer.extractPackageFile(${fileName})`);
let composerJson;
......@@ -14,6 +46,33 @@ async function extractPackageFile(content, fileName) {
logger.info({ fileName }, 'Invalid JSON');
return null;
}
const repositories = {};
const registryUrls = [];
const res = {};
// handle lockfile
const lockfilePath = fileName.replace(/\.json$/, '.lock');
const lockContents = await platform.getFile(lockfilePath);
let lockParsed;
if (lockContents) {
logger.debug({ packageFile: fileName }, 'Found composer lock file');
res.composerLock = lockfilePath;
try {
lockParsed = JSON.parse(lockContents);
} catch (err) /* istanbul ignore next */ {
logger.warn({ err }, 'Error processing composer.lock');
}
} else {
res.composerLock = false;
}
// handle composer.json repositories
if (composerJson.repositories) {
parseRepositories(composerJson.repositories, repositories, registryUrls);
}
if (registryUrls.length !== 0) {
res.registryUrls = registryUrls;
}
const deps = [];
const depTypes = ['require', 'require-dev'];
for (const depType of depTypes) {
......@@ -23,12 +82,30 @@ async function extractPackageFile(content, fileName) {
composerJson[depType]
)) {
const currentValue = version.trim();
// Default datasource and lookupName
let datasource = 'packagist';
let lookupName = depName;
// Check custom repositories by type
if (repositories[depName]) {
// eslint-disable-next-line default-case
switch (repositories[depName].type) {
case 'vcs':
case 'git':
datasource = 'gitTags';
lookupName = repositories[depName].url;
break;
}
}
const dep = {
depType,
depName,
currentValue,
datasource: 'packagist',
datasource,
};
if (depName !== lookupName) {
dep.lookupName = lookupName;
}
if (!depName.includes('/')) {
dep.skipReason = 'unsupported';
}
......@@ -38,27 +115,7 @@ async function extractPackageFile(content, fileName) {
if (currentValue === '*') {
dep.skipReason = 'any-version';
}
deps.push(dep);
}
} catch (err) /* istanbul ignore next */ {
logger.info({ fileName, depType, err }, 'Error parsing composer.json');
return null;
}
}
}
if (!deps.length) {
return null;
}
const res = { deps };
const filePath = fileName.replace(/\.json$/, '.lock');
const lockContents = await platform.getFile(filePath);
// istanbul ignore if
if (lockContents) {
logger.debug({ packageFile: fileName }, 'Found composer lock file');
res.composerLock = filePath;
try {
const lockParsed = JSON.parse(lockContents);
for (const dep of res.deps) {
if (lockParsed) {
const lockedDep = lockParsed.packages.find(
item => item.name === dep.depName
);
......@@ -66,31 +123,18 @@ async function extractPackageFile(content, fileName) {
dep.lockedVersion = lockedDep.version.replace(/^v/i, '');
}
}
} catch (err) {
logger.warn({ err }, 'Error processing composer.lock');
}
} else {
res.composerLock = false;
}
if (composerJson.repositories) {
if (is.array(composerJson.repositories)) {
res.registryUrls = composerJson.repositories;
} else if (is.object(composerJson.repositories)) {
try {
res.registryUrls = [];
for (const repository of Object.values(composerJson.repositories)) {
res.registryUrls.push(repository);
deps.push(dep);
}
} catch (err) /* istanbul ignore next */ {
logger.warn({ err }, 'Error extracting composer repositories');
logger.info({ fileName, depType, err }, 'Error parsing composer.json');
return null;
}
} /* istanbul ignore next */ else {
logger.info(
{ repositories: composerJson.repositories },
'Unknown composer repositories'
);
}
}
if (!deps.length) {
return null;
}
res.deps = deps;
if (composerJson.type) {
res.composerJsonType = composerJson.type;
}
......
......@@ -565,6 +565,44 @@ Object {
}
`;
exports[`lib/manager/composer/extract extractPackageFile() extracts object repositories and registryUrls with lock file 1`] = `
Object {
"composerLock": "composer.lock",
"deps": Array [
Object {
"currentValue": "*",
"datasource": "packagist",
"depName": "aws/aws-sdk-php",
"depType": "require",
"skipReason": "any-version",
},
Object {
"currentValue": "dev-trunk",
"datasource": "gitTags",
"depName": "awesome/vcs",
"depType": "require",
"lockedVersion": "1.1.0",
"lookupName": "https://my-vcs.example/my-vcs-repo",
"skipReason": "unsupported-constraint",
},
Object {
"currentValue": ">=7.0.2",
"datasource": "gitTags",
"depName": "awesome/git",
"depType": "require",
"lockedVersion": "1.2.0",
"lookupName": "git@my-git.example:my-git-repo",
},
],
"registryUrls": Array [
Object {
"type": "composer",
"url": "https://wpackagist.org",
},
],
}
`;
exports[`lib/manager/composer/extract extractPackageFile() extracts registryUrls 1`] = `
Object {
"composerLock": false,
......@@ -605,3 +643,39 @@ Object {
],
}
`;
exports[`lib/manager/composer/extract extractPackageFile() extracts repositories and registryUrls 1`] = `
Object {
"composerLock": false,
"deps": Array [
Object {
"currentValue": "*",
"datasource": "packagist",
"depName": "aws/aws-sdk-php",
"depType": "require",
"skipReason": "any-version",
},
Object {
"currentValue": "dev-trunk",
"datasource": "gitTags",
"depName": "awesome/vcs",
"depType": "require",
"lookupName": "https://my-vcs.example/my-vcs-repo",
"skipReason": "unsupported-constraint",
},
Object {
"currentValue": ">=7.0.2",
"datasource": "gitTags",
"depName": "awesome/git",
"depType": "require",
"lookupName": "https://my-git.example/my-git-repo",
},
],
"registryUrls": Array [
Object {
"type": "composer",
"url": "https://wpackagist.org",
},
],
}
`;
{
"name": "acme/git-sources",
"description": "Fetch Packages via git",
"repositories":[
{
"name": "awesome/vcs",
"type":"vcs",
"url":"https://my-vcs.example/my-vcs-repo"
},
{
"name": "awesome/git",
"type":"git",
"url":"https://my-git.example/my-git-repo"
},
{
"type": "composer",
"url": "https://wpackagist.org"
}
],
"require": {
"aws/aws-sdk-php":"*",
"awesome/vcs":"dev-trunk",
"awesome/git":">=7.0.2"
},
"autoload": {
"psr-0": {
"Acme": "src/"
}
}
}
{
"name": "acme/git-sources",
"description": "Fetch Packages via git",
"repositories":{
"awesome/vcs": {
"type":"vcs",
"url":"https://my-vcs.example/my-vcs-repo"
},
"awesome/git": {
"type":"git",
"url":"git@my-git.example:my-git-repo"
},
"wpackagist": {
"type": "composer",
"url": "https://wpackagist.org"
}
},
"require": {
"aws/aws-sdk-php":"*",
"awesome/vcs":"dev-trunk",
"awesome/git":">=7.0.2"
},
"autoload": {
"psr-0": {
"Acme": "src/"
}
}
}
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "h34j5h3p3g4ug34u34543j5h34j53h5j",
"packages": [
{
"name": "awesome/vcs",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://my-vcs.example/my-vcs-repo",
"reference": "3j5b345j3b45j345j345h34j5h345j34h5j34h5j"
}
},
{
"name": "awesome/git",
"version": "1.2.0",
"source": {
"type": "git",
"url": "git@my-git.example/awesome.git",
"reference": "3j5b345j3b45j345j345h34j5h345j34h5j34h5j"
}
}
]
}
......@@ -16,6 +16,18 @@ const requirements3 = fs.readFileSync(
'test/manager/composer/_fixtures/composer3.json',
'utf8'
);
const requirements4 = fs.readFileSync(
'test/manager/composer/_fixtures/composer4.json',
'utf8'
);
const requirements5 = fs.readFileSync(
'test/manager/composer/_fixtures/composer5.json',
'utf8'
);
const requirements5Lock = fs.readFileSync(
'test/manager/composer/_fixtures/composer5.lock',
'utf8'
);
describe('lib/manager/composer/extract', () => {
describe('extractPackageFile()', () => {
......@@ -43,6 +55,17 @@ describe('lib/manager/composer/extract', () => {
expect(res).toMatchSnapshot();
expect(res.registryUrls).toHaveLength(3);
});
it('extracts repositories and registryUrls', async () => {
const res = await extractPackageFile(requirements4, packageFile);
expect(res).toMatchSnapshot();
expect(res.registryUrls).toHaveLength(1);
});
it('extracts object repositories and registryUrls with lock file', async () => {
platform.getFile.mockReturnValueOnce(requirements5Lock);
const res = await extractPackageFile(requirements5, packageFile);
expect(res).toMatchSnapshot();
expect(res.registryUrls).toHaveLength(1);
});
it('extracts dependencies with lock file', async () => {
platform.getFile.mockReturnValueOnce('some content');
const res = await extractPackageFile(requirements1, packageFile);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment