Skip to content

Commit

Permalink
fix(circleci): parsing of inline orb definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
steved committed Dec 13, 2024
1 parent 955229e commit 9959872
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 23 deletions.
60 changes: 60 additions & 0 deletions lib/modules/manager/circleci/extract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,65 @@ describe('modules/manager/circleci/extract', () => {
},
]);
});

it('extracts orb definitions', () => {
const res = extractPackageFile(codeBlock`
version: 2.1
orbs:
myorb:
orbs:
python: circleci/[email protected]
executors:
python:
docker:
- image: cimg/python:3.9
jobs:
test_image:
docker:
- image: cimg/python:3.7
steps:
- checkout
workflows:
Test:
jobs:
- myorb/test_image`);

expect(res).toEqual({
deps: [
{
currentValue: '2.1.1',
datasource: 'orb',
depName: 'python',
depType: 'orb',
packageName: 'circleci/python',
versioning: 'npm',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '3.9',
datasource: 'docker',
depName: 'cimg/python',
depType: 'docker',
replaceString: 'cimg/python:3.9',
},
{
autoReplaceStringTemplate:
'{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}',
currentDigest: undefined,
currentValue: '3.7',
datasource: 'docker',
depName: 'cimg/python',
depType: 'docker',
replaceString: 'cimg/python:3.7',
},
],
});
});
});
});
58 changes: 37 additions & 21 deletions lib/modules/manager/circleci/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@ import type {
PackageDependency,
PackageFileContent,
} from '../types';
import { CircleCiFile, type CircleCiJob } from './schema';
import { CircleCiFile, type CircleCiJob, type CircleCiOrb } from './schema';

export function extractPackageFile(
content: string,
function extractDefinition(
definition: CircleCiOrb | CircleCiFile,
packageFile?: string,
config?: ExtractConfig,
): PackageFileContent | null {
): PackageDependency[] {
const deps: PackageDependency[] = [];
try {
const parsed = parseSingleYaml(content, {
customSchema: CircleCiFile,
});

for (const [key, orb] of Object.entries(parsed.orbs ?? {})) {
for (const [key, orb] of Object.entries(definition.orbs ?? {})) {
if (typeof orb === 'string') {
const [packageName, currentValue] = orb.split('@');

deps.push({
Expand All @@ -33,21 +30,40 @@ export function extractPackageFile(
versioning: npmVersioning.id,
datasource: OrbDatasource.id,
});
} else {
deps.push(...extractDefinition(orb));
}
}

// extract environments
const environments: CircleCiJob[] = [
Object.values(parsed.executors ?? {}),
Object.values(parsed.jobs ?? {}),
].flat();
for (const job of environments) {
for (const dockerElement of coerceArray(job.docker)) {
deps.push({
...getDep(dockerElement.image, true, config?.registryAliases),
depType: 'docker',
});
}
// extract environments
const environments: CircleCiJob[] = [
Object.values(definition.executors ?? {}),
Object.values(definition.jobs ?? {}),
].flat();
for (const job of environments) {
for (const dockerElement of coerceArray(job.docker)) {
deps.push({
...getDep(dockerElement.image, true, config?.registryAliases),
depType: 'docker',
});
}
}

return deps;
}

export function extractPackageFile(
content: string,
packageFile?: string,
config?: ExtractConfig,
): PackageFileContent | null {
const deps: PackageDependency[] = [];
try {
const parsed = parseSingleYaml(content, {
customSchema: CircleCiFile,
});

deps.push(...extractDefinition(parsed, packageFile, config));

for (const alias of coerceArray(parsed.aliases)) {
deps.push({
Expand Down
21 changes: 19 additions & 2 deletions lib/modules/manager/circleci/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@ export const CircleCiDocker = z.object({
image: z.string(),
});

export type CircleCiJob = z.infer<typeof CircleCiJob>;
export const CircleCiJob = z.object({
docker: z.array(CircleCiDocker).optional(),
});
export type CircleCiJob = z.infer<typeof CircleCiJob>;

const baseOrb = z.object({
executors: z.record(z.string(), CircleCiJob).optional(),
jobs: z.record(z.string(), CircleCiJob).optional(),
});

type Orb = z.infer<typeof baseOrb> & {
orbs?: Record<string, string | Orb>;
};

export const CircleCiOrb: z.ZodType<Orb> = baseOrb.extend({
orbs: z.lazy(() =>
z.record(z.string(), z.union([z.string(), CircleCiOrb])).optional(),
),
});
export type CircleCiOrb = z.infer<typeof CircleCiOrb>;

export const CircleCiFile = z.object({
aliases: z.array(CircleCiDocker).optional(),
executors: z.record(z.string(), CircleCiJob).optional(),
jobs: z.record(z.string(), CircleCiJob).optional(),
orbs: z.record(z.string()).optional(),
orbs: z.record(z.string(), z.union([z.string(), CircleCiOrb])).optional(),
});
export type CircleCiFile = z.infer<typeof CircleCiFile>;

0 comments on commit 9959872

Please sign in to comment.