mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-15 09:06:25 +00:00
206 lines
6.5 KiB
TypeScript
206 lines
6.5 KiB
TypeScript
import is from '@sindresorhus/is';
|
|
import { logger } from '../../../logger';
|
|
import { readLocalFile } from '../../../util/fs';
|
|
import { regEx } from '../../../util/regex';
|
|
import { GitTagsDatasource } from '../../datasource/git-tags';
|
|
import { GithubTagsDatasource } from '../../datasource/github-tags';
|
|
import { PackagistDatasource } from '../../datasource/packagist';
|
|
import { api as semverComposer } from '../../versioning/composer';
|
|
import type { PackageDependency, PackageFile } from '../types';
|
|
import type {
|
|
ComposerConfig,
|
|
ComposerLock,
|
|
ComposerManagerData,
|
|
ComposerRepositories,
|
|
Repo,
|
|
} from './types';
|
|
|
|
/**
|
|
* The regUrl is expected to be a base URL. GitLab composer repository installation guide specifies
|
|
* to use a base URL containing packages.json. Composer still works in this scenario by determining
|
|
* whether to add / remove packages.json from the URL.
|
|
*
|
|
* See https://github.com/composer/composer/blob/750a92b4b7aecda0e5b2f9b963f1cb1421900675/src/Composer/Repository/ComposerRepository.php#L815
|
|
*/
|
|
function transformRegUrl(url: string): string {
|
|
return url.replace(regEx(/(\/packages\.json)$/), '');
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
function parseRepositories(
|
|
repoJson: ComposerRepositories,
|
|
repositories: Record<string, Repo>,
|
|
registryUrls: string[]
|
|
): void {
|
|
try {
|
|
let packagist = true;
|
|
Object.entries(repoJson).forEach(([key, repo]) => {
|
|
if (is.object(repo)) {
|
|
const name = is.array(repoJson) ? repo.name : key;
|
|
|
|
switch (repo.type) {
|
|
case 'vcs':
|
|
case 'git':
|
|
repositories[name!] = repo;
|
|
break;
|
|
case 'composer':
|
|
registryUrls.push(transformRegUrl(repo.url));
|
|
break;
|
|
case 'package':
|
|
logger.debug(
|
|
{ url: repo.url },
|
|
'type package is not supported yet'
|
|
);
|
|
}
|
|
if (repo.packagist === false || repo['packagist.org'] === false) {
|
|
packagist = false;
|
|
}
|
|
} // istanbul ignore else: invalid repo
|
|
else if (['packagist', 'packagist.org'].includes(key) && repo === false) {
|
|
packagist = false;
|
|
}
|
|
});
|
|
if (packagist) {
|
|
registryUrls.push('https://packagist.org');
|
|
} else {
|
|
logger.debug('Disabling packagist.org');
|
|
}
|
|
} catch (e) /* istanbul ignore next */ {
|
|
logger.debug(
|
|
{ repositories: repoJson },
|
|
'Error parsing composer.json repositories config'
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function extractPackageFile(
|
|
content: string,
|
|
fileName: string
|
|
): Promise<PackageFile | null> {
|
|
logger.trace(`composer.extractPackageFile(${fileName})`);
|
|
let composerJson: ComposerConfig;
|
|
try {
|
|
composerJson = JSON.parse(content);
|
|
} catch (err) {
|
|
logger.debug(`Invalid JSON in ${fileName}`);
|
|
return null;
|
|
}
|
|
const repositories: Record<string, Repo> = {};
|
|
const registryUrls: string[] = [];
|
|
const res: PackageFile = { deps: [] };
|
|
|
|
// handle lockfile
|
|
const lockfilePath = fileName.replace(regEx(/\.json$/), '.lock');
|
|
const lockContents = await readLocalFile(lockfilePath, 'utf8');
|
|
let lockParsed: ComposerLock | undefined;
|
|
if (lockContents) {
|
|
logger.debug(`Found composer lock file ${fileName}`);
|
|
res.lockFiles = [lockfilePath];
|
|
try {
|
|
lockParsed = JSON.parse(lockContents) as ComposerLock;
|
|
} catch (err) /* istanbul ignore next */ {
|
|
logger.warn({ err }, 'Error processing composer.lock');
|
|
}
|
|
}
|
|
|
|
// handle composer.json repositories
|
|
if (composerJson.repositories) {
|
|
parseRepositories(composerJson.repositories, repositories, registryUrls);
|
|
}
|
|
|
|
const deps: PackageDependency[] = [];
|
|
const depTypes: ('require' | 'require-dev')[] = ['require', 'require-dev'];
|
|
for (const depType of depTypes) {
|
|
if (composerJson[depType]) {
|
|
try {
|
|
for (const [depName, version] of Object.entries(
|
|
composerJson[depType]!
|
|
)) {
|
|
const currentValue = version.trim();
|
|
if (depName === 'php') {
|
|
deps.push({
|
|
depType,
|
|
depName,
|
|
currentValue,
|
|
datasource: GithubTagsDatasource.id,
|
|
packageName: 'php/php-src',
|
|
extractVersion: '^php-(?<version>.*)$',
|
|
});
|
|
} else {
|
|
// Default datasource and packageName
|
|
let datasource = PackagistDatasource.id;
|
|
let packageName = depName;
|
|
|
|
// Check custom repositories by type
|
|
if (repositories[depName]) {
|
|
switch (repositories[depName].type) {
|
|
case 'vcs':
|
|
case 'git':
|
|
datasource = GitTagsDatasource.id;
|
|
packageName = repositories[depName].url;
|
|
break;
|
|
}
|
|
}
|
|
const dep: PackageDependency = {
|
|
depType,
|
|
depName,
|
|
currentValue,
|
|
datasource,
|
|
};
|
|
if (depName !== packageName) {
|
|
dep.packageName = packageName;
|
|
}
|
|
if (!depName.includes('/')) {
|
|
dep.skipReason = 'unsupported';
|
|
}
|
|
if (lockParsed) {
|
|
const lockField =
|
|
depType === 'require'
|
|
? 'packages'
|
|
: /* istanbul ignore next */ 'packages-dev';
|
|
const lockedDep = lockParsed[lockField]?.find(
|
|
(item) => item.name === dep.depName
|
|
);
|
|
if (lockedDep && semverComposer.isVersion(lockedDep.version)) {
|
|
dep.lockedVersion = lockedDep.version.replace(regEx(/^v/i), '');
|
|
}
|
|
}
|
|
if (
|
|
!dep.skipReason &&
|
|
(!repositories[depName] ||
|
|
repositories[depName].type === 'composer') &&
|
|
registryUrls.length !== 0
|
|
) {
|
|
dep.registryUrls = registryUrls;
|
|
}
|
|
deps.push(dep);
|
|
}
|
|
}
|
|
} catch (err) /* istanbul ignore next */ {
|
|
logger.debug({ fileName, depType, err }, 'Error parsing composer.json');
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
if (!deps.length) {
|
|
return null;
|
|
}
|
|
res.deps = deps;
|
|
if (is.string(composerJson.type)) {
|
|
const managerData: ComposerManagerData = {
|
|
composerJsonType: composerJson.type,
|
|
};
|
|
res.managerData = managerData;
|
|
}
|
|
|
|
if (composerJson.require?.php) {
|
|
res.extractedConstraints = { php: composerJson.require.php };
|
|
}
|
|
|
|
return res;
|
|
}
|