rew! refactor(manager/buildkite): match git-URL plugin refs via the YAML walk

As a follow-up to the previous commit, which moved plain
`name#version` plugin matching from line-based regex onto the new YAML
walk, this carries the git-URL matching over too
(`ssh://git@host/org/repo#ref`/`https://host/org/repo#ref`, including
the Bitbucket-vs-GitHub datasource split and `.git` suffix stripping),
so `extractPluginDep` has a single way of recognising a plugin
reference instead of two.

This is carried over unchanged from the pre-existing line-based regex
- behaviour is identical, just matched against the parsed YAML key
instead of a raw line. Splitting it into its own commit keeps the
previous commit's diff to the plain-name case only.

Co-Authored-By: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
This commit is contained in:
Jamie Tanna
2026-10-11 16:56:43 +01:00
parent e4c02e7964
commit 830ee71bda
2 changed files with 120 additions and 0 deletions
@@ -141,6 +141,104 @@ describe('modules/manager/buildkite/extract', () => {
]);
});
it('extracts git-based plugins', () => {
const fileContent = codeBlock`
steps:
- ssh://git@github.company.com/some-org/some-plugin#v3.2.7:
username: abc
- https://github.company.com/some-third-org/some-third-plugin#v0.0.1:
build: app
`;
const res = extractPackageFile(fileContent)?.deps;
expect(res).toEqual([
{
currentValue: 'v3.2.7',
datasource: 'github-tags',
depName: 'some-org/some-plugin',
registryUrls: ['https://github.company.com'],
},
{
currentValue: 'v0.0.1',
datasource: 'github-tags',
depName: 'some-third-org/some-third-plugin',
registryUrls: ['https://github.company.com'],
},
]);
});
it('extracts git-based plugin with .git at the end of its name', () => {
const expectedPackageDependency: PackageDependency = {
currentValue: 'v3.2.7',
datasource: 'github-tags',
depName: 'some-org/some-plugin',
registryUrls: ['https://github.company.com'],
};
const fileContent = codeBlock`
steps:
- ssh://git@github.company.com/some-org/some-plugin.git#v3.2.7:
`;
const res = extractPackageFile(fileContent)?.deps;
expect(res).toHaveLength(1);
expect(res).toEqual([expectedPackageDependency]);
});
it('extracts plugins outside plugins sections', () => {
const fileContent = codeBlock`
.docker-options: &some-options
copy-checkout: true
.python3-container: &python3-container
ssh://git@github.some-domain.com/some-org/some-plugin#v3.2.7:
some-config: some-value
<<: *some-options
`;
const res = extractPackageFile(fileContent)?.deps;
const expectedPackageDependency: PackageDependency = {
currentValue: 'v3.2.7',
datasource: 'github-tags',
depName: 'some-org/some-plugin',
registryUrls: ['https://github.some-domain.com'],
};
expect(res).toEqual([expectedPackageDependency]);
});
it('extracts plugin with preceding ?', () => {
const fileContent = codeBlock`
steps:
- ? ssh://git@github.company.com/some-org/some-plugin.git#v3.2.7
`;
const res = extractPackageFile(fileContent)?.deps;
const expectedPackageDependency: PackageDependency = {
currentValue: 'v3.2.7',
datasource: 'github-tags',
depName: 'some-org/some-plugin',
registryUrls: ['https://github.company.com'],
};
expect(res).toEqual([expectedPackageDependency]);
});
it('extracts plugin tags from bitbucket', () => {
const fileContent = codeBlock`
steps:
- plugins:
- ssh://git@bitbucket.org/some-org/some-plugin.git#v3.2.7:
- docker-compose#v1.3.2:
`;
const res = extractPackageFile(fileContent)?.deps;
const githubDependency: PackageDependency = {
currentValue: 'v1.3.2',
datasource: 'github-tags',
depName: 'docker-compose',
packageName: 'buildkite-plugins/docker-compose-buildkite-plugin',
};
const bitbucketDependency: PackageDependency = {
currentValue: 'v3.2.7',
datasource: 'bitbucket-tags',
depName: 'some-org/some-plugin',
registryUrls: ['https://bitbucket.org'],
};
expect(res).toEqual([bitbucketDependency, githubDependency]);
});
it('extracts plugin tags with quotes', () => {
const fileContent = codeBlock`
steps:
+22
View File
@@ -2,11 +2,16 @@ import { isObject } from '@sindresorhus/is';
import { logger } from '../../../logger/index.ts';
import { regEx } from '../../../util/regex.ts';
import { parseSingleYaml } from '../../../util/yaml.ts';
import { BitbucketTagsDatasource } from '../../datasource/bitbucket-tags/index.ts';
import { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';
import { isVersion } from '../../versioning/semver/index.ts';
import type { PackageDependency, PackageFileContent } from '../types.ts';
const pluginKeyRegex = regEx(/^(?<depName>[^#\s]+)#(?<currentValue>[^:]+)$/);
const gitPluginRegex = regEx(
/(?:ssh:\/\/git@|https:\/\/)(?<registry>[^/]+)\/(?<gitPluginName>.*)/,
);
const gitSuffixRegex = regEx('\\.git$');
function extractPluginDep(key: string): PackageDependency | null {
const match = pluginKeyRegex.exec(key);
@@ -16,6 +21,23 @@ function extractPluginDep(key: string): PackageDependency | null {
const { depName, currentValue } = match.groups;
logger.trace(`Found Buildkite plugin ${depName}`);
// Plugins may simply be git repos. If so, we need to parse out the registry.
const gitPluginMatch = gitPluginRegex.exec(depName);
if (gitPluginMatch?.groups) {
const { registry, gitPluginName } = gitPluginMatch.groups;
const gitDepName = gitPluginName.replace(gitSuffixRegex, '');
const datasource =
registry === 'bitbucket.org'
? BitbucketTagsDatasource.id
: GithubTagsDatasource.id;
return {
depName: gitDepName,
currentValue,
registryUrls: [`https://${registry}`],
datasource,
};
}
if (!isVersion(currentValue)) {
logger.debug(
`Skipping non-pinned Buildkite current version ${currentValue}`,