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

feat(github-actions): add some community action support #34791

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8104fba
add community action
trim21 Mar 13, 2025
ec54ffe
fix
trim21 Mar 13, 2025
0a377d8
fix lint
trim21 Mar 13, 2025
ba536e9
fix lint
trim21 Mar 13, 2025
eb04d6f
add uv
trim21 Mar 13, 2025
085743a
docs
trim21 Mar 13, 2025
38dfb64
add type
trim21 Mar 13, 2025
3100f49
cov
trim21 Mar 13, 2025
67739fb
add setup-pnpm
trim21 Mar 13, 2025
3f17f7d
remove versioning for arbitrary github repo
trim21 Mar 13, 2025
f1c9746
add setup-pdm
trim21 Mar 13, 2025
1c79146
fix
trim21 Mar 13, 2025
f2a4688
Merge branch 'main' into feat/binary-action
trim21 Mar 15, 2025
20d1f8e
use zod union
trim21 Mar 15, 2025
71e9081
move to seprated file
trim21 Mar 15, 2025
309f488
fix lint
trim21 Mar 15, 2025
84cd28d
white space only change
trim21 Mar 15, 2025
ca7ea74
no need to add link
trim21 Mar 15, 2025
74a912a
this looks better
trim21 Mar 15, 2025
02339a9
add setup-pixi
trim21 Mar 15, 2025
df61b6b
fix syntax
trim21 Mar 15, 2025
6ae57ae
escape-string-regexp 4.0.0
trim21 Mar 17, 2025
115dfdc
Merge remote-tracking branch 'upstream/main' into feat/binary-action
trim21 Mar 17, 2025
ec464d0
fix
trim21 Mar 17, 2025
e944153
fix
trim21 Mar 17, 2025
fb90de2
fix
trim21 Mar 17, 2025
b791e56
fix
trim21 Mar 17, 2025
f1c56e7
fix comment
trim21 Mar 17, 2025
980a647
fix comment
trim21 Mar 17, 2025
1414fb5
Merge remote-tracking branch 'upstream/main' into feat/binary-action
trim21 Apr 1, 2025
96df66f
fix test
trim21 Apr 1, 2025
632c5b2
fix pixi versioning
trim21 Apr 1, 2025
30efe71
fix pixi versioning
trim21 Apr 1, 2025
1a34c63
Update lib/modules/manager/github-actions/extract.ts
trim21 Apr 2, 2025
f4ae338
add skip
trim21 Apr 2, 2025
404f5eb
fix test
trim21 Apr 2, 2025
9cb72d1
allow latest
trim21 Apr 3, 2025
c17f22c
fix test
trim21 Apr 3, 2025
a0fa225
cov
trim21 Apr 3, 2025
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
105 changes: 105 additions & 0 deletions lib/modules/manager/github-actions/extract.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { codeBlock } from 'common-tags';
import { GlobalConfig } from '../../../config/global';
import { dump } from '../../../util/yaml';
import { extractPackageFile } from '.';
import { Fixtures } from '~test/fixtures';

Expand Down Expand Up @@ -705,5 +706,109 @@ describe('modules/manager/github-actions/extract', () => {
},
]);
});

it('extracts package from community actions', () => {
const yamlContent = dump({
jobs: {
build: {
steps: [
{
name: 'Pinning a minor version of uv',
uses: 'astral-sh/setup-uv@v5',
with: {
version: 'latest',
},
},
{
uses: 'pnpm/action-setup@v4',
with: {
version: 'latest',
},
},
{
name: 'Install gotestsum',
uses: 'jaxxstorm/[email protected]',
with: {
repo: 'gotestyourself/gotestsum',
tag: 'v1.12.1',
platform: 'linux',
arch: 'amd64',
},
},
{
name: 'Pinning a minor version of uv',
uses: 'astral-sh/setup-uv@v5',
with: {
version: '0.4.x',
},
},
{
uses: 'pnpm/action-setup@v4',
with: {
version: 10,
},
},
{
uses: 'pnpm/action-setup@v4',
with: {
version: '10.x',
},
},
{
uses: 'pdm-project/[email protected]',
with: {
version: '1.2.3',
},
},
],
},
},
});

const res = extractPackageFile(yamlContent, 'workflow.yml');
expect(res?.deps.filter((pkg) => pkg.depType !== 'action')).toMatchObject(
[
{
currentValue: 'v1.12.1',
datasource: 'github-releases',
depName: 'gotestyourself/gotestsum',
depType: 'uses-with',
packageName: 'gotestyourself/gotestsum',
},
{
currentValue: '0.4.x',
datasource: 'github-releases',
depName: 'astral-sh/uv',
depType: 'uses-with',
packageName: 'astral-sh/uv',
versioning: 'npm',
},
{
currentValue: '10',
datasource: 'npm',
depName: 'pnpm',
depType: 'uses-with',
packageName: 'pnpm',
versioning: 'npm',
},
{
currentValue: '10.x',
datasource: 'npm',
depName: 'pnpm',
depType: 'uses-with',
packageName: 'pnpm',
versioning: 'npm',
},
{
currentValue: '1.2.3',
datasource: 'pypi',
depName: 'pdm',
depType: 'uses-with',
packageName: 'pdm',
versioning: 'pep440',
},
],
);
});
});
});
99 changes: 99 additions & 0 deletions lib/modules/manager/github-actions/extract.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import is from '@sindresorhus/is';
import { z } from 'zod';
import { GlobalConfig } from '../../../config/global';
import { logger } from '../../../logger';
import { coerceArray, isNotNullOrUndefined } from '../../../util/array';
import { detectPlatform } from '../../../util/common';
import { newlineRegex, regEx } from '../../../util/regex';
import { Result } from '../../../util/result';
import { parseSingleYaml } from '../../../util/yaml';
import { GiteaTagsDatasource } from '../../datasource/gitea-tags';
import { GithubReleasesDatasource } from '../../datasource/github-releases';
import { GithubRunnersDatasource } from '../../datasource/github-runners';
import { GithubTagsDatasource } from '../../datasource/github-tags';
import { NpmDatasource } from '../../datasource/npm';
import { PypiDatasource } from '../../datasource/pypi';
import * as dockerVersioning from '../../versioning/docker';
import * as nodeVersioning from '../../versioning/node';
import * as npmVersioning from '../../versioning/npm';
import * as pep440versioning from '../../versioning/pep440';
import { getDep } from '../dockerfile/extract';
import type {
ExtractConfig,
Expand Down Expand Up @@ -193,6 +198,89 @@ function extractRunners(runner: unknown): PackageDependency[] {
return runners.map(extractRunner).filter(isNotNullOrUndefined);
}

const communityActions = [
{
// https://github.com/jaxxstorm/action-install-gh-release
use: /^jaxxstorm\/action-install-gh-release@.*$/,
schema: z
.object({ with: z.object({ repo: z.string(), tag: z.string() }) })
.transform(({ with: val }): PackageDependency => {
return {
datasource: GithubReleasesDatasource.id,
depName: val.repo,
packageName: val.repo,
currentValue: val.tag,
depType: 'uses-with',
};
}),
},
{
// https://github.com/astral-sh/setup-uv
use: /^astral-sh\/setup-uv@.*$/,
schema: z
.object({ with: z.object({ version: z.string() }) })
.transform(({ with: val }): PackageDependency | undefined => {
if (val.version === 'latest') {
return;
}

return {
datasource: GithubReleasesDatasource.id,
depName: 'astral-sh/uv',
versioning: npmVersioning.id,
packageName: 'astral-sh/uv',
currentValue: val.version,
depType: 'uses-with',
};
}),
},
{
// https://github.com/pnpm/action-setup
use: /^pnpm\/action-setup@.*$/,
schema: z
.object({
with: z.object({
version: z.union([
z.string(),
z.number().transform((s) => s.toString()),
]),
}),
})
.transform(({ with: val }): PackageDependency | undefined => {
if (val.version === 'latest') {
return;
}

return {
datasource: NpmDatasource.id,
depName: 'pnpm',
versioning: npmVersioning.id,
packageName: 'pnpm',
currentValue: val.version,
depType: 'uses-with',
};
}),
},
{
// https://github.com/astral-sh/setup-uv
use: /^pdm-project\/setup-pdm@.*$/,
schema: z
.object({
with: z.object({ version: z.string().refine((s) => s !== 'head') }),
})
.transform(({ with: val }): PackageDependency | undefined => {
return {
datasource: PypiDatasource.id,
depName: 'pdm',
versioning: pep440versioning.id,
packageName: 'pdm',
currentValue: val.version,
depType: 'uses-with',
};
}),
},
];

function extractWithYAMLParser(
content: string,
packageFile: string,
Expand Down Expand Up @@ -246,6 +334,17 @@ function extractWithYAMLParser(
};

for (const step of coerceArray(job?.steps)) {
if (step.uses) {
for (const action of communityActions) {
if (action.use.test(step.uses)) {
const val = Result.parse(step, action.schema).unwrapOrNull();
if (val) {
deps.push(val);
}
}
}
}

for (const [action, actionData] of Object.entries(actionsWithVersions)) {
const actionName = `actions/setup-${action}`;
if (
Expand Down