diff --git a/lib/manager/terraform/__fixtures__/1.tf b/lib/manager/terraform/__fixtures__/1.tf index 5bb3886b382a36c3bedfb53a18590ab81a49a8ef..6199b622cda0d1efa689fa1403022a03e0b32fc2 100644 --- a/lib/manager/terraform/__fixtures__/1.tf +++ b/lib/manager/terraform/__fixtures__/1.tf @@ -92,7 +92,7 @@ module "addons_aws" { aws-ebs-csi-driver = { enabled = true is_default_class = true - version = "1.0.0" + version = "1.0.0" } @@ -143,7 +143,7 @@ provider "gitlab" { } provider "gitlab" { - token = "${var.gitlab_token}" + token = "${var.gitlab_token}" version = "=1.3" } @@ -189,6 +189,26 @@ module "gittags_ssh" { source = "git::ssh://git@bitbucket.com/hashicorp/example?ref=v1.0.3" } +module "bitbucket_ssh" { + source = "git::ssh://git@bitbucket.org/hashicorp/example.git?ref=v1.0.0" +} + +module "bitbucket_https" { + source = "git::https://git@bitbucket.org/hashicorp/example.git?ref=v1.0.0" +} + +module "bitbucket_plain" { + source = "bitbucket.org/hashicorp/example.git?ref=v1.0.0" +} + +module "bitbucket_subfolder" { + source = "bitbucket.org/hashicorp/example.git/terraform?ref=v1.0.0" +} + +module "bitbucket_subfolder_with_double_slash" { + source = "bitbucket.org/hashicorp/example.git//terraform?ref=v1.0.0" +} + terraform { required_providers { aws = ">= 2.7.0" diff --git a/lib/manager/terraform/__snapshots__/extract.spec.ts.snap b/lib/manager/terraform/__snapshots__/extract.spec.ts.snap index fa6022ae042664429c7bbfcfa347e002b01a1344..2a4c2b8e8b5a3b5e842201fd4a518b6985be5697 100644 --- a/lib/manager/terraform/__snapshots__/extract.spec.ts.snap +++ b/lib/manager/terraform/__snapshots__/extract.spec.ts.snap @@ -263,6 +263,41 @@ Object { "depType": "module", "lookupName": "ssh://git@bitbucket.com/hashicorp/example", }, + Object { + "currentValue": "v1.0.0", + "datasource": "bitbucket-tags", + "depName": "example", + "depType": "module", + "lookupName": "example", + }, + Object { + "currentValue": "v1.0.0", + "datasource": "bitbucket-tags", + "depName": "example", + "depType": "module", + "lookupName": "example", + }, + Object { + "currentValue": "v1.0.0", + "datasource": "bitbucket-tags", + "depName": "example", + "depType": "module", + "lookupName": "example", + }, + Object { + "currentValue": "v1.0.0", + "datasource": "bitbucket-tags", + "depName": "example", + "depType": "module", + "lookupName": "example", + }, + Object { + "currentValue": "v1.0.0", + "datasource": "bitbucket-tags", + "depName": "example", + "depType": "module", + "lookupName": "example", + }, Object { "currentValue": ">= 2.7.0", "datasource": "terraform-provider", @@ -285,7 +320,7 @@ Object { "depName": "hashicorp/terraform", "depType": "required_version", "extractVersion": "v(?<version>.*)$", - "lineNumber": 230, + "lineNumber": 250, }, Object { "currentValue": "2.7.2", diff --git a/lib/manager/terraform/extract.spec.ts b/lib/manager/terraform/extract.spec.ts index fe6f6f4b1fcf32e7efccc668ecf0ac740909f15e..1d4afed611462c62d3a8e53d8e34d4380c081458 100644 --- a/lib/manager/terraform/extract.spec.ts +++ b/lib/manager/terraform/extract.spec.ts @@ -35,7 +35,7 @@ describe('manager/terraform/extract', () => { it('extracts', async () => { const res = await extractPackageFile(tf1, '1.tf', {}); expect(res).toMatchSnapshot(); - expect(res.deps).toHaveLength(46); + expect(res.deps).toHaveLength(51); expect(res.deps.filter((dep) => dep.skipReason)).toHaveLength(8); }); diff --git a/lib/manager/terraform/modules.spec.ts b/lib/manager/terraform/modules.spec.ts index f7c1674e5a655ba8879cbd3b50e41bd305499c01..fcbc3fc33333b1f49691f0a6e9d6fc3537167e9a 100644 --- a/lib/manager/terraform/modules.spec.ts +++ b/lib/manager/terraform/modules.spec.ts @@ -1,4 +1,8 @@ -import { gitTagsRefMatchRegex, githubRefMatchRegex } from './modules'; +import { + bitbucketRefMatchRegex, + gitTagsRefMatchRegex, + githubRefMatchRegex, +} from './modules'; describe('manager/terraform/modules', () => { describe('githubRefMatchRegex', () => { @@ -60,4 +64,53 @@ describe('manager/terraform/modules', () => { expect(ssh.tag).toBe('v1.0.0'); }); }); + describe('bitbucketRefMatchRegex', () => { + it('should split workspace, project and tag from source', () => { + const ssh = bitbucketRefMatchRegex.exec( + 'git::ssh://git@bitbucket.org/hashicorp/example.git?ref=v1.0.0' + ).groups; + const https = bitbucketRefMatchRegex.exec( + 'git::https://git@bitbucket.org/hashicorp/example.git?ref=v1.0.0' + ).groups; + const plain = bitbucketRefMatchRegex.exec( + 'bitbucket.org/hashicorp/example.git?ref=v1.0.0' + ).groups; + const subfolder = bitbucketRefMatchRegex.exec( + 'bitbucket.org/hashicorp/example.git/terraform?ref=v1.0.0' + ).groups; + const subfolderWithDoubleSlash = bitbucketRefMatchRegex.exec( + 'bitbucket.org/hashicorp/example.git//terraform?ref=v1.0.0' + ).groups; + + expect(ssh.workspace).toBe('hashicorp'); + expect(ssh.project).toBe('example'); + expect(ssh.tag).toBe('v1.0.0'); + + expect(https.workspace).toBe('hashicorp'); + expect(https.project).toBe('example'); + expect(https.tag).toBe('v1.0.0'); + + expect(plain.workspace).toBe('hashicorp'); + expect(plain.project).toBe('example'); + expect(plain.tag).toBe('v1.0.0'); + + expect(subfolder.workspace).toBe('hashicorp'); + expect(subfolder.project).toBe('example'); + expect(subfolder.tag).toBe('v1.0.0'); + + expect(subfolderWithDoubleSlash.workspace).toBe('hashicorp'); + expect(subfolderWithDoubleSlash.project).toBe('example'); + expect(subfolderWithDoubleSlash.tag).toBe('v1.0.0'); + }); + + it('should parse alpha-numeric characters as well as dots, underscores, and dashes in repo names', () => { + const dots = bitbucketRefMatchRegex.exec( + 'bitbucket.org/hashicorp/example.repo-123.git?ref=v1.0.0' + ).groups; + + expect(dots.workspace).toBe('hashicorp'); + expect(dots.project).toBe('example.repo-123'); + expect(dots.tag).toBe('v1.0.0'); + }); + }); }); diff --git a/lib/manager/terraform/modules.ts b/lib/manager/terraform/modules.ts index 810c614c0ed0f9c09789e2bfe79a95501a88d263..cd387c739305795fb66ae046be75f15a8abb08a1 100644 --- a/lib/manager/terraform/modules.ts +++ b/lib/manager/terraform/modules.ts @@ -1,3 +1,4 @@ +import { BitBucketTagsDatasource } from '../../datasource/bitbucket-tags'; import { GitTagsDatasource } from '../../datasource/git-tags'; import * as datasourceGithubTags from '../../datasource/github-tags'; import { TerraformModuleDatasource } from '../../datasource/terraform-module'; @@ -12,6 +13,9 @@ import type { ExtractionResult } from './types'; export const githubRefMatchRegex = regEx( /github\.com([/:])(?<project>[^/]+\/[a-z0-9-_.]+).*\?ref=(?<tag>.*)$/i ); +export const bitbucketRefMatchRegex = regEx( + /(?:git::)?(?<url>(?:http|https|ssh)?(?::\/\/)?(?:.*@)?(?<path>bitbucket\.org\/(?<workspace>.*)\/(?<project>.*).git\/?(?<subfolder>.*)))\?ref=(?<tag>.*)$/ +); export const gitTagsRefMatchRegex = regEx( /(?:git::)?(?<url>(?:http|https|ssh):\/\/(?:.*@)?(?<path>.*.*\/(?<project>.*\/.*)))\?ref=(?<tag>.*)$/ ); @@ -31,6 +35,7 @@ export function extractTerraformModule( export function analyseTerraformModule(dep: PackageDependency): void { const githubRefMatch = githubRefMatchRegex.exec(dep.managerData.source); + const bitbucketRefMatch = bitbucketRefMatchRegex.exec(dep.managerData.source); const gitTagsRefMatch = gitTagsRefMatchRegex.exec(dep.managerData.source); if (githubRefMatch) { @@ -39,6 +44,12 @@ export function analyseTerraformModule(dep: PackageDependency): void { dep.depName = 'github.com/' + dep.lookupName; dep.currentValue = githubRefMatch.groups.tag; dep.datasource = datasourceGithubTags.id; + } else if (bitbucketRefMatch) { + dep.depType = 'module'; + dep.depName = bitbucketRefMatch.groups.project; + dep.lookupName = dep.depName; + dep.currentValue = bitbucketRefMatch.groups.tag; + dep.datasource = BitBucketTagsDatasource.id; } else if (gitTagsRefMatch) { dep.depType = 'module'; if (gitTagsRefMatch.groups.path.includes('//')) {