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")