feat(manager): add bitrise manager (#30256)

This commit is contained in:
Sebastian Poxhofer
2024-07-26 11:34:20 +02:00
committed by GitHub
parent 4da584a9a2
commit d439f84bf9
8 changed files with 327 additions and 0 deletions
+2
View File
@@ -10,6 +10,7 @@ import * as bazelModule from './bazel-module';
import * as bazelisk from './bazelisk';
import * as bicep from './bicep';
import * as bitbucketPipelines from './bitbucket-pipelines';
import * as bitrise from './bitrise';
import * as buildkite from './buildkite';
import * as bun from './bun';
import * as bundler from './bundler';
@@ -108,6 +109,7 @@ api.set('bazel-module', bazelModule);
api.set('bazelisk', bazelisk);
api.set('bicep', bicep);
api.set('bitbucket-pipelines', bitbucketPipelines);
api.set('bitrise', bitrise);
api.set('buildkite', buildkite);
api.set('bun', bun);
api.set('bundler', bundler);
+138
View File
@@ -0,0 +1,138 @@
import { codeBlock } from 'common-tags';
import { BitriseDatasource } from '../../datasource/bitrise';
import { extractPackageFile } from '.';
describe('modules/manager/bitrise/extract', () => {
describe('extractPackageFile()', () => {
it('returns null on an empty file', () => {
expect(extractPackageFile('', 'bitrise.yml')).toBeNull();
});
it('returns a valid file', () => {
expect(
extractPackageFile(
codeBlock`
workflows:
test:
steps:
- script@1.1.5:
`,
'bitrise.yml',
),
).toEqual({
deps: [
{
datasource: BitriseDatasource.id,
packageName: 'script',
currentValue: '1.1.5',
replaceString: 'script@1.1.5',
},
],
});
});
it('returns a valid file with custom default_step_lib_source', () => {
expect(
extractPackageFile(
codeBlock`
format_version: 11
default_step_lib_source: https://github.com/custom/steplib.git
project_type: android
app:
envs:
- MY_NAME: My Name
workflows:
test:
steps:
- script@1.1.5:
inputs:
- content: echo "Hello!"
- restore-cache@1.1.2:
foo: bar
`,
'bitrise.yml',
),
).toEqual({
deps: [
{
datasource: BitriseDatasource.id,
packageName: 'script',
currentValue: '1.1.5',
replaceString: 'script@1.1.5',
registryUrls: ['https://github.com/custom/steplib.git'],
},
{
datasource: BitriseDatasource.id,
packageName: 'restore-cache',
currentValue: '1.1.2',
replaceString: 'restore-cache@1.1.2',
registryUrls: ['https://github.com/custom/steplib.git'],
},
],
});
});
it('extracts git and path prefixes', () => {
expect(
extractPackageFile(
codeBlock`
workflows:
test:
steps:
- git::https://github.com/bitrise-io/steps-script.git@1.1.3:
- path::./relative/path:
- https://github.com/foo/bar.git::script@1:
`,
'bitrise.yml',
),
).toEqual({
deps: [
{
currentValue: '1.1.3',
datasource: 'git-tags',
packageName: 'https://github.com/bitrise-io/steps-script.git',
replaceString:
'git::https://github.com/bitrise-io/steps-script.git@1.1.3',
},
{
datasource: 'bitrise',
packageName: 'path::./relative/path',
replaceString: 'path::./relative/path',
skipReason: 'unspecified-version',
},
{
currentValue: '1',
datasource: 'bitrise',
packageName: 'script',
registryUrls: ['https://github.com/foo/bar.git'],
replaceString: 'https://github.com/foo/bar.git::script@1',
},
],
});
});
it('extracts Bitrise library reference', () => {
expect(
extractPackageFile(
codeBlock`
workflows:
test:
steps:
- https://github.com/foo/bar.git::script@1:
`,
'bitrise.yml',
),
).toEqual({
deps: [
{
currentValue: '1',
datasource: 'bitrise',
packageName: 'script',
registryUrls: ['https://github.com/foo/bar.git'],
replaceString: 'https://github.com/foo/bar.git::script@1',
},
],
});
});
});
});
+38
View File
@@ -0,0 +1,38 @@
import is from '@sindresorhus/is';
import { logger } from '../../../logger';
import { parseSingleYaml } from '../../../util/yaml';
import type { PackageDependency, PackageFileContent } from '../types';
import { BitriseFile } from './schema';
import { parseStep } from './utils';
export function extractPackageFile(
content: string,
packageFile: string,
): PackageFileContent | null {
const deps: PackageDependency[] = [];
try {
const parsed = parseSingleYaml(content, {
customSchema: BitriseFile,
});
const workflows = Object.values(parsed.workflows);
for (const workflow of workflows) {
const steps = workflow.steps.flatMap((step) => Object.keys(step));
for (const step of steps) {
const dep = parseStep(step, parsed.default_step_lib_source);
if (!is.nullOrUndefined(dep)) {
deps.push(dep);
}
}
}
} catch (err) {
logger.debug({ err, packageFile }, `Failed to parse Bitrise YAML config`);
}
if (!deps.length) {
return null;
}
return { deps };
}
+23
View File
@@ -0,0 +1,23 @@
import type { Category } from '../../../constants';
import { BitriseDatasource } from '../../datasource/bitrise';
import { GitTagsDatasource } from '../../datasource/git-tags';
import { extractPackageFile } from './extract';
export { extractPackageFile };
export const defaultConfig = {
fileMatch: ['(^|/)bitrise\\.ya?ml$'],
};
export const displayName = 'Bitrise';
export const categories: Category[] = ['ci'];
export const supportedDatasources = [
BitriseDatasource.id,
GitTagsDatasource.id,
];
export const urls = [
'https://devcenter.bitrise.io/en/steps-and-workflows/introduction-to-steps.html',
];
+1
View File
@@ -0,0 +1 @@
Updates step references of `bitrise.yml` files.
+12
View File
@@ -0,0 +1,12 @@
import { z } from 'zod';
export const BitriseStep = z.record(z.string(), z.unknown());
export const BitriseWorkflow = z.object({
steps: z.array(BitriseStep),
});
export const BitriseFile = z.object({
default_step_lib_source: z.string().optional(),
workflows: z.record(z.string(), BitriseWorkflow),
});
+28
View File
@@ -0,0 +1,28 @@
import { BitriseDatasource } from '../../datasource/bitrise';
import { parseStep } from './utils';
describe('modules/manager/bitrise/utils', () => {
describe('parseStep()', () => {
it('returns null on an empty string', () => {
expect(parseStep('')).toBeNull();
});
it('returns dependency for step', () => {
expect(parseStep('restore-gradle-cache@1.1.2')).toEqual({
currentValue: '1.1.2',
datasource: BitriseDatasource.id,
packageName: 'restore-gradle-cache',
replaceString: 'restore-gradle-cache@1.1.2',
});
});
it('parses missing version', () => {
expect(parseStep('share-pipeline-variable')).toEqual({
datasource: BitriseDatasource.id,
packageName: 'share-pipeline-variable',
replaceString: 'share-pipeline-variable',
skipReason: 'unspecified-version',
});
});
});
});
+85
View File
@@ -0,0 +1,85 @@
import is from '@sindresorhus/is';
import { BitriseDatasource } from '../../datasource/bitrise';
import { GitTagsDatasource } from '../../datasource/git-tags';
import type { PackageDependency } from '../types';
export function parseStep(
stepRef: string,
defaultRegistry?: string,
): PackageDependency | null {
if (is.emptyString(stepRef)) {
return null;
}
const dep: PackageDependency = {
datasource: BitriseDatasource.id,
replaceString: stepRef,
};
const [ref, currentValue] = stepRef.split('@', 2);
const refDep = parseStepRef(ref, defaultRegistry);
// no version
if (is.nullOrUndefined(currentValue)) {
return {
...dep,
packageName: stepRef,
skipReason: 'unspecified-version',
};
}
return {
...dep,
...refDep,
currentValue,
};
}
export function parseStepRef(
ref: string,
defaultRegistry?: string,
): PackageDependency {
// handle local path
// https://devcenter.bitrise.io/en/references/steps-reference/step-reference-id-format.html
if (ref.startsWith('path::')) {
return {
depName: ref.split('::', 2)[1],
skipReason: 'local-dependency',
};
}
// handle Git references
// https://devcenter.bitrise.io/en/references/steps-reference/step-reference-id-format.html
if (ref.startsWith('git::')) {
const [, packageName] = ref.split('::');
return {
packageName,
datasource: GitTagsDatasource.id,
};
}
// step library references
// https://devcenter.bitrise.io/en/references/steps-reference/step-reference-id-format.html
const splitted = ref.split('::', 2);
// reference which uses default registry
// - script:
if (splitted.length === 1) {
const [packageName] = splitted;
return {
packageName,
datasource: BitriseDatasource.id,
registryUrls: defaultRegistry ? [defaultRegistry] : undefined,
};
}
// reference which overwrites Bitrise registry
// https://github.com/bitrise-io/bitrise-steplib.git::script@1:
const [registryUrl, packageName] = splitted;
return {
packageName,
datasource: BitriseDatasource.id,
registryUrls: [registryUrl],
};
}