Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: renovatebot/renovate
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 39.213.6
Choose a base ref
...
head repository: renovatebot/renovate
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 39.214.0
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Mar 25, 2025

  1. fix(libyear): reduce log noise (#34996)

    viceice authored Mar 25, 2025

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    ba2fc05 View commit details
  2. feat(github-actions): extract versioned actions from composite actions (

    viceice authored Mar 25, 2025

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    ed7d148 View commit details
129 changes: 129 additions & 0 deletions lib/modules/manager/github-actions/extract.spec.ts
Original file line number Diff line number Diff line change
@@ -705,5 +705,134 @@ describe('modules/manager/github-actions/extract', () => {
},
]);
});

it('extracts x-version from actions/setup-x in composite action', () => {
const yamlContent = `
steps:
- name: "Setup Node.js"
uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: "Setup Node.js with exact version"
uses: actions/setup-node@v3
with:
node-version: '20.0.0'
- name: "Setup Go"
uses: actions/setup-go@v5
with:
go-version: '1.23'
- name: "Setup Python with range"
uses: actions/setup-python@v3
with:
python-version: '>=3.8.0 <3.10.0'
- name: "Setup Node.js with latest"
uses: actions/setup-node@v3
with:
node-version: 'latest'`;

const res = extractPackageFile(yamlContent, 'action.yml');
expect(res?.deps).toMatchObject([
{
autoReplaceStringTemplate:
'{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}',
commitMessageTopic: '{{{depName}}} action',
currentValue: 'v3',
datasource: 'github-tags',
depName: 'actions/setup-node',
depType: 'action',
replaceString: 'actions/setup-node@v3',
versioning: 'docker',
},
{
autoReplaceStringTemplate:
'{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}',
commitMessageTopic: '{{{depName}}} action',
currentValue: 'v3',
datasource: 'github-tags',
depName: 'actions/setup-node',
depType: 'action',
replaceString: 'actions/setup-node@v3',
versioning: 'docker',
},
{
autoReplaceStringTemplate:
'{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}',
commitMessageTopic: '{{{depName}}} action',
currentValue: 'v5',
datasource: 'github-tags',
depName: 'actions/setup-go',
depType: 'action',
replaceString: 'actions/setup-go@v5',
versioning: 'docker',
},
{
autoReplaceStringTemplate:
'{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}',
commitMessageTopic: '{{{depName}}} action',
currentValue: 'v3',
datasource: 'github-tags',
depName: 'actions/setup-python',
depType: 'action',
replaceString: 'actions/setup-python@v3',
versioning: 'docker',
},
{
autoReplaceStringTemplate:
'{{depName}}@{{#if newDigest}}{{newDigest}}{{#if newValue}} # {{newValue}}{{/if}}{{/if}}{{#unless newDigest}}{{newValue}}{{/unless}}',
commitMessageTopic: '{{{depName}}} action',
currentValue: 'v3',
datasource: 'github-tags',
depName: 'actions/setup-node',
depType: 'action',
replaceString: 'actions/setup-node@v3',
versioning: 'docker',
},
{
depName: 'node',
packageName: 'actions/node-versions',
currentValue: '16.x',
datasource: 'github-releases',
versioning: 'node',
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$',
depType: 'uses-with',
},
{
depName: 'node',
packageName: 'actions/node-versions',
currentValue: '20.0.0',
datasource: 'github-releases',
versioning: 'node',
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$',
depType: 'uses-with',
},
{
depName: 'go',
packageName: 'actions/go-versions',
currentValue: '1.23',
datasource: 'github-releases',
versioning: 'npm',
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$',
depType: 'uses-with',
},
{
depName: 'python',
packageName: 'actions/python-versions',
currentValue: '>=3.8.0 <3.10.0',
datasource: 'github-releases',
versioning: 'npm',
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$',
depType: 'uses-with',
},
{
depName: 'node',
packageName: 'actions/node-versions',
currentValue: 'latest',
datasource: 'github-releases',
versioning: 'node',
extractVersion: '^(?<version>\\d+\\.\\d+\\.\\d+)(-\\d+)?$',
depType: 'uses-with',
},
]);
});
});
});
87 changes: 50 additions & 37 deletions lib/modules/manager/github-actions/extract.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,8 @@ import type {
PackageDependency,
PackageFileContent,
} from '../types';
import { WorkflowJobsSchema } from './schema';
import type { Steps } from './schema';
import { WorkflowSchema } from './schema';

const dockerActionRe = regEx(/^\s+uses\s*: ['"]?docker:\/\/([^'"]+)\s*$/);
const actionRe = regEx(
@@ -167,6 +168,41 @@ function extractRunner(runner: string): PackageDependency | null {
return dependency;
}

const versionedActions: Record<string, string> = {
go: npmVersioning.id,
node: nodeVersioning.id,
python: npmVersioning.id,
// Not covered yet because they use different datasources/packageNames:
// - dotnet
// - java
};

function extractSteps(
steps: Steps[],
deps: PackageDependency<Record<string, any>>[],
): void {
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: 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: string,
packageFile: string,
@@ -175,11 +211,19 @@ function extractWithYAMLParser(
logger.trace('github-actions.extractWithYAMLParser()');
const deps: PackageDependency[] = [];

const jobs = withMeta({ packageFile }, () =>
WorkflowJobsSchema.parse(content),
);
const obj = withMeta({ packageFile }, () => WorkflowSchema.parse(content));

for (const job of jobs) {
if (!obj) {
return deps;
}

// composite action
if ('steps' in obj) {
extractSteps(obj.steps, deps);
return deps;
}

for (const job of obj) {
if (job.container) {
const dep = getDep(job.container, true, config.registryAliases);
if (dep) {
@@ -203,38 +247,7 @@ function extractWithYAMLParser(
}
}

const versionedActions: Record<string, string> = {
go: npmVersioning.id,
node: nodeVersioning.id,
python: npmVersioning.id,
// Not covered yet because they use different datasources/packageNames:
// - dotnet
// - java
};

for (const step of job.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: 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',
});
}
}
}
}
extractSteps(job.steps, deps);
}

return deps;
30 changes: 18 additions & 12 deletions lib/modules/manager/github-actions/schema.ts
Original file line number Diff line number Diff line change
@@ -6,8 +6,14 @@ import {
withDebugMessage,
} from '../../../util/schema-utils';

export const WorkflowJobsSchema = Yaml.pipe(
z.object({
const StepsSchema = z.object({
uses: z.string(),
with: LooseRecord(z.string()),
});
export type Steps = z.infer<typeof StepsSchema>;

const WorkFlowJobsSchema = z
.object({
jobs: LooseRecord(
z.object({
container: z
@@ -28,15 +34,15 @@ export const WorkflowJobsSchema = Yaml.pipe(
'runs-on': z
.union([z.string().transform((v) => [v]), z.array(z.string())])
.catch([]),
steps: LooseArray(
z.object({
uses: z.string(),
with: LooseRecord(z.string()),
}),
).catch([]),
steps: LooseArray(StepsSchema).catch([]),
}),
),
}),
)
.transform((v) => Object.values(v.jobs))
.catch(withDebugMessage([], 'Does not match schema'));
})
.transform((v) => Object.values(v.jobs));

const ActionStepsSchema = z.object({
steps: LooseArray(StepsSchema).catch([]),
});
export const WorkflowSchema = Yaml.pipe(
z.union([WorkFlowJobsSchema, ActionStepsSchema, z.null()]),
).catch(withDebugMessage(null, 'Does not match schema'));
6 changes: 3 additions & 3 deletions lib/workers/repository/process/libyear.spec.ts
Original file line number Diff line number Diff line change
@@ -94,13 +94,13 @@ describe('workers/repository/process/libyear', () => {
],
};
calculateLibYears(config, packageFiles);
expect(logger.logger.debug).toHaveBeenCalledWith(
expect(logger.logger.once.debug).toHaveBeenCalledWith(
'No currentVersionTimestamp for some/image',
);
expect(logger.logger.debug).toHaveBeenCalledWith(
expect(logger.logger.once.debug).toHaveBeenCalledWith(
'No releaseTimestamp for dep1 update to 3.0.0',
);
expect(logger.logger.debug).toHaveBeenCalledWith(
expect(logger.logger.once.debug).toHaveBeenCalledWith(
'No currentVersionTimestamp for dep3',
);
expect(logger.logger.debug).toHaveBeenCalledWith(
4 changes: 2 additions & 2 deletions lib/workers/repository/process/libyear.ts
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ export function calculateLibYears(

depInfo.outdated = true;
if (!dep.currentVersionTimestamp) {
logger.debug(`No currentVersionTimestamp for ${dep.depName}`);
logger.once.debug(`No currentVersionTimestamp for ${dep.depName}`);
allDeps.push(depInfo);
continue;
}
@@ -50,7 +50,7 @@ export function calculateLibYears(

for (const update of dep.updates) {
if (!update.releaseTimestamp) {
logger.debug(
logger.once.debug(
`No releaseTimestamp for ${dep.depName} update to ${update.newVersion}`,
);
continue;