From 0434ecb11a9ff8f2c025c12d234846849a2e20b3 Mon Sep 17 00:00:00 2001 From: Mutahhir Hayat <mutahhir.hayat@hashicorp.com> Date: Tue, 15 Aug 2023 17:31:23 +0200 Subject: [PATCH 1/3] chore: add logging --- packages/@cdktf/commons/src/gradle.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@cdktf/commons/src/gradle.ts b/packages/@cdktf/commons/src/gradle.ts index c777cd123e..6e8080b322 100644 --- a/packages/@cdktf/commons/src/gradle.ts +++ b/packages/@cdktf/commons/src/gradle.ts @@ -5,6 +5,7 @@ import path from "path"; import fs from "fs-extra"; +import { logger } from "./logging"; export function isGradleProject(workingDirectory: string): boolean { const buildGradlePath = path.join(workingDirectory, "build.gradle"); @@ -13,6 +14,7 @@ export function isGradleProject(workingDirectory: string): boolean { fs.accessSync(buildGradlePath, fs.constants.R_OK | fs.constants.W_OK); return true; } catch { + logger.debug(`No build.gradle found at ${buildGradlePath}`); return false; } } From cbfeb6b55f7835f261ab45f9a59029bd07674569 Mon Sep 17 00:00:00 2001 From: Mutahhir Hayat <mutahhir.hayat@hashicorp.com> Date: Wed, 16 Aug 2023 11:16:47 +0200 Subject: [PATCH 2/3] fix: use gradle dependencies for provider info --- .../src/lib/dependencies/package-manager.ts | 65 +++------ packages/@cdktf/commons/src/debug.ts | 63 +-------- packages/@cdktf/commons/src/gradle.ts | 125 ++++++++++++++++++ 3 files changed, 147 insertions(+), 106 deletions(-) diff --git a/packages/@cdktf/cli-core/src/lib/dependencies/package-manager.ts b/packages/@cdktf/cli-core/src/lib/dependencies/package-manager.ts index 15d4fc66ec..e456672114 100644 --- a/packages/@cdktf/cli-core/src/lib/dependencies/package-manager.ts +++ b/packages/@cdktf/cli-core/src/lib/dependencies/package-manager.ts @@ -6,6 +6,8 @@ import { Errors, logger, isGradleProject, + getGradleDependencies, + getDependencyInformationFromLine, } from "@cdktf/commons"; import { existsSync } from "fs-extra"; import path from "path"; @@ -649,67 +651,42 @@ class GradlePackageManager extends JavaPackageManager { packageName: string, packageVersion = "latest.release" ): Promise<void> { - const dependencyRegex = - /dependencies {(\s*(implementation|api) ('|")(\S)*('|")\s*\n)*}/gm; - const buildGradlePath = path.join(this.workingDirectory, "build.gradle"); const buildGradle = await fs.readFile(buildGradlePath, "utf8"); + const buildGradleLines = buildGradle.split(/\r?\n/); - const dependencies = dependencyRegex.exec(buildGradle); - if (!dependencies || !dependencies.index) { + const dependenciesRegex = /dependencies\s+\{/i; + const dependencyBlockStart = buildGradleLines.findIndex((line) => + dependenciesRegex.test(line) + ); + if (dependencyBlockStart === -1) { throw Errors.Usage( "Could not find dependencies section in the build.gradle" ); } - // Find the index of the opening bracket of the dependencies section - const dependenciesStartIndex = buildGradle.indexOf("{", dependencies.index); - const newBuildGradle = - buildGradle.slice(0, dependenciesStartIndex + 1) + - `\n implementation '${packageName}:${packageVersion}'` + - buildGradle.slice(dependenciesStartIndex + 1); - await fs.writeFile(buildGradlePath, newBuildGradle); + const newPackageDependency = `\timplementation '${packageName}:${packageVersion}'`; + buildGradleLines.splice(dependencyBlockStart + 1, 0, newPackageDependency); + await fs.writeFile(buildGradlePath, buildGradleLines.join("\n")); } public async listProviderPackages(): Promise< { name: string; version: string }[] > { - const dependencyRegex = - /dependencies {(\s*(implementation|api) ('|")(\S)*('|")\s*\n)*}/gm; - - const buildGradlePath = path.join(this.workingDirectory, "build.gradle"); - const buildGradle = await fs.readFile(buildGradlePath, "utf8"); - const dependencies = buildGradle.match(dependencyRegex); + const dependencies = await getGradleDependencies(); if (!dependencies) { - throw Errors.Usage( - "Could not find dependencies section in the build.gradle" - ); - } - const dependencyBody = dependencies[0]; - // e.g. implementation 'com.hashicorp:cdktf-provider-google:8.0.8' - // or api 'com.hashicorp:cdktf-provider-google:8.0.8' - // We need to get the name and version from this - const dependencyRegex2 = /(\S)*('|")/gm; - const dependencyLines = dependencyBody.match(dependencyRegex2); - if (!dependencyLines) { - throw Errors.Usage( - "Could not find dependencies section in the build.gradle" - ); + throw Errors.Usage("Could not find any dependencies"); } - function removeQuotes(str: string): string { - return str.replace(/['"]+/g, ""); - } - - const dependencyList = dependencyLines - .map((line) => { - const dependency = line.split(":"); - return { - name: `${removeQuotes(dependency[0])}.${dependency[1]}`, - version: removeQuotes(dependency[2]), - }; + const dependencyList = dependencies + .map((line) => getDependencyInformationFromLine(line)) + .filter((dep) => { + if (!dep) { + return false; + } + return dep.name.includes("cdktf-provider-"); }) - .filter(({ name }) => name.startsWith("com.hashicorp.cdktf-provider-")); + .map((dep) => ({ name: dep!.name, version: dep!.version })); return dependencyList; } diff --git a/packages/@cdktf/commons/src/debug.ts b/packages/@cdktf/commons/src/debug.ts index 0e693c78ef..1f12f3d73c 100644 --- a/packages/@cdktf/commons/src/debug.ts +++ b/packages/@cdktf/commons/src/debug.ts @@ -7,7 +7,7 @@ import { exec } from "./util"; import { terraformVersion } from "./terraform"; import { DISPLAY_VERSION } from "./version"; import { pathExists } from "fs-extra"; -import { isGradleProject } from "./gradle"; +import { getGradlePackageVersion, isGradleProject } from "./gradle"; export function getLanguage(projectPath = process.cwd()): string | undefined { try { @@ -336,67 +336,6 @@ async function getMavenPackageVersion(packageName: string) { return versionLine.substring(versionStart, versionEnd); } -/* - * Example output: - implementation - Implementation dependencies for the 'main' feature. (n) - +--- com.hashicorp:cdktf:0.18.0 (n) - +--- software.constructs:constructs:10.0.25 (n) - +--- junit:junit:4.13.2 (n) - \--- org.junit.jupiter:junit-jupiter:5.8.0 (n) -*/ -async function getGradlePackageVersion(packageName: string) { - const translationMap: Record<string, string> = { - jsii: "jsii-runtime", - }; - const gradlePackageName = translationMap[packageName] || packageName; - - let output; - try { - output = await exec("gradle", ["dependencies", "--console=plain"], { - env: process.env, - }); - } catch (e) { - logger.debug(`Unable to run 'gradle dependencies': ${e}`); - return undefined; - } - - const lines = output.split(/\r\n|\r|\n/); - - // find the implementation section - const implementationSection = lines.findIndex((line) => - line.includes("implementation - ") - ); - if (implementationSection === -1) { - logger.debug( - `Unable to find implementation section in output of 'gradle dependencies': ${output}` - ); - return undefined; - } - - // loop through the subsequent lines to find the one starting with package name - for (let i = implementationSection + 1; i < lines.length; i++) { - const line = lines[i]; - if (line.includes(`:${gradlePackageName}:`)) { - const packageNameRegex = /^.*\s+[^:]+:[^:]+:([^\s]+)/; - const matches = line.match(packageNameRegex); - if (!matches) { - logger.debug( - "Unexpected format for gradle build. Please file an issue at https://cdk.tf/bug" - ); - return undefined; - } - - return matches[1]; - } - - if (line.trim() === "") { - break; - } - } - - return undefined; -} - async function getJavaPackageVersion(packageName: string) { if (isGradleProject("./")) { return getGradlePackageVersion(packageName); diff --git a/packages/@cdktf/commons/src/gradle.ts b/packages/@cdktf/commons/src/gradle.ts index 6e8080b322..b9ba5df0ad 100644 --- a/packages/@cdktf/commons/src/gradle.ts +++ b/packages/@cdktf/commons/src/gradle.ts @@ -6,6 +6,7 @@ import path from "path"; import fs from "fs-extra"; import { logger } from "./logging"; +import { exec } from "./util"; export function isGradleProject(workingDirectory: string): boolean { const buildGradlePath = path.join(workingDirectory, "build.gradle"); @@ -18,3 +19,127 @@ export function isGradleProject(workingDirectory: string): boolean { return false; } } + +export async function getGradleDependencies() { + let output; + try { + output = await exec("gradle", ["dependencies", "--console=plain"], { + env: process.env, + }); + } catch (e) { + logger.debug(`Unable to run 'gradle dependencies': ${e}`); + return undefined; + } + + const lines = output.split(/\r\n|\r|\n/); + + // find the implementation section + const implementationSection = lines.findIndex((line) => + line.includes("implementation - ") + ); + if (implementationSection === -1) { + logger.debug( + `Unable to find implementation section in output of 'gradle dependencies': ${output}` + ); + return undefined; + } + + const emptyLineRegex = /^\s*$/; + const implementationSectionLines = lines.slice(implementationSection + 1); + const sectionEnd = implementationSectionLines.findIndex((line) => + emptyLineRegex.test(line) + ); + const implementationLines = implementationSectionLines.slice(0, sectionEnd); + + // find the api section + const apiSection = lines.findIndex((line) => line.includes("api - ")); + if (apiSection === -1) { + logger.debug( + `Unable to find api section in output of 'gradle dependencies': ${output}` + ); + return undefined; + } + + const apiSectionLines = lines.slice(apiSection + 1); + const apiSectionEnd = apiSectionLines.findIndex((line) => + emptyLineRegex.test(line) + ); + + const apiLines = apiSectionLines.slice(0, apiSectionEnd); + + const prefixRegex = /^\s*[+\\-]+\s*/; + const suffixRegex = /\s+\([nc\*]\)\s*$/; + + return [...implementationLines, ...apiLines] + .filter((line) => line !== "No dependencies") + .map((line) => line.replace(prefixRegex, "").replace(suffixRegex, "")); +} + +export type DependencyInformation = { + group: string; + name: string; + version: string; +}; +export function getDependencyInformationFromLine( + line: string +): DependencyInformation | undefined { + const packageNameRegex = /^\s*([^:]+):([^:]+)(?::([^\s]+))?/; + const matches = line.match(packageNameRegex); + if (!matches) { + logger.debug( + "Unexpected format for gradle build. Please file an issue at https://cdk.tf/bug" + ); + return undefined; + } + + if (matches[3] === undefined) { + return { + group: "", + name: matches[1], + version: matches[2], + }; + } + + return { + group: matches[1], + name: matches[2], + version: matches[3], + }; +} + +/* + * Example output: + implementation - Implementation dependencies for the 'main' feature. (n) + +--- com.hashicorp:cdktf:0.18.0 (n) + +--- software.constructs:constructs:10.0.25 (n) + +--- junit:junit:4.13.2 (n) + \--- org.junit.jupiter:junit-jupiter:5.8.0 (n) +*/ +export async function getGradlePackageVersion(packageName: string) { + const translationMap: Record<string, string> = { + jsii: "jsii-runtime", + }; + const gradlePackageName = translationMap[packageName] || packageName; + logger.debug( + "Running 'gradle dependencies' to find package version", + gradlePackageName + ); + + const lines = await getGradleDependencies(); + if (!lines || lines.length === 0) { + return undefined; + } + + // loop through the subsequent lines to find the one starting with package name + for (const line of lines) { + if (line.includes(`:${gradlePackageName}:`)) { + const dep = getDependencyInformationFromLine(line); + if (dep) { + return dep.version; + } + return undefined; + } + } + + return undefined; +} From fc14bdff19d4e799178b7c795bea731dcfac96ec Mon Sep 17 00:00:00 2001 From: Mutahhir Hayat <mutahhir.hayat@hashicorp.com> Date: Wed, 16 Aug 2023 11:28:25 +0200 Subject: [PATCH 3/3] fix: linter warning in regex --- packages/@cdktf/commons/src/gradle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@cdktf/commons/src/gradle.ts b/packages/@cdktf/commons/src/gradle.ts index b9ba5df0ad..c57ce9ded6 100644 --- a/packages/@cdktf/commons/src/gradle.ts +++ b/packages/@cdktf/commons/src/gradle.ts @@ -68,7 +68,7 @@ export async function getGradleDependencies() { const apiLines = apiSectionLines.slice(0, apiSectionEnd); const prefixRegex = /^\s*[+\\-]+\s*/; - const suffixRegex = /\s+\([nc\*]\)\s*$/; + const suffixRegex = /\s+\([nc*]\)\s*$/; return [...implementationLines, ...apiLines] .filter((line) => line !== "No dependencies")