renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
211 lines • 8.98 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractPackageFile = extractPackageFile;
const tslib_1 = require("tslib");
const global_1 = require("../../../config/global");
const logger_1 = require("../../../logger");
const common_1 = require("../../../util/common");
const regex_1 = require("../../../util/regex");
const gitea_tags_1 = require("../../datasource/gitea-tags");
const github_releases_1 = require("../../datasource/github-releases");
const github_runners_1 = require("../../datasource/github-runners");
const github_tags_1 = require("../../datasource/github-tags");
const dockerVersioning = tslib_1.__importStar(require("../../versioning/docker"));
const nodeVersioning = tslib_1.__importStar(require("../../versioning/node"));
const npmVersioning = tslib_1.__importStar(require("../../versioning/npm"));
const extract_1 = require("../dockerfile/extract");
const schema_1 = require("./schema");
const dockerActionRe = (0, regex_1.regEx)(/^\s+uses\s*: ['"]?docker:\/\/([^'"]+)\s*$/);
const actionRe = (0, regex_1.regEx)(/^\s+-?\s+?uses\s*: (?<replaceString>['"]?(?<depName>(?<registryUrl>https:\/\/[.\w-]+\/)?(?<packageName>[\w-]+\/[.\w-]+))(?<path>\/.*)?@(?<currentValue>[^\s'"]+)['"]?(?:(?<commentWhiteSpaces>\s+)#\s*(((?:renovate\s*:\s*)?(?:pin\s+|tag\s*=\s*)?|(?:ratchet:[\w-]+\/[.\w-]+)?)@?(?<tag>([\w-]*[-/])?v?\d+(?:\.\d+(?:\.\d+)?)?)|(?:ratchet:exclude)))?)/);
// SHA1 or SHA256, see https://github.blog/2020-10-19-git-2-29-released/
const shaRe = (0, regex_1.regEx)(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
const shaShortRe = (0, regex_1.regEx)(/^[a-f0-9]{6,7}$/);
// detects if we run against a Github Enterprise Server and adds the URL to the beginning of the registryURLs for looking up Actions
// This reflects the behavior of how GitHub looks up Actions
// First on the Enterprise Server, then on GitHub.com
function detectCustomGitHubRegistryUrlsForActions() {
const endpoint = global_1.GlobalConfig.get('endpoint');
const registryUrls = ['https://github.com'];
if (endpoint && global_1.GlobalConfig.get('platform') === 'github') {
const parsedEndpoint = new URL(endpoint);
if (parsedEndpoint.host !== 'github.com' &&
parsedEndpoint.host !== 'api.github.com') {
registryUrls.unshift(`${parsedEndpoint.protocol}//${parsedEndpoint.host}`);
return { registryUrls };
}
}
return {};
}
function extractWithRegex(content, config) {
const customRegistryUrlsPackageDependency = detectCustomGitHubRegistryUrlsForActions();
logger_1.logger.trace('github-actions.extractWithRegex()');
const deps = [];
for (const line of content.split(regex_1.newlineRegex)) {
if (line.trim().startsWith('#')) {
continue;
}
const dockerMatch = dockerActionRe.exec(line);
if (dockerMatch) {
const [, currentFrom] = dockerMatch;
const dep = (0, extract_1.getDep)(currentFrom, true, config.registryAliases);
dep.depType = 'docker';
deps.push(dep);
continue;
}
const tagMatch = actionRe.exec(line);
if (tagMatch?.groups) {
const { depName, packageName, currentValue, path = '', tag, replaceString, registryUrl = '', commentWhiteSpaces = ' ', } = tagMatch.groups;
let quotes = '';
if (replaceString.includes("'")) {
quotes = "'";
}
if (replaceString.includes('"')) {
quotes = '"';
}
const dep = {
depName,
...(packageName !== depName && { packageName }),
commitMessageTopic: '{{{depName}}} action',
datasource: github_tags_1.GithubTagsDatasource.id,
versioning: dockerVersioning.id,
depType: 'action',
replaceString,
autoReplaceStringTemplate: `${quotes}{{depName}}${path}@{{#if newDigest}}{{newDigest}}${quotes}{{#if newValue}}${commentWhiteSpaces}# {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}${quotes}{{/unless}}`,
...(registryUrl
? detectDatasource(registryUrl)
: customRegistryUrlsPackageDependency),
};
if (shaRe.test(currentValue)) {
dep.currentValue = tag;
dep.currentDigest = currentValue;
}
else if (shaShortRe.test(currentValue)) {
dep.currentValue = tag;
dep.currentDigestShort = currentValue;
}
else {
dep.currentValue = currentValue;
}
deps.push(dep);
}
}
return deps;
}
function detectDatasource(registryUrl) {
const platform = (0, common_1.detectPlatform)(registryUrl);
switch (platform) {
case 'github':
return { registryUrls: [registryUrl] };
case 'gitea':
return {
registryUrls: [registryUrl],
datasource: gitea_tags_1.GiteaTagsDatasource.id,
};
}
return {
skipReason: 'unsupported-url',
};
}
const runnerVersionRegex = (0, regex_1.regEx)(/^\s*(?<depName>[a-zA-Z]+)-(?<currentValue>[^\s]+)/);
function extractRunner(runner) {
const runnerVersionGroups = runnerVersionRegex.exec(runner)?.groups;
if (!runnerVersionGroups) {
return null;
}
const { depName, currentValue } = runnerVersionGroups;
if (!github_runners_1.GithubRunnersDatasource.isValidRunner(depName, currentValue)) {
return null;
}
const dependency = {
depName,
currentValue,
replaceString: `${depName}-${currentValue}`,
depType: 'github-runner',
datasource: github_runners_1.GithubRunnersDatasource.id,
autoReplaceStringTemplate: '{{depName}}-{{newValue}}',
};
if (!dockerVersioning.api.isValid(currentValue)) {
dependency.skipReason = 'invalid-version';
}
return dependency;
}
const versionedActions = {
go: npmVersioning.id,
node: nodeVersioning.id,
python: npmVersioning.id,
// Not covered yet because they use different datasources/packageNames:
// - dotnet
// - java
};
function extractSteps(steps, deps) {
for (const step of steps) {
for (const [action, versioning] of Object.entries(versionedActions)) {
const actionName = `actions/setup-${action}`;
if (step.uses === actionName || step.uses?.startsWith(`${actionName}@`)) {
const fieldName = `${action}-version`;
const currentValue = step.with?.[fieldName];
if (currentValue) {
deps.push({
datasource: github_releases_1.GithubReleasesDatasource.id,
depName: action,
packageName: `actions/${action}-versions`,
versioning,
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$', // Actions release tags are like 1.24.1-13667719799
currentValue,
depType: 'uses-with',
});
}
}
}
}
}
function extractWithYAMLParser(content, packageFile, config) {
logger_1.logger.trace('github-actions.extractWithYAMLParser()');
const deps = [];
const obj = (0, logger_1.withMeta)({ packageFile }, () => schema_1.WorkflowSchema.parse(content));
if (!obj) {
return deps;
}
// composite action
if ('runs' in obj && obj.runs.steps) {
extractSteps(obj.runs.steps, deps);
}
else if ('jobs' in obj) {
for (const job of Object.values(obj.jobs)) {
if (job.container) {
const dep = (0, extract_1.getDep)(job.container, true, config.registryAliases);
if (dep) {
dep.depType = 'container';
deps.push(dep);
}
}
for (const service of job.services) {
const dep = (0, extract_1.getDep)(service, true, config.registryAliases);
if (dep) {
dep.depType = 'service';
deps.push(dep);
}
}
for (const runner of job['runs-on']) {
const dep = extractRunner(runner);
if (dep) {
deps.push(dep);
}
}
extractSteps(job.steps, deps);
}
}
return deps;
}
function extractPackageFile(content, packageFile, config = {}) {
logger_1.logger.trace(`github-actions.extractPackageFile(${packageFile})`);
const deps = [
...extractWithRegex(content, config),
...extractWithYAMLParser(content, packageFile, config),
];
if (!deps.length) {
return null;
}
return { deps };
}
//# sourceMappingURL=extract.js.map