From 7a0d5461efaf4a300769ba13604cd82361059e4f Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 16:46:26 -0700 Subject: [PATCH 1/7] feat(git-clone): add clone_args variable for custom git clone arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow users to pass additional arguments to the git clone command via a new `clone_args` variable. This enables use cases like using a local mirror with `--reference` to speed up cloning large repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/README.md | 29 ++++++++++++++++++++++ registry/coder/modules/git-clone/main.tf | 7 ++++++ registry/coder/modules/git-clone/run.sh | 13 +++++++--- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/registry/coder/modules/git-clone/README.md b/registry/coder/modules/git-clone/README.md index 94d2ddacd..fa0b39c49 100644 --- a/registry/coder/modules/git-clone/README.md +++ b/registry/coder/modules/git-clone/README.md @@ -199,6 +199,35 @@ module "git-clone" { } ``` +## Custom clone arguments + +Pass additional arguments to the git clone command using the `clone_args` variable. +This is useful for scenarios like using a local mirror with `--reference` to speed up cloning. + +```tf +module "git-clone" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/git-clone/coder" + version = "1.2.2" + agent_id = coder_agent.example.id + url = "https://github.com/coder/coder" + clone_args = "--reference /mnt/git-mirrors/coder.git" +} +``` + +You can also combine multiple arguments: + +```tf +module "git-clone" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/git-clone/coder" + version = "1.2.2" + agent_id = coder_agent.example.id + url = "https://github.com/coder/coder" + clone_args = "--reference /mnt/git-mirrors/coder.git --dissociate" +} +``` + ## Post-clone script Run a custom script after cloning the repository by setting the `post_clone_script` variable. diff --git a/registry/coder/modules/git-clone/main.tf b/registry/coder/modules/git-clone/main.tf index 2d547ad2b..56c2d6ef9 100644 --- a/registry/coder/modules/git-clone/main.tf +++ b/registry/coder/modules/git-clone/main.tf @@ -68,6 +68,12 @@ variable "post_clone_script" { default = null } +variable "clone_args" { + description = "Additional arguments to pass to the git clone command (e.g., '--reference /path/to/mirror' for using a local mirror)." + type = string + default = "" +} + locals { # Remove query parameters and fragments from the URL url = replace(replace(var.url, "/\\?.*/", ""), "/#.*/", "") @@ -129,6 +135,7 @@ resource "coder_script" "git_clone" { BRANCH_NAME : local.branch_name, DEPTH = var.depth, POST_CLONE_SCRIPT : local.encoded_post_clone_script, + CLONE_ARGS : var.clone_args, }) display_name = "Git Clone" icon = "/icon/git.svg" diff --git a/registry/coder/modules/git-clone/run.sh b/registry/coder/modules/git-clone/run.sh index 4b91ee687..64680ff36 100644 --- a/registry/coder/modules/git-clone/run.sh +++ b/registry/coder/modules/git-clone/run.sh @@ -7,6 +7,7 @@ BRANCH_NAME="${BRANCH_NAME}" CLONE_PATH="$${CLONE_PATH/#\~/$${HOME}}" DEPTH="${DEPTH}" POST_CLONE_SCRIPT="${POST_CLONE_SCRIPT}" +CLONE_ARGS="${CLONE_ARGS}" # Check if the variable is empty... if [ -z "$REPO_URL" ]; then @@ -39,16 +40,20 @@ if [ -z "$(ls -A "$CLONE_PATH")" ]; then if [ -z "$BRANCH_NAME" ]; then echo "Cloning $REPO_URL to $CLONE_PATH..." if [ "$DEPTH" -gt 0 ]; then - git clone --depth "$DEPTH" "$REPO_URL" "$CLONE_PATH" + # shellcheck disable=SC2086 + git clone --depth "$DEPTH" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" else - git clone "$REPO_URL" "$CLONE_PATH" + # shellcheck disable=SC2086 + git clone $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" fi else echo "Cloning $REPO_URL to $CLONE_PATH on branch $BRANCH_NAME..." if [ "$DEPTH" -gt 0 ]; then - git clone --depth "$DEPTH" -b "$BRANCH_NAME" "$REPO_URL" "$CLONE_PATH" + # shellcheck disable=SC2086 + git clone --depth "$DEPTH" -b "$BRANCH_NAME" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" else - git clone "$REPO_URL" -b "$BRANCH_NAME" "$CLONE_PATH" + # shellcheck disable=SC2086 + git clone -b "$BRANCH_NAME" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" fi fi else From 4eb3f598b7903f2a2f7c4b4950bc94abd4bfc022 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 16:55:23 -0700 Subject: [PATCH 2/7] chore(git-clone): bump version to 1.3.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/README.md | 36 ++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/registry/coder/modules/git-clone/README.md b/registry/coder/modules/git-clone/README.md index fa0b39c49..d58af14aa 100644 --- a/registry/coder/modules/git-clone/README.md +++ b/registry/coder/modules/git-clone/README.md @@ -14,10 +14,11 @@ This module allows you to automatically clone a repository by URL and skip if it module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" } + ``` ## Examples @@ -28,11 +29,12 @@ module "git-clone" { module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" base_dir = "~/projects/coder" } + ``` ### Git Authentication @@ -43,11 +45,12 @@ To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-prov module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" } + data "coder_external_auth" "github" { id = "github" } @@ -69,11 +72,12 @@ data "coder_parameter" "git_repo" { module "git_clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" 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" { count = data.coder_workspace.me.start_count @@ -103,13 +107,14 @@ Configuring `git-clone` for a self-hosted GitHub Enterprise Server running at `g module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.example.com/coder/coder/tree/feat/example" git_providers = { "https://github.example.com/" = { provider = "github" } + } } ``` @@ -122,10 +127,11 @@ To GitLab clone with a specific branch like `feat/example` module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://gitlab.com/coder/coder/-/tree/feat/example" } + ``` Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com` @@ -134,13 +140,14 @@ Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com` module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://gitlab.example.com/coder/coder/-/tree/feat/example" git_providers = { "https://gitlab.example.com/" = { provider = "gitlab" } + } } ``` @@ -155,11 +162,12 @@ For example, to clone the `feat/example` branch: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" branch_name = "feat/example" } + ``` ## Git clone with different destination folder @@ -173,12 +181,13 @@ For example, this will clone into the `~/projects/coder/coder-dev` folder: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" folder_name = "coder-dev" base_dir = "~/projects/coder" } + ``` ## Git shallow clone @@ -208,11 +217,12 @@ This is useful for scenarios like using a local mirror with `--reference` to spe module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" clone_args = "--reference /mnt/git-mirrors/coder.git" } + ``` You can also combine multiple arguments: @@ -221,11 +231,12 @@ You can also combine multiple arguments: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" clone_args = "--reference /mnt/git-mirrors/coder.git --dissociate" } + ``` ## Post-clone script @@ -237,7 +248,7 @@ This is useful for running initialization tasks like installing dependencies or module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.2.2" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" post_clone_script = <<-EOT @@ -249,4 +260,5 @@ module "git-clone" { make setup EOT } + ``` From cdbb6636e7d7a727e4ccfe780398436e1940255d Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 16:57:10 -0700 Subject: [PATCH 3/7] style(git-clone): fix formatting in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/README.md | 32 ++++++++-------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/registry/coder/modules/git-clone/README.md b/registry/coder/modules/git-clone/README.md index d58af14aa..7b45e9de2 100644 --- a/registry/coder/modules/git-clone/README.md +++ b/registry/coder/modules/git-clone/README.md @@ -14,11 +14,10 @@ This module allows you to automatically clone a repository by URL and skip if it module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" } - ``` ## Examples @@ -29,12 +28,11 @@ module "git-clone" { module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" base_dir = "~/projects/coder" } - ``` ### Git Authentication @@ -45,7 +43,7 @@ To use with [Git Authentication](https://coder.com/docs/v2/latest/admin/git-prov module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" } @@ -72,7 +70,7 @@ data "coder_parameter" "git_repo" { module "git_clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = data.coder_parameter.git_repo.value } @@ -107,7 +105,7 @@ Configuring `git-clone` for a self-hosted GitHub Enterprise Server running at `g module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.example.com/coder/coder/tree/feat/example" git_providers = { @@ -127,11 +125,10 @@ To GitLab clone with a specific branch like `feat/example` module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://gitlab.com/coder/coder/-/tree/feat/example" } - ``` Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com` @@ -140,7 +137,7 @@ Configuring `git-clone` for a self-hosted GitLab running at `gitlab.example.com` module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://gitlab.example.com/coder/coder/-/tree/feat/example" git_providers = { @@ -162,12 +159,11 @@ For example, to clone the `feat/example` branch: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" branch_name = "feat/example" } - ``` ## Git clone with different destination folder @@ -181,13 +177,12 @@ For example, this will clone into the `~/projects/coder/coder-dev` folder: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" folder_name = "coder-dev" base_dir = "~/projects/coder" } - ``` ## Git shallow clone @@ -217,12 +212,11 @@ This is useful for scenarios like using a local mirror with `--reference` to spe module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" clone_args = "--reference /mnt/git-mirrors/coder.git" } - ``` You can also combine multiple arguments: @@ -231,12 +225,11 @@ You can also combine multiple arguments: module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" clone_args = "--reference /mnt/git-mirrors/coder.git --dissociate" } - ``` ## Post-clone script @@ -248,7 +241,7 @@ This is useful for running initialization tasks like installing dependencies or module "git-clone" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/git-clone/coder" - version = "1.3.0" + version = "1.3.0" agent_id = coder_agent.example.id url = "https://github.com/coder/coder" post_clone_script = <<-EOT @@ -260,5 +253,4 @@ module "git-clone" { make setup EOT } - ``` From 3210f764aabc54145072189acc83bdefeb5daff6 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 16:59:17 -0700 Subject: [PATCH 4/7] test(git-clone): add test for clone_args variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/main.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/registry/coder/modules/git-clone/main.test.ts b/registry/coder/modules/git-clone/main.test.ts index 0ae0a8dbf..b62279b1d 100644 --- a/registry/coder/modules/git-clone/main.test.ts +++ b/registry/coder/modules/git-clone/main.test.ts @@ -261,4 +261,18 @@ describe("git-clone", async () => { expect(output.stdout).toContain("Running post-clone script..."); expect(output.stdout).toContain("Post-clone script executed"); }); + + it("runs with clone_args", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "https://github.com/michaelbrewer/repo-tests.log", + clone_args: "--depth 1 --single-branch", + }); + 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...", + ]); + }); }); From b954ff1fdfd691ef687daab575ee25e7714b7c32 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 17:02:20 -0700 Subject: [PATCH 5/7] refactor(git-clone): simplify run.sh using POSIX argument building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace nested conditionals with a single git clone call that builds arguments dynamically using POSIX-compatible `set --` and `$@`. This reduces code duplication and makes the script easier to maintain. Also switched shebang from bash to sh for better portability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/run.sh | 38 +++++++++++++------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/registry/coder/modules/git-clone/run.sh b/registry/coder/modules/git-clone/run.sh index 64680ff36..98e7e4a73 100644 --- a/registry/coder/modules/git-clone/run.sh +++ b/registry/coder/modules/git-clone/run.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh REPO_URL="${REPO_URL}" CLONE_PATH="${CLONE_PATH}" @@ -37,25 +37,27 @@ fi # Check if the directory is empty # and if it is, clone the repo, otherwise skip cloning if [ -z "$(ls -A "$CLONE_PATH")" ]; then - if [ -z "$BRANCH_NAME" ]; then - echo "Cloning $REPO_URL to $CLONE_PATH..." - if [ "$DEPTH" -gt 0 ]; then - # shellcheck disable=SC2086 - git clone --depth "$DEPTH" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" - else - # shellcheck disable=SC2086 - git clone $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" - fi - else + if [ -n "$BRANCH_NAME" ]; then echo "Cloning $REPO_URL to $CLONE_PATH on branch $BRANCH_NAME..." - if [ "$DEPTH" -gt 0 ]; then - # shellcheck disable=SC2086 - git clone --depth "$DEPTH" -b "$BRANCH_NAME" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" - else - # shellcheck disable=SC2086 - git clone -b "$BRANCH_NAME" $CLONE_ARGS "$REPO_URL" "$CLONE_PATH" - fi + else + echo "Cloning $REPO_URL to $CLONE_PATH..." fi + + # Build the git clone command arguments + set -- + if [ -n "$DEPTH" ] && [ "$DEPTH" -gt 0 ]; then + set -- "$@" --depth "$DEPTH" + fi + if [ -n "$BRANCH_NAME" ]; then + set -- "$@" -b "$BRANCH_NAME" + fi + # shellcheck disable=SC2086 + if [ -n "$CLONE_ARGS" ]; then + set -- "$@" $CLONE_ARGS + fi + set -- "$@" "$REPO_URL" "$CLONE_PATH" + + git clone "$@" else echo "$CLONE_PATH already exists and isn't empty, skipping clone!" fi From ce7621eed608c9da3fc21db131e0a331bbe6215d Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 17:04:37 -0700 Subject: [PATCH 6/7] test(git-clone): add comprehensive tests for argument combinations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests covering all combinations of depth, branch_name, and clone_args to ensure the refactored run.sh properly handles all argument scenarios. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/main.test.ts | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/registry/coder/modules/git-clone/main.test.ts b/registry/coder/modules/git-clone/main.test.ts index b62279b1d..8e9f1be3f 100644 --- a/registry/coder/modules/git-clone/main.test.ts +++ b/registry/coder/modules/git-clone/main.test.ts @@ -266,7 +266,7 @@ describe("git-clone", async () => { const state = await runTerraformApply(import.meta.dir, { agent_id: "foo", url: "https://github.com/michaelbrewer/repo-tests.log", - clone_args: "--depth 1 --single-branch", + clone_args: "--single-branch", }); const output = await executeScriptInContainer(state, "alpine/git"); expect(output.exitCode).toBe(0); @@ -275,4 +275,64 @@ describe("git-clone", async () => { "Cloning https://github.com/michaelbrewer/repo-tests.log to ~/repo-tests.log...", ]); }); + + it("runs with depth", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "https://github.com/michaelbrewer/repo-tests.log", + depth: "1", + }); + 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...", + ]); + }); + + it("runs with depth and branch_name", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "https://github.com/michaelbrewer/repo-tests.log", + branch_name: "feat/branch", + depth: "1", + }); + 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...", + ]); + }); + + it("runs with clone_args and branch_name", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "https://github.com/michaelbrewer/repo-tests.log", + branch_name: "feat/branch", + clone_args: "--single-branch", + }); + 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...", + ]); + }); + + it("runs with depth, branch_name, and clone_args", async () => { + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + url: "https://github.com/michaelbrewer/repo-tests.log", + branch_name: "feat/branch", + depth: "1", + clone_args: "--single-branch", + }); + 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...", + ]); + }); }); From e89431fc8932e9cd843997e019ffc19b15f87065 Mon Sep 17 00:00:00 2001 From: Jason Barnett Date: Wed, 10 Dec 2025 17:12:42 -0700 Subject: [PATCH 7/7] refactor(git-clone): use bash arrays for cleaner argument building MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor run.sh to use bash arrays instead of nested conditionals - Replace 4 separate git clone command variations with a single call - Update tests to use bitnami/git image (has both bash and git) - Use debian:stable-slim for "no git" test (has bash without git) - Update test assertions to use toContain for path flexibility This simplifies the script while maintaining all functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- registry/coder/modules/git-clone/main.test.ts | 117 +++++++++++------- registry/coder/modules/git-clone/run.sh | 16 +-- 2 files changed, 77 insertions(+), 56 deletions(-) diff --git a/registry/coder/modules/git-clone/main.test.ts b/registry/coder/modules/git-clone/main.test.ts index 8e9f1be3f..9a59074da 100644 --- a/registry/coder/modules/git-clone/main.test.ts +++ b/registry/coder/modules/git-clone/main.test.ts @@ -6,6 +6,9 @@ import { testRequiredVariables, } from "~test"; +// Use bitnami/git which has both bash and git pre-installed +const gitImage = "bitnami/git"; + describe("git-clone", async () => { await runTerraformInit(import.meta.dir); @@ -19,7 +22,12 @@ describe("git-clone", async () => { agent_id: "foo", url: "some-url", }); - const output = await executeScriptInContainer(state, "alpine"); + // Use debian:stable-slim which has bash but no git + const output = await executeScriptInContainer( + state, + "debian:stable-slim", + "bash", + ); expect(output.exitCode).toBe(1); expect(output.stdout).toEqual(["Git is not installed!"]); }); @@ -29,11 +37,10 @@ describe("git-clone", async () => { agent_id: "foo", url: "fake-url", }); - const output = await executeScriptInContainer(state, "alpine/git"); - expect(output.stdout).toEqual([ - "Creating directory ~/fake-url...", - "Cloning fake-url to ~/fake-url...", - ]); + const output = await executeScriptInContainer(state, gitImage, "bash"); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("fake-url"); + expect(output.stdout[1]).toContain("Cloning fake-url to"); expect(output.stderr.join(" ")).toContain("fatal"); expect(output.stderr.join(" ")).toContain("fake-url"); }); @@ -204,12 +211,14 @@ describe("git-clone", async () => { agent_id: "foo", url: "https://github.com/michaelbrewer/repo-tests.log/tree/feat/branch", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); it("runs with gitlab clone with switch to feat/branch", async () => { @@ -217,12 +226,14 @@ describe("git-clone", async () => { agent_id: "foo", url: "https://gitlab.com/mike.brew/repo-tests.log/-/tree/feat/branch", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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 on branch feat/branch...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://gitlab.com/mike.brew/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); it("runs with github clone with branch_name set to feat/branch", async () => { @@ -238,12 +249,14 @@ describe("git-clone", async () => { expect(state.outputs.web_url.value).toEqual(url); expect(state.outputs.branch_name.value).toEqual(branch_name); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); it("runs post-clone script", async () => { @@ -254,8 +267,8 @@ describe("git-clone", async () => { }); const output = await executeScriptInContainer( state, - "alpine/git", - "sh", + gitImage, + "bash", "mkdir -p ~/fake-url && echo 'existing' > ~/fake-url/file.txt", ); expect(output.stdout).toContain("Running post-clone script..."); @@ -268,12 +281,13 @@ describe("git-clone", async () => { url: "https://github.com/michaelbrewer/repo-tests.log", clone_args: "--single-branch", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); }); it("runs with depth", async () => { @@ -282,12 +296,13 @@ describe("git-clone", async () => { url: "https://github.com/michaelbrewer/repo-tests.log", depth: "1", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); }); it("runs with depth and branch_name", async () => { @@ -297,12 +312,14 @@ describe("git-clone", async () => { branch_name: "feat/branch", depth: "1", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); it("runs with clone_args and branch_name", async () => { @@ -312,12 +329,14 @@ describe("git-clone", async () => { branch_name: "feat/branch", clone_args: "--single-branch", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); it("runs with depth, branch_name, and clone_args", async () => { @@ -328,11 +347,13 @@ describe("git-clone", async () => { depth: "1", clone_args: "--single-branch", }); - const output = await executeScriptInContainer(state, "alpine/git"); + const output = await executeScriptInContainer(state, gitImage, "bash"); 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...", - ]); + expect(output.stdout[0]).toContain("Creating directory"); + expect(output.stdout[0]).toContain("repo-tests.log"); + expect(output.stdout[1]).toContain( + "Cloning https://github.com/michaelbrewer/repo-tests.log", + ); + expect(output.stdout[1]).toContain("on branch feat/branch"); }); }); diff --git a/registry/coder/modules/git-clone/run.sh b/registry/coder/modules/git-clone/run.sh index 98e7e4a73..3601b4743 100644 --- a/registry/coder/modules/git-clone/run.sh +++ b/registry/coder/modules/git-clone/run.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash REPO_URL="${REPO_URL}" CLONE_PATH="${CLONE_PATH}" @@ -44,20 +44,20 @@ if [ -z "$(ls -A "$CLONE_PATH")" ]; then fi # Build the git clone command arguments - set -- + args=() if [ -n "$DEPTH" ] && [ "$DEPTH" -gt 0 ]; then - set -- "$@" --depth "$DEPTH" + args+=(--depth "$DEPTH") fi if [ -n "$BRANCH_NAME" ]; then - set -- "$@" -b "$BRANCH_NAME" + args+=(-b "$BRANCH_NAME") fi - # shellcheck disable=SC2086 + # shellcheck disable=SC2206 if [ -n "$CLONE_ARGS" ]; then - set -- "$@" $CLONE_ARGS + args+=($CLONE_ARGS) fi - set -- "$@" "$REPO_URL" "$CLONE_PATH" + args+=("$REPO_URL" "$CLONE_PATH") - git clone "$@" + git clone "$${args[@]}" else echo "$CLONE_PATH already exists and isn't empty, skipping clone!" fi