From 6b1fdbecc4b5344c26793489a850c867102fd29d Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 31 Mar 2024 18:23:38 -0700 Subject: [PATCH 01/19] feat(git-clone): add support for tree git clone url --- git-clone/main.test.ts | 65 ++++++++++++++++++++++++++++++++++++++++++ git-clone/main.tf | 19 ++++++++++-- git-clone/run.sh | 15 ++++++++++ 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 0c3dd54c..a23aa8ad 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -14,6 +14,71 @@ describe("git-clone", async () => { url: "foo", }); + it("repo_dir should match repo name for https", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://github.com/coder/coder.git", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.clone_url.value).toEqual( + "https://github.com/coder/coder.git", + ); + expect(state.outputs.branch_name.value).toEqual(""); + }); + + it("repo_dir should match repo name for https without .git", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://github.com/coder/coder", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.clone_url.value).toEqual( + "https://github.com/coder/coder", + ); + expect(state.outputs.branch_name.value).toEqual(""); + }); + + it("repo_dir should match repo name for ssh", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "git@github.com:coder/coder.git", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.clone_url.value).toEqual( + "git@github.com:coder/coder.git", + ); + expect(state.outputs.branch_name.value).toEqual(""); + }); + + it("repo_dir should match repo name with gitlab tree url", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.clone_url.value).toEqual( + "https://gitlab.com/mike.brew/repo-tests.log", + ); + expect(state.outputs.branch_name.value).toEqual("feat/branch"); + }); + + it("repo_dir should match repo name with github tree url", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://github.com/michaelbrewer/repo-tests.log/tree/feat/branch", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.clone_url.value).toEqual( + "https://github.com/michaelbrewer/repo-tests.log", + ); + expect(state.outputs.branch_name.value).toEqual("feat/branch"); + }); + it("fails without git", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", diff --git a/git-clone/main.tf b/git-clone/main.tf index c1e65cfd..22a44c91 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -26,7 +26,9 @@ variable "agent_id" { } locals { - clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(var.url), ".git", "")]) : join("/", ["~", replace(basename(var.url), ".git", "")]) + clone_url = replace(replace(var.url, "//-/tree/.*/", ""), "//tree/.*/", "") + branch_name = replace(replace(replace(var.url, local.clone_url, ""), "/.*/-/tree//", ""), "/.*/tree//", "") + clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(local.clone_url), ".git", "")]) : join("/", ["~", replace(basename(local.clone_url), ".git", "")]) } output "repo_dir" { @@ -34,11 +36,22 @@ output "repo_dir" { description = "Full path of cloned repo directory" } +output "clone_url" { + value = local.clone_url + description = "Git repository URL" +} + +output "branch_name" { + value = local.branch_name + description = "Git branch" +} + resource "coder_script" "git_clone" { agent_id = var.agent_id script = templatefile("${path.module}/run.sh", { - CLONE_PATH = local.clone_path - REPO_URL : var.url, + CLONE_PATH = local.clone_path, + REPO_URL : local.clone_url, + BRANCH_NAME : local.branch_name, }) display_name = "Git Clone" icon = "/icon/git.svg" diff --git a/git-clone/run.sh b/git-clone/run.sh index df647a11..5a8ac249 100755 --- a/git-clone/run.sh +++ b/git-clone/run.sh @@ -2,6 +2,7 @@ REPO_URL="${REPO_URL}" CLONE_PATH="${CLONE_PATH}" +BRANCH_NAME="${BRANCH_NAME}" # Expand home if it's specified! CLONE_PATH="$${CLONE_PATH/#\~/$${HOME}}" @@ -35,6 +36,20 @@ fi if [ -z "$(ls -A "$CLONE_PATH")" ]; then echo "Cloning $REPO_URL to $CLONE_PATH..." git clone "$REPO_URL" "$CLONE_PATH" + + # Return the exit code of the last command + exit_code=$? + if [ $exit_code -ne 0 ]; then + exit $exit_code + fi + + # If BRANCH_NAME is set of a non-blank value, switch to that branch + if [ -n "$BRANCH_NAME" ]; then + echo "Switch to branch $BRANCH_NAME..." + cd "$CLONE_PATH" || exit 1 + git switch "$BRANCH_NAME" + cd - || exit 1 + fi else echo "$CLONE_PATH already exists and isn't empty, skipping clone!" exit 0 From 96367b86572199a71d368581248d9e2095503e62 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 31 Mar 2024 18:26:28 -0700 Subject: [PATCH 02/19] bun fmt --- git-clone/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/run.sh b/git-clone/run.sh index 5a8ac249..ffb305aa 100755 --- a/git-clone/run.sh +++ b/git-clone/run.sh @@ -40,7 +40,7 @@ if [ -z "$(ls -A "$CLONE_PATH")" ]; then # Return the exit code of the last command exit_code=$? if [ $exit_code -ne 0 ]; then - exit $exit_code + exit $exit_code fi # If BRANCH_NAME is set of a non-blank value, switch to that branch From 002a8b5d37643c9fe42b2edb48914587300fcbff Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 31 Mar 2024 20:06:05 -0700 Subject: [PATCH 03/19] tests(git-clone): add real tests --- git-clone/main.test.ts | 52 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index a23aa8ad..cf11de50 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -14,6 +14,29 @@ describe("git-clone", async () => { url: "foo", }); + it("fails without git", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "some-url", + }); + const output = await executeScriptInContainer(state, "alpine"); + expect(output.exitCode).toBe(1); + expect(output.stdout).toEqual(["Git is not installed!"]); + }); + + it("runs with git", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "fake-url", + }); + const output = await executeScriptInContainer(state, "alpine/git"); + expect(output.exitCode).toBe(128); + expect(output.stdout).toEqual([ + "Creating directory ~/fake-url...", + "Cloning fake-url to ~/fake-url...", + ]); + }); + it("repo_dir should match repo name for https", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", @@ -79,26 +102,35 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); - it("fails without git", async () => { + it("runs with github clone with switch to feat/branch", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - url: "some-url", + url: "https://github.com/michaelbrewer/repo-tests.log/tree/feat/branch", }); - const output = await executeScriptInContainer(state, "alpine"); - expect(output.exitCode).toBe(1); - expect(output.stdout).toEqual(["Git is not installed!"]); + const output = await executeScriptInContainer(state, "alpine/git"); + expect(output.exitCode).toBe(0); + expect(output.stdout).toEqual([ + "Creating directory ~/repo-tests.log...", + "Cloning https://github.com/michaelbrewer/repo-tests.log to ~/repo-tests.log...", + "Switch to branch feat/branch...", + "branch 'feat/branch' set up to track 'origin/feat/branch'.", + "/git", + ]); }); - it("runs with git", async () => { + it("runs with gitlab clone with switch to feat/branch", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - url: "fake-url", + url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch", }); const output = await executeScriptInContainer(state, "alpine/git"); - expect(output.exitCode).toBe(128); + expect(output.exitCode).toBe(0); expect(output.stdout).toEqual([ - "Creating directory ~/fake-url...", - "Cloning fake-url to ~/fake-url...", + "Creating directory ~/repo-tests.log...", + "Cloning https://gitlab.com/mike.brew/repo-tests.log to ~/repo-tests.log...", + "Switch to branch feat/branch...", + "branch 'feat/branch' set up to track 'origin/feat/branch'.", + "/git", ]); }); }); From b525c785b4ca4218f3db8c7a8ead916e733b8b6d Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 31 Mar 2024 20:22:51 -0700 Subject: [PATCH 04/19] feat: used -b for clone --- git-clone/main.test.ts | 10 ++-------- git-clone/run.sh | 21 ++++++--------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index cf11de50..8370e503 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -111,10 +111,7 @@ describe("git-clone", async () => { expect(output.exitCode).toBe(0); expect(output.stdout).toEqual([ "Creating directory ~/repo-tests.log...", - "Cloning https://github.com/michaelbrewer/repo-tests.log to ~/repo-tests.log...", - "Switch to branch feat/branch...", - "branch 'feat/branch' set up to track 'origin/feat/branch'.", - "/git", + "Cloning https://github.com/michaelbrewer/repo-tests.log to ~/repo-tests.log on branch feat/branch...", ]); }); @@ -127,10 +124,7 @@ describe("git-clone", async () => { expect(output.exitCode).toBe(0); expect(output.stdout).toEqual([ "Creating directory ~/repo-tests.log...", - "Cloning https://gitlab.com/mike.brew/repo-tests.log to ~/repo-tests.log...", - "Switch to branch feat/branch...", - "branch 'feat/branch' set up to track 'origin/feat/branch'.", - "/git", + "Cloning https://gitlab.com/mike.brew/repo-tests.log to ~/repo-tests.log on branch feat/branch...", ]); }); }); diff --git a/git-clone/run.sh b/git-clone/run.sh index ffb305aa..bd807177 100755 --- a/git-clone/run.sh +++ b/git-clone/run.sh @@ -34,21 +34,12 @@ fi # Check if the directory is empty # and if it is, clone the repo, otherwise skip cloning if [ -z "$(ls -A "$CLONE_PATH")" ]; then - echo "Cloning $REPO_URL to $CLONE_PATH..." - git clone "$REPO_URL" "$CLONE_PATH" - - # Return the exit code of the last command - exit_code=$? - if [ $exit_code -ne 0 ]; then - exit $exit_code - fi - - # If BRANCH_NAME is set of a non-blank value, switch to that branch - if [ -n "$BRANCH_NAME" ]; then - echo "Switch to branch $BRANCH_NAME..." - cd "$CLONE_PATH" || exit 1 - git switch "$BRANCH_NAME" - cd - || exit 1 + if [ -z "$BRANCH_NAME" ]; then + echo "Cloning $REPO_URL to $CLONE_PATH..." + git clone "$REPO_URL" "$CLONE_PATH" + else + echo "Cloning $REPO_URL to $CLONE_PATH on branch $BRANCH_NAME..." + git clone "$REPO_URL" -b "$BRANCH_NAME" "$CLONE_PATH" fi else echo "$CLONE_PATH already exists and isn't empty, skipping clone!" From 60d0c5ecbbc0a7a70ed0ae93005c0cdb3b510b26 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 2 Apr 2024 10:29:32 -0700 Subject: [PATCH 05/19] feat(git-clone): add support for web_url --- git-clone/main.test.ts | 46 ++++++++++++++++++++++++++++++++---------- git-clone/main.tf | 20 +++++++++++++++--- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 8370e503..7a5c22e4 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -38,44 +38,62 @@ describe("git-clone", async () => { }); it("repo_dir should match repo name for https", async () => { + const url = "https://github.com/coder/coder.git"; const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", - url: "https://github.com/coder/coder.git", + url, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); - expect(state.outputs.clone_url.value).toEqual( - "https://github.com/coder/coder.git", - ); + expect(state.outputs.clone_url.value).toEqual(url); + expect(state.outputs.web_url.value).toEqual(url); expect(state.outputs.branch_name.value).toEqual(""); }); it("repo_dir should match repo name for https without .git", async () => { + const url = "https://github.com/coder/coder"; const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", - url: "https://github.com/coder/coder", + url, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); - expect(state.outputs.clone_url.value).toEqual( - "https://github.com/coder/coder", - ); + expect(state.outputs.clone_url.value).toEqual(url); + expect(state.outputs.web_url.value).toEqual(url); expect(state.outputs.branch_name.value).toEqual(""); }); it("repo_dir should match repo name for ssh", async () => { + const url = "git@github.com:coder/coder.git"; const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", - url: "git@github.com:coder/coder.git", + url, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); - expect(state.outputs.clone_url.value).toEqual( - "git@github.com:coder/coder.git", + expect(state.outputs.clone_url.value).toEqual(url); + expect(state.outputs.web_url.value).toEqual( + "https://github.com/coder/coder.git", ); expect(state.outputs.branch_name.value).toEqual(""); }); + it("branch_name should not include query string", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch?ref_type=heads", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.clone_url.value).toEqual( + "https://gitlab.com/mike.brew/repo-tests.log", + ); + expect(state.outputs.web_url.value).toEqual( + "https://gitlab.com/mike.brew/repo-tests.log", + ); + expect(state.outputs.branch_name.value).toEqual("feat/branch"); + }); + it("repo_dir should match repo name with gitlab tree url", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", @@ -86,6 +104,9 @@ describe("git-clone", async () => { expect(state.outputs.clone_url.value).toEqual( "https://gitlab.com/mike.brew/repo-tests.log", ); + expect(state.outputs.web_url.value).toEqual( + "https://gitlab.com/mike.brew/repo-tests.log", + ); expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); @@ -99,6 +120,9 @@ describe("git-clone", async () => { expect(state.outputs.clone_url.value).toEqual( "https://github.com/michaelbrewer/repo-tests.log", ); + expect(state.outputs.web_url.value).toEqual( + "https://github.com/michaelbrewer/repo-tests.log", + ); expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); diff --git a/git-clone/main.tf b/git-clone/main.tf index 22a44c91..83510a9a 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -26,9 +26,18 @@ variable "agent_id" { } locals { - clone_url = replace(replace(var.url, "//-/tree/.*/", ""), "//tree/.*/", "") - branch_name = replace(replace(replace(var.url, local.clone_url, ""), "/.*/-/tree//", ""), "/.*/tree//", "") - clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(local.clone_url), ".git", "")]) : join("/", ["~", replace(basename(local.clone_url), ".git", "")]) + # Remove query parameters and branch name from the URL + url = replace(replace(var.url, "/\\?.*/", ""), "/#.*", "") + # Remove tree and branch name from the URL + clone_url = replace(replace(local.url, "//-/tree/.*/", ""), "//tree/.*/", "") + # Extract the branch name from the URL + branch_name = replace(replace(replace(local.url, local.clone_url, ""), "/.*/-/tree//", ""), "/.*/tree//", "") + # Construct the path to clone the repository + clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(local.clone_url), ".git", "")]) : join("/", ["~", replace(basename(local.clone_url), ".git", "")]) + + # git@gitlab.com:mike.brew/repo-tests.log.git + # becomes https://gitlab.com/mike.brew/repo-tests.log.git + web_url = startswith(local.clone_url, "git@") ? replace(replace(local.clone_url, ":", "/"), "git@", "https://") : local.clone_url } output "repo_dir" { @@ -41,6 +50,11 @@ output "clone_url" { description = "Git repository URL" } +output "web_url" { + value = local.web_url + description = "Git https repository URL" +} + output "branch_name" { value = local.branch_name description = "Git branch" From 6510434c4e58b22c36d633031b790bb78e17a223 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 5 Apr 2024 09:16:40 -0700 Subject: [PATCH 06/19] Update git-clone/main.tf Co-authored-by: Mathias Fredriksson --- git-clone/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/main.tf b/git-clone/main.tf index 83510a9a..971120de 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -47,7 +47,7 @@ output "repo_dir" { output "clone_url" { value = local.clone_url - description = "Git repository URL" + description = "The exact Git repository URL that will be cloned" } output "web_url" { From 741d656a89bdb2fd719a63f1f96705fda87c8b6f Mon Sep 17 00:00:00 2001 From: "Brewer, Michael (US - California)" Date: Fri, 5 Apr 2024 16:11:31 -0700 Subject: [PATCH 07/19] test(git-clone): add missing fragment test --- git-clone/main.test.ts | 40 ++++++++++++++++++++++------------------ git-clone/main.tf | 14 +++++++------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 7a5c22e4..8f31c82f 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -85,12 +85,22 @@ describe("git-clone", async () => { url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch?ref_type=heads", }); expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); - expect(state.outputs.clone_url.value).toEqual( - "https://gitlab.com/mike.brew/repo-tests.log", - ); - expect(state.outputs.web_url.value).toEqual( - "https://gitlab.com/mike.brew/repo-tests.log", - ); + const https_url = "https://gitlab.com/mike.brew/repo-tests.log"; + expect(state.outputs.clone_url.value).toEqual(https_url); + expect(state.outputs.web_url.value).toEqual(https_url); + expect(state.outputs.branch_name.value).toEqual("feat/branch"); + }); + + it("branch_name should not include fragments", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch#name", + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + const https_url = "https://gitlab.com/mike.brew/repo-tests.log"; + expect(state.outputs.clone_url.value).toEqual(https_url); + expect(state.outputs.web_url.value).toEqual(https_url); expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); @@ -101,12 +111,9 @@ describe("git-clone", async () => { url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch", }); expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); - expect(state.outputs.clone_url.value).toEqual( - "https://gitlab.com/mike.brew/repo-tests.log", - ); - expect(state.outputs.web_url.value).toEqual( - "https://gitlab.com/mike.brew/repo-tests.log", - ); + const https_url = "https://gitlab.com/mike.brew/repo-tests.log"; + expect(state.outputs.clone_url.value).toEqual(https_url); + expect(state.outputs.web_url.value).toEqual(https_url); expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); @@ -117,12 +124,9 @@ describe("git-clone", async () => { url: "https://github.com/michaelbrewer/repo-tests.log/tree/feat/branch", }); expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); - expect(state.outputs.clone_url.value).toEqual( - "https://github.com/michaelbrewer/repo-tests.log", - ); - expect(state.outputs.web_url.value).toEqual( - "https://github.com/michaelbrewer/repo-tests.log", - ); + const https_url = "https://github.com/michaelbrewer/repo-tests.log"; + expect(state.outputs.clone_url.value).toEqual(https_url); + expect(state.outputs.web_url.value).toEqual(https_url); expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); diff --git a/git-clone/main.tf b/git-clone/main.tf index 971120de..a1a4ad1f 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -26,17 +26,17 @@ variable "agent_id" { } locals { - # Remove query parameters and branch name from the URL + # Remove query parameters and fragments from the URL url = replace(replace(var.url, "/\\?.*/", ""), "/#.*", "") # Remove tree and branch name from the URL clone_url = replace(replace(local.url, "//-/tree/.*/", ""), "//tree/.*/", "") # Extract the branch name from the URL branch_name = replace(replace(replace(local.url, local.clone_url, ""), "/.*/-/tree//", ""), "/.*/tree//", "") + # Extract the folder name from the URL + folder_name = replace(basename(local.clone_url), ".git", "") # Construct the path to clone the repository - clone_path = var.base_dir != "" ? join("/", [var.base_dir, replace(basename(local.clone_url), ".git", "")]) : join("/", ["~", replace(basename(local.clone_url), ".git", "")]) - - # git@gitlab.com:mike.brew/repo-tests.log.git - # becomes https://gitlab.com/mike.brew/repo-tests.log.git + clone_path = var.base_dir != "" ? join("/", [var.base_dir, local.folder_name]) : join("/", ["~", local.folder_name]) + # Construct the web URL web_url = startswith(local.clone_url, "git@") ? replace(replace(local.clone_url, ":", "/"), "git@", "https://") : local.clone_url } @@ -52,12 +52,12 @@ output "clone_url" { output "web_url" { value = local.web_url - description = "Git https repository URL" + description = "Git https repository URL (may be invalid for unsupported providers)" } output "branch_name" { value = local.branch_name - description = "Git branch" + description = "Git branch name (may be empty)" } resource "coder_script" "git_clone" { From d76e4d954355d16b14186ed957bd78b7e2a3a26a Mon Sep 17 00:00:00 2001 From: "Brewer, Michael (US - California)" Date: Fri, 5 Apr 2024 16:15:30 -0700 Subject: [PATCH 08/19] test(git-clone): add missing fragment test --- git-clone/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/main.tf b/git-clone/main.tf index a1a4ad1f..f745b95a 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -27,7 +27,7 @@ variable "agent_id" { locals { # Remove query parameters and fragments from the URL - url = replace(replace(var.url, "/\\?.*/", ""), "/#.*", "") + url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") # Remove tree and branch name from the URL clone_url = replace(replace(local.url, "//-/tree/.*/", ""), "//tree/.*/", "") # Extract the branch name from the URL From 52c80095117ae7aa39b4dec481bb0247bbc233ba Mon Sep 17 00:00:00 2001 From: "Brewer, Michael (US - California)" Date: Fri, 5 Apr 2024 18:15:37 -0700 Subject: [PATCH 09/19] feat(git-clone): add support git_providers setting --- git-clone/main.test.ts | 17 +++++++++++++++-- git-clone/main.tf | 25 +++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 8f31c82f..1ee1c13b 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -104,7 +104,7 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); - it("repo_dir should match repo name with gitlab tree url", async () => { + it("gitlab tree url branch_name should match", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", @@ -117,7 +117,7 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); - it("repo_dir should match repo name with github tree url", async () => { + it("github tree url branch_name should match", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", @@ -130,6 +130,19 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); + it("handle unsupported git provider", async () => { + const url = "https://git.unknown.com/coder/coder"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url, + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.clone_url.value).toEqual(url); + expect(state.outputs.web_url.value).toEqual(url); + expect(state.outputs.branch_name.value).toEqual(""); + }); + it("runs with github clone with switch to feat/branch", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", diff --git a/git-clone/main.tf b/git-clone/main.tf index f745b95a..b0371c12 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -25,13 +25,34 @@ variable "agent_id" { type = string } +variable "git_providers" { + type = map(object({ + tree_path = string + })) + description = "The set of tree paths for each git provider." + default = { + "https://github.com/" = { + tree_path = "/tree/" + }, + "https://gitlab.com/" = { + tree_path = "/-/tree/" + }, + } +} + locals { # Remove query parameters and fragments from the URL url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") + + # Determine the git provider based on the URL + git_provider = try(coalesce([for provider in keys(var.git_providers) : provider if startswith(local.url, provider)]...), null) + # Get the tree path based on the git provider + tree_path = try(lookup(var.git_providers, local.git_provider).tree_path, "") + # Remove tree and branch name from the URL - clone_url = replace(replace(local.url, "//-/tree/.*/", ""), "//tree/.*/", "") + clone_url = local.tree_path != "" ? replace(local.url, "/${local.tree_path}.*/", "") : local.url # Extract the branch name from the URL - branch_name = replace(replace(replace(local.url, local.clone_url, ""), "/.*/-/tree//", ""), "/.*/tree//", "") + branch_name = local.tree_path != "" ? replace(replace(local.url, local.clone_url, ""), "/.*${local.tree_path}/", "") : "" # Extract the folder name from the URL folder_name = replace(basename(local.clone_url), ".git", "") # Construct the path to clone the repository From cb67c37e9830b0dce6d934f38178170acd59b140 Mon Sep 17 00:00:00 2001 From: "Brewer, Michael (US - California)" Date: Fri, 5 Apr 2024 18:34:29 -0700 Subject: [PATCH 10/19] doc(git-clone): add basic docs --- git-clone/README.md | 58 ++++++++++++++++++++++++++++++++++++++++++ git-clone/main.test.ts | 23 +++++++++++++++-- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/git-clone/README.md b/git-clone/README.md index 054e30c1..7121ff84 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -50,3 +50,61 @@ data "coder_git_auth" "github" { id = "github" } ``` + +## Github clone with branch name + +To github clone a url at a specific branch like `feat/example` + +```tf +module "git-clone" { + source = "registry.coder.com/modules/git-clone/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + url = "https://github.com/coder/coder/tree/feat/example" +} +``` + +Self host github + +```tf +module "git-clone" { + source = "registry.coder.com/modules/git-clone/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + url = "https://github.example.com/coder/coder/tree/feat/example" + git_providers = { + "https://github.example.com/" = { + tree_path = "/tree/" + } + } +} +``` + +## Gitlab clone with branch name + +To gitlab clone a url at a specific branch like `feat/example` + +```tf +module "git-clone" { + source = "registry.coder.com/modules/git-clone/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + url = "https://gitlab.com/coder/coder/-/tree/feat/example" +} +``` + +Self host gitlab + +```tf +module "git-clone" { + source = "registry.coder.com/modules/git-clone/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + url = "https://gitlab.example.com/coder/coder/-/tree/feat/example" + git_providers = { + "https://gitlab.example.com/" = { + tree_path = "/-/tree/" + } + } +} +``` diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 1ee1c13b..678766a5 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -104,7 +104,7 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); - it("gitlab tree url branch_name should match", async () => { + it("gitlab url with branch should match", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", @@ -117,7 +117,7 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); - it("github tree url branch_name should match", async () => { + it("github url with branch should match", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", base_dir: "/tmp", @@ -130,6 +130,25 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/branch"); }); + it("self-host git url with branch should match", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + base_dir: "/tmp", + url: "https://git.example.com/example/project/-/tree/feat/example", + git_providers: ` + { + "https://git.example.com/" = { + tree_path = "/-/tree/" + } + }`, + }); + expect(state.outputs.repo_dir.value).toEqual("/tmp/project"); + const https_url = "https://git.example.com/example/project"; + expect(state.outputs.clone_url.value).toEqual(https_url); + expect(state.outputs.web_url.value).toEqual(https_url); + expect(state.outputs.branch_name.value).toEqual("feat/example"); + }); + it("handle unsupported git provider", async () => { const url = "https://git.unknown.com/coder/coder"; const state = await runTerraformApply(import.meta.dir, { From 53cf0b5fd8076d869d25c17b202b767d21e33c65 Mon Sep 17 00:00:00 2001 From: "Brewer, Michael (US - California)" Date: Fri, 5 Apr 2024 20:20:15 -0700 Subject: [PATCH 11/19] feat(git-clone): easier ux --- git-clone/README.md | 4 ++-- git-clone/main.test.ts | 18 +++++++++++++++++- git-clone/main.tf | 20 ++++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/git-clone/README.md b/git-clone/README.md index 7121ff84..7c9b2289 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -74,7 +74,7 @@ module "git-clone" { url = "https://github.example.com/coder/coder/tree/feat/example" git_providers = { "https://github.example.com/" = { - tree_path = "/tree/" + tree_path = "github" } } } @@ -103,7 +103,7 @@ module "git-clone" { url = "https://gitlab.example.com/coder/coder/-/tree/feat/example" git_providers = { "https://gitlab.example.com/" = { - tree_path = "/-/tree/" + tree_path = "gitlab" } } } diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index 678766a5..d66486ce 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -138,7 +138,7 @@ describe("git-clone", async () => { git_providers: ` { "https://git.example.com/" = { - tree_path = "/-/tree/" + provider = "gitlab" } }`, }); @@ -149,6 +149,22 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/example"); }); + it("handle invalid git provider configuration", async () => { + const t = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "foo", + git_providers: ` + { + "https://git.example.com/" = { + provider = "bitbucket" + } + }`, + }); + }; + expect(t).toThrow('Allowed values for provider are "github" or "gitlab".'); + }); + it("handle unsupported git provider", async () => { const url = "https://git.unknown.com/coder/coder"; const state = await runTerraformApply(import.meta.dir, { diff --git a/git-clone/main.tf b/git-clone/main.tf index b0371c12..8688702e 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -27,27 +27,31 @@ variable "agent_id" { variable "git_providers" { type = map(object({ - tree_path = string + provider = string })) - description = "The set of tree paths for each git provider." + description = "The set of git provider by the base url." default = { "https://github.com/" = { - tree_path = "/tree/" + provider = "github" }, "https://gitlab.com/" = { - tree_path = "/-/tree/" + provider = "gitlab" }, } + validation { + error_message = "Allowed values for provider are \"github\" or \"gitlab\"." + condition = alltrue([for provider in var.git_providers : contains(["github", "gitlab"], provider.provider)]) + } } locals { # Remove query parameters and fragments from the URL url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") - # Determine the git provider based on the URL - git_provider = try(coalesce([for provider in keys(var.git_providers) : provider if startswith(local.url, provider)]...), null) - # Get the tree path based on the git provider - tree_path = try(lookup(var.git_providers, local.git_provider).tree_path, "") + # Find the git provider based on the URL and determine the tree path + git_provider_key = try(coalesce([for provider in keys(var.git_providers) : provider if startswith(local.url, provider)]...), null) + git_provider = try(lookup(var.git_providers, local.git_provider_key).provider, "") + tree_path = local.git_provider == "gitlab" ? "/-/tree/" : local.git_provider == "github" ? "/tree/" : "" # Remove tree and branch name from the URL clone_url = local.tree_path != "" ? replace(local.url, "/${local.tree_path}.*/", "") : local.url From 402838ccac4ffc92d726050c53b5b13cbb09a45f Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sat, 6 Apr 2024 09:26:18 -0700 Subject: [PATCH 12/19] chore(git-clone): code cleanup and use one --- git-clone/main.test.ts | 9 ++++----- git-clone/main.tf | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index d66486ce..e7fffa45 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -72,9 +72,8 @@ describe("git-clone", async () => { }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); expect(state.outputs.clone_url.value).toEqual(url); - expect(state.outputs.web_url.value).toEqual( - "https://github.com/coder/coder.git", - ); + const https_url = "https://github.com/coder/coder.git"; + expect(state.outputs.web_url.value).toEqual(https_url); expect(state.outputs.branch_name.value).toEqual(""); }); @@ -149,7 +148,7 @@ describe("git-clone", async () => { expect(state.outputs.branch_name.value).toEqual("feat/example"); }); - it("handle invalid git provider configuration", async () => { + it("handle unsupported git provider configuration", async () => { const t = async () => { await runTerraformApply(import.meta.dir, { agent_id: "foo", @@ -165,7 +164,7 @@ describe("git-clone", async () => { expect(t).toThrow('Allowed values for provider are "github" or "gitlab".'); }); - it("handle unsupported git provider", async () => { + it("handle unknown git provider url", async () => { const url = "https://git.unknown.com/coder/coder"; const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", diff --git a/git-clone/main.tf b/git-clone/main.tf index 8688702e..b29979c9 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -49,9 +49,9 @@ locals { url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") # Find the git provider based on the URL and determine the tree path - git_provider_key = try(coalesce([for provider in keys(var.git_providers) : provider if startswith(local.url, provider)]...), null) - git_provider = try(lookup(var.git_providers, local.git_provider_key).provider, "") - tree_path = local.git_provider == "gitlab" ? "/-/tree/" : local.git_provider == "github" ? "/tree/" : "" + provider_key = try(one([for key in keys(var.git_providers) : key if startswith(local.url, key)]), null) + provider = try(lookup(var.git_providers, local.provider_key).provider, "") + tree_path = local.provider == "gitlab" ? "/-/tree/" : local.provider == "github" ? "/tree/" : "" # Remove tree and branch name from the URL clone_url = local.tree_path != "" ? replace(local.url, "/${local.tree_path}.*/", "") : local.url From 3ebf901cd8d30e848d956360402e134e85c027e7 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sat, 6 Apr 2024 13:16:11 -0700 Subject: [PATCH 13/19] doc(git-clone): update docs --- git-clone/README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/git-clone/README.md b/git-clone/README.md index 7c9b2289..7f26fea0 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -51,20 +51,27 @@ data "coder_git_auth" "github" { } ``` -## Github clone with branch name +## GitHub clone with branch name -To github clone a url at a specific branch like `feat/example` +To GitHub clone with a specific branch like `feat/example` ```tf +# Prompt the user for the git repo URL +data "coder_parameter" "git_repo" { + name = "git_repo" + display_name = "Git repository" + default = "https://github.com/coder/coder/tree/feat/example" +} + module "git-clone" { source = "registry.coder.com/modules/git-clone/coder" version = "1.0.11" agent_id = coder_agent.example.id - url = "https://github.com/coder/coder/tree/feat/example" + url = data.coder_parameter.git_repo.value } ``` -Self host github +Configuring `git-clone` for a self-hosted GitHub Enterprise Server running at `github.example.com` ```tf module "git-clone" { @@ -80,9 +87,9 @@ module "git-clone" { } ``` -## Gitlab clone with branch name +## GitLab clone with branch name -To gitlab clone a url at a specific branch like `feat/example` +To GitLab clone with a specific branch like `feat/example` ```tf module "git-clone" { @@ -93,7 +100,7 @@ module "git-clone" { } ``` -Self host gitlab +Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com` ```tf module "git-clone" { From 12efc56ccf1cdc4abbfb99dd05f26762ad9cb0e5 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 7 Apr 2024 02:33:27 -0700 Subject: [PATCH 14/19] feat(git-clone): add outputs for folder_name and git_provider --- git-clone/main.test.ts | 9 +++++++-- git-clone/main.tf | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index e7fffa45..c7d6e815 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -45,6 +45,7 @@ describe("git-clone", async () => { url, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.folder_name.value).toEqual("coder"); expect(state.outputs.clone_url.value).toEqual(url); expect(state.outputs.web_url.value).toEqual(url); expect(state.outputs.branch_name.value).toEqual(""); @@ -71,6 +72,7 @@ describe("git-clone", async () => { url, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/coder"); + expect(state.outputs.git_provider.value).toEqual(""); expect(state.outputs.clone_url.value).toEqual(url); const https_url = "https://github.com/coder/coder.git"; expect(state.outputs.web_url.value).toEqual(https_url); @@ -80,10 +82,10 @@ describe("git-clone", async () => { it("branch_name should not include query string", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", - base_dir: "/tmp", url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch?ref_type=heads", }); - expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.repo_dir.value).toEqual("~/repo-tests.log"); + expect(state.outputs.folder_name.value).toEqual("repo-tests.log"); const https_url = "https://gitlab.com/mike.brew/repo-tests.log"; expect(state.outputs.clone_url.value).toEqual(https_url); expect(state.outputs.web_url.value).toEqual(https_url); @@ -110,6 +112,7 @@ describe("git-clone", async () => { url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch", }); expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.git_provider.value).toEqual("gitlab"); const https_url = "https://gitlab.com/mike.brew/repo-tests.log"; expect(state.outputs.clone_url.value).toEqual(https_url); expect(state.outputs.web_url.value).toEqual(https_url); @@ -123,6 +126,7 @@ describe("git-clone", async () => { url: "https://github.com/michaelbrewer/repo-tests.log/tree/feat/branch", }); expect(state.outputs.repo_dir.value).toEqual("/tmp/repo-tests.log"); + expect(state.outputs.git_provider.value).toEqual("github"); const https_url = "https://github.com/michaelbrewer/repo-tests.log"; expect(state.outputs.clone_url.value).toEqual(https_url); expect(state.outputs.web_url.value).toEqual(https_url); @@ -142,6 +146,7 @@ describe("git-clone", async () => { }`, }); expect(state.outputs.repo_dir.value).toEqual("/tmp/project"); + expect(state.outputs.git_provider.value).toEqual("gitlab"); const https_url = "https://git.example.com/example/project"; expect(state.outputs.clone_url.value).toEqual(https_url); expect(state.outputs.web_url.value).toEqual(https_url); diff --git a/git-clone/main.tf b/git-clone/main.tf index b29979c9..fc728099 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -70,6 +70,16 @@ output "repo_dir" { description = "Full path of cloned repo directory" } +output "git_provider" { + value = local.provider + description = "The git provider of the repository" +} + +output "folder_name" { + value = local.folder_name + description = "The name of the folder that will be created" +} + output "clone_url" { value = local.clone_url description = "The exact Git repository URL that will be cloned" From b46c628590a8bab568cdd575710928424597dfc0 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 7 Apr 2024 03:03:19 -0700 Subject: [PATCH 15/19] doc(git-clone): more complete example --- git-clone/README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/git-clone/README.md b/git-clone/README.md index 7f26fea0..485f8479 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -63,12 +63,34 @@ data "coder_parameter" "git_repo" { default = "https://github.com/coder/coder/tree/feat/example" } -module "git-clone" { +# Clone the repository for branch `feat/example` +module "git_clone" { source = "registry.coder.com/modules/git-clone/coder" version = "1.0.11" agent_id = coder_agent.example.id url = data.coder_parameter.git_repo.value } + +# Create a code-server instance for the cloned repository +module "code-server" { + source = "registry.coder.com/modules/code-server/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + order = 1 + folder = "/home/${local.username}/${module.git_clone.folder_name}" +} + +# Create a Coder app for the website +resource "coder_app" "website" { + agent_id = coder_agent.example.id + order = 2 + slug = "website" + external = true + display_name = module.git_clone.folder_name + url = module.git_clone.web_url + icon = module.git_clone.git_provider != "" ? "/icon/${module.git_clone.git_provider}.svg" : "/icon/git.svg" + count = module.git_clone.web_url != "" ? 1 : 0 +} ``` Configuring `git-clone` for a self-hosted GitHub Enterprise Server running at `github.example.com` From d58c69ace9d0a8ed9d61f81fbe99af23d8bd05d9 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 16 Apr 2024 14:36:36 -0700 Subject: [PATCH 16/19] Update README.md Co-authored-by: Asher --- git-clone/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/README.md b/git-clone/README.md index 485f8479..07718e41 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -132,7 +132,7 @@ module "git-clone" { url = "https://gitlab.example.com/coder/coder/-/tree/feat/example" git_providers = { "https://gitlab.example.com/" = { - tree_path = "gitlab" + provider = "gitlab" } } } From 0ff5872d417ed43d9f5b26628d7abc332b2ea7fc Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 16 Apr 2024 14:36:44 -0700 Subject: [PATCH 17/19] Update README.md Co-authored-by: Asher --- git-clone/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/README.md b/git-clone/README.md index 07718e41..216b93b2 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -103,7 +103,7 @@ module "git-clone" { url = "https://github.example.com/coder/coder/tree/feat/example" git_providers = { "https://github.example.com/" = { - tree_path = "github" + provider = "github" } } } From 228666d84285efcd54d14c21488ce64eeb162842 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 16 Apr 2024 14:38:12 -0700 Subject: [PATCH 18/19] Update main.tf Co-authored-by: Asher --- git-clone/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-clone/main.tf b/git-clone/main.tf index fc728099..9563f51f 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -29,7 +29,7 @@ variable "git_providers" { type = map(object({ provider = string })) - description = "The set of git provider by the base url." + description = "A mapping of URLs to their git provider." default = { "https://github.com/" = { provider = "github" From b8073e3e8ec699c39480f4c2ffe29fa4d1051095 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Tue, 16 Apr 2024 15:03:53 -0700 Subject: [PATCH 19/19] feat: add branch_name --- git-clone/README.md | 16 ++++++++++++++++ git-clone/main.test.ts | 21 +++++++++++++++++++++ git-clone/main.tf | 10 ++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/git-clone/README.md b/git-clone/README.md index 216b93b2..a8dd312f 100644 --- a/git-clone/README.md +++ b/git-clone/README.md @@ -137,3 +137,19 @@ module "git-clone" { } } ``` + +## Git clone with branch_name set + +Alternatively, you can set the `branch_name` attribute to clone a specific branch. + +For example, to clone the `feat/example` branch: + +```tf +module "git-clone" { + source = "registry.coder.com/modules/git-clone/coder" + version = "1.0.11" + agent_id = coder_agent.example.id + url = "https://github.com/coder/coder" + branch_name = "feat/example" +} +``` diff --git a/git-clone/main.test.ts b/git-clone/main.test.ts index c7d6e815..87b0e4a6 100644 --- a/git-clone/main.test.ts +++ b/git-clone/main.test.ts @@ -207,4 +207,25 @@ describe("git-clone", async () => { "Cloning https://gitlab.com/mike.brew/repo-tests.log to ~/repo-tests.log on branch feat/branch...", ]); }); + + it("runs with github clone with branch_name set to feat/branch", async () => { + const url = "https://github.com/michaelbrewer/repo-tests.log"; + const branch_name = "feat/branch"; + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url, + branch_name, + }); + expect(state.outputs.repo_dir.value).toEqual("~/repo-tests.log"); + expect(state.outputs.clone_url.value).toEqual(url); + expect(state.outputs.web_url.value).toEqual(url); + expect(state.outputs.branch_name.value).toEqual(branch_name); + + const output = await executeScriptInContainer(state, "alpine/git"); + expect(output.exitCode).toBe(0); + expect(output.stdout).toEqual([ + "Creating directory ~/repo-tests.log...", + "Cloning https://github.com/michaelbrewer/repo-tests.log to ~/repo-tests.log on branch feat/branch...", + ]); + }); }); diff --git a/git-clone/main.tf b/git-clone/main.tf index 9563f51f..4af5000e 100644 --- a/git-clone/main.tf +++ b/git-clone/main.tf @@ -44,6 +44,12 @@ variable "git_providers" { } } +variable "branch_name" { + description = "The branch name to clone. If not provided, the default branch will be cloned." + type = string + default = "" +} + locals { # Remove query parameters and fragments from the URL url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") @@ -54,9 +60,9 @@ locals { tree_path = local.provider == "gitlab" ? "/-/tree/" : local.provider == "github" ? "/tree/" : "" # Remove tree and branch name from the URL - clone_url = local.tree_path != "" ? replace(local.url, "/${local.tree_path}.*/", "") : local.url + clone_url = var.branch_name == "" && local.tree_path != "" ? replace(local.url, "/${local.tree_path}.*/", "") : local.url # Extract the branch name from the URL - branch_name = local.tree_path != "" ? replace(replace(local.url, local.clone_url, ""), "/.*${local.tree_path}/", "") : "" + branch_name = var.branch_name == "" && local.tree_path != "" ? replace(replace(local.url, local.clone_url, ""), "/.*${local.tree_path}/", "") : var.branch_name # Extract the folder name from the URL folder_name = replace(basename(local.clone_url), ".git", "") # Construct the path to clone the repository