diff --git a/lib/modules/manager/tekton/__fixtures__/multi-doc-annotations.yaml b/lib/modules/manager/tekton/__fixtures__/multi-doc-annotations.yaml new file mode 100644 index 00000000000000..70ad497472769c --- /dev/null +++ b/lib/modules/manager/tekton/__fixtures__/multi-doc-annotations.yaml @@ -0,0 +1,26 @@ +--- +kind: PipelineRun +metadata: + annotations: + pipelinesascode.tekton.dev/on-event: "[pull_request]" + pipelinesascode.tekton.dev/task: "[git-clone,https://github.com/foo/bar/releases/download/v0.0.4/stakater-create-git-tag.yaml]" +--- +kind: PipelineRun +metadata: + annotations: + pipelinesascode.tekton.dev/task: "[git-clone, + https://raw.githubusercontent.com/foo/bar/v0.0.6/tasks/create-git-tag/create-git-tag.yaml]" +--- +kind: PipelineRun +metadata: + annotations: + pipelinesascode.tekton.dev/task: "git-clone" + pipelinesascode.tekton.dev/task-1: "https://github.com/foo/bar/raw/v0.0.8/tasks/create-git-tag/create-git-tag.yaml" +--- +kind: PipelineRun +metadata: + annotations: + pipelinesascode.tekton.dev/task: "[git-clone, + https://github.com/foo/bar/releases/download/v0.0.9/stakater-create-git-tag.yaml, + https://github.com/foo/bar/raw/v0.0.7/tasks/create-git-tag/create-git-tag.yaml, + https://raw.githubusercontent.com/foo/bar/v0.0.5/tasks/create-git-tag/create-git-tag.yaml]" diff --git a/lib/modules/manager/tekton/extract.spec.ts b/lib/modules/manager/tekton/extract.spec.ts index 74df01953ae258..449fe10217d8a4 100644 --- a/lib/modules/manager/tekton/extract.spec.ts +++ b/lib/modules/manager/tekton/extract.spec.ts @@ -12,6 +12,59 @@ describe('modules/manager/tekton/extract', () => { expect(result?.deps).toHaveLength(39); }); + it('extracts deps from a file in annotations', () => { + const result = extractPackageFile( + Fixtures.get('multi-doc-annotations.yaml'), + 'test-file.yaml', + ); + expect(result).toEqual({ + deps: [ + { + currentValue: 'v0.0.4', + datasource: 'github-releases', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'foo/bar', + }, + { + currentValue: 'v0.0.6', + datasource: 'git-tags', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'https://github.com/foo/bar', + }, + { + currentValue: 'v0.0.8', + datasource: 'git-tags', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'https://github.com/foo/bar', + }, + { + currentValue: 'v0.0.9', + datasource: 'github-releases', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'foo/bar', + }, + { + currentValue: 'v0.0.7', + datasource: 'git-tags', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'https://github.com/foo/bar', + }, + { + currentValue: 'v0.0.5', + datasource: 'git-tags', + depName: 'github.com/foo/bar', + depType: 'tekton-annotation', + packageName: 'https://github.com/foo/bar', + }, + ], + }); + }); + it('ignores file without any deps', () => { expect(extractPackageFile('foo: bar', 'test-file.yaml')).toBeNull(); }); diff --git a/lib/modules/manager/tekton/extract.ts b/lib/modules/manager/tekton/extract.ts index e6973d3df1f01e..1a56a07d82981e 100644 --- a/lib/modules/manager/tekton/extract.ts +++ b/lib/modules/manager/tekton/extract.ts @@ -1,7 +1,10 @@ import is from '@sindresorhus/is'; import { logger } from '../../../logger'; import { coerceArray } from '../../../util/array'; +import { regEx } from '../../../util/regex'; import { parseYaml } from '../../../util/yaml'; +import { GitTagsDatasource } from '../../datasource/git-tags'; +import { GithubReleasesDatasource } from '../../datasource/github-releases'; import { getDep } from '../dockerfile/extract'; import type { PackageDependency, PackageFileContent } from '../types'; import type { @@ -52,6 +55,8 @@ function getDeps(doc: TektonResource): PackageDependency[] { // Handle PipelineRun resource addDep(doc.spec?.pipelineRef, deps); + addPipelineAsCodeAnnotations(doc.metadata?.annotations, deps); + // Handle PipelineRun resource with inline Pipeline definition const pipelineSpec = doc.spec?.pipelineSpec; if (is.truthy(pipelineSpec)) { @@ -80,6 +85,77 @@ function getDeps(doc: TektonResource): PackageDependency[] { return deps; } +const taskAnnotation = regEx(/^pipelinesascode\.tekton\.dev\/task(-[0-9]+)?$/); + +function addPipelineAsCodeAnnotations( + annotations: Record | undefined | null, + deps: PackageDependency[], +): void { + if (is.nullOrUndefined(annotations)) { + return; + } + + for (const [key, value] of Object.entries(annotations)) { + if (!taskAnnotation.test(key)) { + continue; + } + + const tasks = value + .replace(regEx(/]$/), '') + .replace(regEx(/^\[/), '') + .split(','); + for (const task of tasks) { + const dep = getAnnotationDep(task.trim()); + if (!dep) { + continue; + } + deps.push(dep); + } + } +} + +const githubRelease = regEx( + /^(?(?:(?:http|https):\/\/)?(?(?:[^:/\s]+[:/])?(?[^/\s]+\/[^/\s]+)))\/releases\/download\/(?.+)\/(?[^?\s]*)$/, +); + +const gitUrl = regEx( + /^(?(?:(?:http|https):\/\/)?(?(?:[^:/\s]+[:/])?(?[^/\s]+\/[^/\s]+)))(?:\/raw)?\/(?.+?)\/(?[^?\s]*)$/, +); + +function getAnnotationDep(url: string): PackageDependency | null { + const dep: PackageDependency = {}; + dep.depType = 'tekton-annotation'; + + let groups = githubRelease.exec(url)?.groups; + + if (groups) { + dep.datasource = GithubReleasesDatasource.id; + + dep.depName = groups.path; + dep.packageName = groups.project; + dep.currentValue = groups.currentValue; + return dep; + } + + groups = gitUrl.exec(url)?.groups; + if (groups) { + dep.datasource = GitTagsDatasource.id; + + dep.depName = groups.path.replace( + 'raw.githubusercontent.com', + 'github.com', + ); + dep.packageName = groups.url.replace( + 'raw.githubusercontent.com', + 'github.com', + ); + dep.currentValue = groups.currentValue; + return dep; + } + + return null; +} + function addDep(ref: TektonBundle, deps: PackageDependency[]): void { if (is.falsy(ref)) { return; diff --git a/lib/modules/manager/tekton/index.ts b/lib/modules/manager/tekton/index.ts index a578c6f00619c3..33ee8e82288ae1 100644 --- a/lib/modules/manager/tekton/index.ts +++ b/lib/modules/manager/tekton/index.ts @@ -1,5 +1,6 @@ import type { Category } from '../../../constants'; import { DockerDatasource } from '../../datasource/docker'; +import { GitTagsDatasource } from '../../datasource/git-tags'; import { extractPackageFile } from './extract'; export const defaultConfig = { @@ -8,6 +9,6 @@ export const defaultConfig = { export const categories: Category[] = ['ci', 'cd']; -export const supportedDatasources = [DockerDatasource.id]; +export const supportedDatasources = [DockerDatasource.id, GitTagsDatasource.id]; export { extractPackageFile }; diff --git a/lib/modules/manager/tekton/readme.md b/lib/modules/manager/tekton/readme.md index 51429924a71fad..9e0f08f219e051 100644 --- a/lib/modules/manager/tekton/readme.md +++ b/lib/modules/manager/tekton/readme.md @@ -12,9 +12,28 @@ They can be created directly as a Kubernetes resource with standard tools like ` Tasks and Pipeline definitions can also live outside the Kubernetes cluster and get fetched by Tekton when needed, this approach relies on Tekton resource references rather than the resource definition. The `tekton` manager focuses on providing updates to Tekton resource references. -Right now, Renovate's Tekton manager only supports references that are [Bundles](https://tekton.dev/docs/pipelines/tekton-bundle-contracts/). +Right now, Renovate's Tekton manager supports references that are [Bundles](https://tekton.dev/docs/pipelines/tekton-bundle-contracts/) and [PipelinesAsCode](https://pipelinesascode.com) with [remote HTTP URL resolver](https://pipelinesascode.com/docs/guide/resolver/#remote-http-url). Read the [Tekton Pipeline remote resolution docs](https://tekton.dev/docs/pipelines/resolution/) for the different kinds of Tekton references and their corresponding resolvers. +### Using a PipelinesAsCode remote URL reference + +By specifying the annotation with a remote task based on the recommended way using [git based versioning](https://github.com/tektoncd/community/blob/main/teps/0115-tekton-catalog-git-based-versioning.md). How this can be used can be seen in the example below. + +```yaml title="How an annotation in could look like in an pipeline-run.yaml" +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + name: main + annotations: + pipelinesascode.tekton.dev/task: 'https://github.com/foo/bar/raw/v0.0.1/tasks/task/task.yaml' +``` + +Supported URLs: + +1. < +2. +3. + ### Using a Tekton Bundle reference There are three ways to use a Tekton Bundle reference: diff --git a/lib/modules/manager/tekton/types.ts b/lib/modules/manager/tekton/types.ts index 2140457fc76eef..e0b33c7dfd4b22 100644 --- a/lib/modules/manager/tekton/types.ts +++ b/lib/modules/manager/tekton/types.ts @@ -1,6 +1,9 @@ export interface TektonResource { spec: TektonResourceSpec; items?: TektonResource[]; + metadata?: { + annotations: Record; + }; } export interface TektonResourceSpec {