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(manager/bitbucket-pipelines): support docker image object #21102

Merged
merged 4 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 0 additions & 3 deletions lib/modules/datasource/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,4 @@ export interface DatasourceApi extends ModuleApi {
* false: caching is not performed, or performed within the datasource implementation
*/
caching?: boolean | undefined;

/** optional URLs to add to docs as references */
urls?: string[];
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,55 @@
image: node:10.15.1

definitions:
steps:
- step: &build-test
name: Build and test
image:
# comment
name: node:18.15.0
script:
- mvn package
artifacts:
- target/**

- step: &build-test1
image:
username: xxxx
name: node:18.15.1

- step: &build-test2
image:
username: xxx
password: xxx

name: node:18.15.2

- step:
image:
test:
name: malformed

- step:
image:
username: xxx
test:
name: malformed

- step:
image:
username: xxx
password: xxx
test:
name: malformed


pipelines:
default:
- step:
name: Build and Test
image: node:10.15.2
script:
- step: *build-test
- pipe: docker://jfrogecosystem/jfrog-setup-cli:2.0.2
- npm install
- npm test
Expand Down
92 changes: 60 additions & 32 deletions lib/modules/manager/bitbucket-pipelines/extract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,77 @@ import { extractPackageFile } from '.';
describe('modules/manager/bitbucket-pipelines/extract', () => {
describe('extractPackageFile()', () => {
it('returns null for empty', () => {
expect(extractPackageFile('nothing here')).toBeNull();
expect(
extractPackageFile('nothing here', 'bitbucket-pipelines.yaml')
).toBeNull();
});

it('returns null for malformed', () => {
expect(
extractPackageFile(
'image:\n username: ccc',
'bitbucket-pipelines.yaml'
)
).toBeNull();
});

it('extracts dependencies', () => {
const res = extractPackageFile(Fixtures.get('bitbucket-pipelines.yaml'));
expect(res?.deps).toMatchInlineSnapshot(`
[
const res = extractPackageFile(
Fixtures.get('bitbucket-pipelines.yaml'),
'bitbucket-pipelines.yaml'
);
expect(res).toMatchObject({
deps: [
{
currentDigest: undefined,
currentValue: '10.15.1',
datasource: 'docker',
depName: 'node',
depType: 'docker',
},
{
currentDigest: undefined,
currentValue: '18.15.0',
datasource: 'docker',
depName: 'node',
depType: 'docker',
},
{
currentDigest: undefined,
currentValue: '18.15.1',
datasource: 'docker',
depName: 'node',
depType: 'docker',
},
{
"autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}",
"currentDigest": undefined,
"currentValue": "10.15.1",
"datasource": "docker",
"depName": "node",
"depType": "docker",
"replaceString": "node:10.15.1",
currentDigest: undefined,
currentValue: '18.15.2',
datasource: 'docker',
depName: 'node',
depType: 'docker',
},
{
"autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}",
"currentDigest": undefined,
"currentValue": "10.15.2",
"datasource": "docker",
"depName": "node",
"depType": "docker",
"replaceString": "node:10.15.2",
currentDigest: undefined,
currentValue: '10.15.2',
datasource: 'docker',
depName: 'node',
depType: 'docker',
},
{
"autoReplaceStringTemplate": "{{depName}}{{#if newValue}}:{{newValue}}{{/if}}{{#if newDigest}}@{{newDigest}}{{/if}}",
"currentDigest": undefined,
"currentValue": "2.0.2",
"datasource": "docker",
"depName": "jfrogecosystem/jfrog-setup-cli",
"depType": "docker",
"replaceString": "jfrogecosystem/jfrog-setup-cli:2.0.2",
currentDigest: undefined,
currentValue: '2.0.2',
datasource: 'docker',
depName: 'jfrogecosystem/jfrog-setup-cli',
depType: 'docker',
},
{
"currentValue": "0.2.1",
"datasource": "bitbucket-tags",
"depName": "atlassian/aws-s3-deploy",
"depType": "bitbucket-tags",
currentValue: '0.2.1',
datasource: 'bitbucket-tags',
depName: 'atlassian/aws-s3-deploy',
depType: 'bitbucket-tags',
},
]
`);
expect(res?.deps).toHaveLength(4);
],
});
});
});
});
72 changes: 40 additions & 32 deletions lib/modules/manager/bitbucket-pipelines/extract.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,44 @@
import { logger } from '../../../logger';
import { newlineRegex, regEx } from '../../../util/regex';
import { BitbucketTagsDatasource } from '../../datasource/bitbucket-tags';
import { getDep } from '../dockerfile/extract';
import { newlineRegex } from '../../../util/regex';
import type { PackageDependency, PackageFileContent } from '../types';
import {
addDepAsBitbucketTag,
addDepAsDockerImage,
addDepFromObject,
dockerImageObjectRegex,
dockerImageRegex,
pipeRegex,
} from './util';

const pipeRegex = regEx(`^\\s*-\\s?pipe:\\s*'?"?([^\\s'"]+)'?"?\\s*$`);
const dockerImageRegex = regEx(`^\\s*-?\\s?image:\\s*'?"?([^\\s'"]+)'?"?\\s*$`);

export function extractPackageFile(content: string): PackageFileContent | null {
export function extractPackageFile(
content: string,
filename: string
): PackageFileContent | null {
const deps: PackageDependency[] = [];

try {
const lines = content.split(newlineRegex);
for (const line of lines) {
const lines = content
.replaceAll(/^\s*\r?\n/gm, '') // replace empty lines
.replaceAll(/^\s*#.*\r?\n/gm, '') // replace comment lines
.split(newlineRegex);
const len = lines.length;
for (let lineIdx = 0; lineIdx < len; lineIdx++) {
const line = lines[lineIdx];

const dockerImageObjectGroups = dockerImageObjectRegex.exec(line)?.groups;
if (dockerImageObjectGroups) {
// image object
// https://support.atlassian.com/bitbucket-cloud/docs/docker-image-options/
lineIdx = addDepFromObject(
deps,
lines,
lineIdx,
len,
dockerImageObjectGroups.spaces
);
continue;
}

const pipeMatch = pipeRegex.exec(line);
if (pipeMatch) {
const pipe = pipeMatch[1];
Expand All @@ -23,6 +49,7 @@ export function extractPackageFile(content: string): PackageFileContent | null {
} else {
addDepAsBitbucketTag(deps, pipe);
}
continue;
}

const dockerImageMatch = dockerImageRegex.exec(line);
Expand All @@ -32,32 +59,13 @@ export function extractPackageFile(content: string): PackageFileContent | null {
}
}
} catch (err) /* istanbul ignore next */ {
logger.warn({ err }, 'Error extracting Bitbucket Pipes dependencies');
logger.debug(
{ err, filename },
'Error extracting Bitbucket Pipes dependencies'
);
}
if (!deps.length) {
return null;
}
return { deps };
}
function addDepAsBitbucketTag(
deps: PackageDependency<Record<string, any>>[],
pipe: string
): void {
const [depName, currentValue] = pipe.split(':');
const dep: PackageDependency = {
depName,
currentValue,
datasource: BitbucketTagsDatasource.id,
};
dep.depType = 'bitbucket-tags';
deps.push(dep);
}

function addDepAsDockerImage(
deps: PackageDependency<Record<string, any>>[],
currentDockerImage: string
): void {
const dep = getDep(currentDockerImage);
dep.depType = 'docker';
deps.push(dep);
}
4 changes: 4 additions & 0 deletions lib/modules/manager/bitbucket-pipelines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export const defaultConfig = {
};

export const supportedDatasources = [DockerDatasource.id];

export const urls = [
'https://support.atlassian.com/bitbucket-cloud/docs/bitbucket-pipelines-configuration-reference/',
];
66 changes: 66 additions & 0 deletions lib/modules/manager/bitbucket-pipelines/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { regEx } from '../../../util/regex';
import { BitbucketTagsDatasource } from '../../datasource/bitbucket-tags';
import { getDep } from '../dockerfile/extract';
import type { PackageDependency } from '../types';

export const pipeRegex = regEx(`^\\s*-\\s?pipe:\\s*'?"?([^\\s'"]+)'?"?\\s*$`);
export const dockerImageRegex = regEx(
`^\\s*-?\\s?image:\\s*'?"?([^\\s'"]+)'?"?\\s*$`
);
export const dockerImageObjectRegex = regEx('^(?<spaces>\\s*)image:\\s*$');

export function addDepAsBitbucketTag(
deps: PackageDependency[],
pipe: string
): void {
const [depName, currentValue] = pipe.split(':');
const dep: PackageDependency = {
depName,
currentValue,
datasource: BitbucketTagsDatasource.id,
};
dep.depType = 'bitbucket-tags';
deps.push(dep);
}

export function addDepAsDockerImage(
deps: PackageDependency[],
currentDockerImage: string
): void {
const dep = getDep(currentDockerImage);
dep.depType = 'docker';
deps.push(dep);
}

export function addDepFromObject(
deps: PackageDependency[],
lines: string[],
start: number,
len: number,
spaces: string
): number {
const nameRegex = regEx(
`^${spaces}\\s+name:\\s*['"]?(?<image>[^\\s'"]+)['"]?\\s*$`
);
const indentRegex = regEx(`^${spaces}\\s+`);

for (let idx = start + 1; idx < len; idx++) {
const line = lines[idx];

if (!indentRegex.test(line)) {
// malformed
return idx;
}

const groups = nameRegex.exec(line)?.groups;
if (groups) {
const dep = getDep(groups.image);
dep.depType = 'docker';
deps.push(dep);
return idx;
}
}

// malformed
return start;
}
3 changes: 3 additions & 0 deletions lib/types/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { PackageJson } from 'type-fest';
export interface ModuleApi {
displayName?: string;
url?: string;

/** optional URLs to add to docs as references */
urls?: string[];
}

export type RenovatePackageJson = PackageJson & {
Expand Down
12 changes: 10 additions & 2 deletions tools/docs/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import type { RenovateConfig } from '../../lib/config/types';
import { getManagers } from '../../lib/modules/manager';
import { readFile, updateFile } from '../utils';
import { OpenItems, generateFeatureAndBugMarkdown } from './github-query-items';
import { getDisplayName, getNameWithUrl, replaceContent } from './utils';
import {
formatUrls,
getDisplayName,
getNameWithUrl,
replaceContent,
} from './utils';

function getTitle(manager: string, displayName: string): string {
if (manager === 'regex') {
Expand All @@ -26,7 +31,7 @@ export async function generateManagers(
const language = definition.language ?? 'other';
allLanguages[language] = allLanguages[language] || [];
allLanguages[language].push(manager);
const { defaultConfig, supportedDatasources } = definition;
const { defaultConfig, supportedDatasources, urls } = definition;
const { fileMatch } = defaultConfig as RenovateConfig;
const displayName = getDisplayName(manager, definition);
let md = `---
Expand Down Expand Up @@ -70,6 +75,9 @@ sidebar_label: ${displayName}
.join(', ');
md += `This manager supports extracting the following datasources: ${escapedDatasources}.\n\n`;

md += '## References';
md += formatUrls(urls).replace('**References**:', '');
viceice marked this conversation as resolved.
Show resolved Hide resolved

md += '## Default config\n\n';
md += '```json\n';
md += JSON.stringify(definition.defaultConfig, null, 2) + '\n';
Expand Down