Compare commits
6 Commits
main
...
35C4n0r/gi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
071777990a | ||
|
|
cf98844e27 | ||
|
|
9d2e19de1a | ||
|
|
d0629d2e28 | ||
|
|
1fc855711b | ||
|
|
6117692279 |
@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="7" height="7" x="14" y="3" rx="1"/>
|
||||
<path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 339 B |
@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect width="18" height="7" x="3" y="3" rx="1"/>
|
||||
<rect width="9" height="7" x="3" y="14" rx="1"/>
|
||||
<rect width="5" height="7" x="16" y="14" rx="1"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 336 B |
@ -14,7 +14,7 @@ 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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
}
|
||||
@ -28,7 +28,7 @@ module "git-clone" {
|
||||
module "git-clone" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/git-clone/coder"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
base_dir = "~/projects/coder"
|
||||
@ -43,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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
}
|
||||
@ -70,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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = data.coder_parameter.git_repo.value
|
||||
}
|
||||
@ -105,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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.example.com/coder/coder/tree/feat/example"
|
||||
git_providers = {
|
||||
@ -125,7 +125,7 @@ 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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://gitlab.com/coder/coder/-/tree/feat/example"
|
||||
}
|
||||
@ -137,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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://gitlab.example.com/coder/coder/-/tree/feat/example"
|
||||
git_providers = {
|
||||
@ -159,7 +159,7 @@ 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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
branch_name = "feat/example"
|
||||
@ -177,7 +177,7 @@ 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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
folder_name = "coder-dev"
|
||||
@ -185,21 +185,31 @@ module "git-clone" {
|
||||
}
|
||||
```
|
||||
|
||||
## Git shallow clone
|
||||
## Extra `git clone` arguments
|
||||
|
||||
Limit the clone history to speed-up workspace startup by setting `depth`.
|
||||
Pass any additional flags through `extra_args` (one element per argument).
|
||||
This lets you enable anything `git clone` supports without the module having
|
||||
to expose it explicitly, for example a shallow clone, submodules, parallel
|
||||
fetches, or partial clones.
|
||||
|
||||
When `depth` is greater than `0` the module runs `git clone --depth <depth>`.
|
||||
If not defined, the default, `0`, performs a full clone.
|
||||
> Do not put secrets in `extra_args`. The resolved `git clone` command
|
||||
> (including every element of `extra_args`) is echoed to the workspace
|
||||
> startup log, so values like `--config=http.extraHeader=Authorization: Bearer <token>`
|
||||
> would appear there in plaintext.
|
||||
|
||||
```tf
|
||||
module "git-clone" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/git-clone/coder"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
depth = 1
|
||||
extra_args = [
|
||||
"--depth=1",
|
||||
"--recurse-submodules",
|
||||
"--jobs=8",
|
||||
"--filter=blob:none",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@ -212,7 +222,7 @@ This is useful for preparing the environment or validating prerequisites before
|
||||
module "git-clone" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/git-clone/coder"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
pre_clone_script = <<-EOT
|
||||
@ -235,7 +245,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.1"
|
||||
version = "1.4.0"
|
||||
agent_id = coder_agent.example.id
|
||||
url = "https://github.com/coder/coder"
|
||||
post_clone_script = <<-EOT
|
||||
|
||||
@ -1,11 +1,48 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import {
|
||||
executeScriptInContainer,
|
||||
execContainer,
|
||||
findResourceInstance,
|
||||
runContainer,
|
||||
runTerraformApply,
|
||||
runTerraformInit,
|
||||
testRequiredVariables,
|
||||
type scriptOutput,
|
||||
type TerraformState,
|
||||
} from "~test";
|
||||
|
||||
const executeScriptInContainer = async (
|
||||
state: TerraformState,
|
||||
image: string,
|
||||
before?: string,
|
||||
): Promise<scriptOutput> => {
|
||||
const instance = findResourceInstance(state, "coder_script");
|
||||
const id = await runContainer(image);
|
||||
await execContainer(id, ["sh", "-c", "apk add --no-cache bash >/dev/null"]);
|
||||
if (before) {
|
||||
await execContainer(id, ["sh", "-c", before]);
|
||||
}
|
||||
const resp = await execContainer(id, ["bash", "-c", instance.script]);
|
||||
return {
|
||||
exitCode: resp.exitCode,
|
||||
stdout: resp.stdout.trim().split("\n"),
|
||||
stderr: resp.stderr.trim().split("\n"),
|
||||
};
|
||||
};
|
||||
|
||||
// Drops a fake `git` onto PATH that prints each argv entry on its own line.
|
||||
// Lets tests prove that arguments (including ones with embedded spaces) reach
|
||||
// `git clone` as single argv tokens, which the echo line cannot show because
|
||||
// it joins with spaces.
|
||||
const installFakeGit = [
|
||||
"cat > /usr/local/bin/git <<'SHIM'",
|
||||
"#!/bin/sh",
|
||||
'for arg in "$@"; do',
|
||||
' printf "argv:%s\\n" "$arg"',
|
||||
"done",
|
||||
"SHIM",
|
||||
"chmod +x /usr/local/bin/git",
|
||||
].join("\n");
|
||||
|
||||
describe("git-clone", async () => {
|
||||
await runTerraformInit(import.meta.dir);
|
||||
|
||||
@ -31,8 +68,9 @@ describe("git-clone", async () => {
|
||||
});
|
||||
const output = await executeScriptInContainer(state, "alpine/git");
|
||||
expect(output.stdout).toEqual([
|
||||
"Creating directory ~/fake-url...",
|
||||
"Cloning fake-url to ~/fake-url...",
|
||||
"Creating directory /root/fake-url...",
|
||||
"Cloning fake-url to /root/fake-url...",
|
||||
"Running: git clone fake-url /root/fake-url",
|
||||
]);
|
||||
expect(output.stderr.join(" ")).toContain("fatal");
|
||||
expect(output.stderr.join(" ")).toContain("fake-url");
|
||||
@ -207,8 +245,9 @@ describe("git-clone", async () => {
|
||||
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...",
|
||||
"Creating directory /root/repo-tests.log...",
|
||||
"Cloning https://github.com/michaelbrewer/repo-tests.log to /root/repo-tests.log on branch feat/branch...",
|
||||
"Running: git clone -b feat/branch https://github.com/michaelbrewer/repo-tests.log /root/repo-tests.log",
|
||||
]);
|
||||
});
|
||||
|
||||
@ -220,8 +259,9 @@ describe("git-clone", async () => {
|
||||
const output = await executeScriptInContainer(state, "alpine/git");
|
||||
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...",
|
||||
"Creating directory /root/repo-tests.log...",
|
||||
"Cloning https://gitlab.com/mike.brew/repo-tests.log to /root/repo-tests.log on branch feat/branch...",
|
||||
"Running: git clone -b feat/branch https://gitlab.com/mike.brew/repo-tests.log /root/repo-tests.log",
|
||||
]);
|
||||
});
|
||||
|
||||
@ -241,8 +281,9 @@ describe("git-clone", async () => {
|
||||
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...",
|
||||
"Creating directory /root/repo-tests.log...",
|
||||
"Cloning https://github.com/michaelbrewer/repo-tests.log to /root/repo-tests.log on branch feat/branch...",
|
||||
"Running: git clone -b feat/branch https://github.com/michaelbrewer/repo-tests.log /root/repo-tests.log",
|
||||
]);
|
||||
});
|
||||
|
||||
@ -256,7 +297,6 @@ describe("git-clone", async () => {
|
||||
const output = await executeScriptInContainer(
|
||||
state,
|
||||
"alpine/git",
|
||||
"sh",
|
||||
"mkdir -p /tmp/fake-url && echo 'existing' > /tmp/fake-url/file.txt",
|
||||
);
|
||||
expect(output.stdout).toContain("Running post-clone script...");
|
||||
@ -272,7 +312,7 @@ describe("git-clone", async () => {
|
||||
const output = await executeScriptInContainer(state, "alpine/git");
|
||||
expect(output.stdout).toContain("Running pre-clone script...");
|
||||
expect(output.stdout).toContain("Pre-clone script executed");
|
||||
expect(output.stdout).toContain("Cloning fake-url to ~/fake-url...");
|
||||
expect(output.stdout).toContain("Cloning fake-url to /root/fake-url...");
|
||||
});
|
||||
|
||||
it("fails when pre-clone script fails", async () => {
|
||||
@ -285,7 +325,79 @@ describe("git-clone", async () => {
|
||||
expect(output.exitCode).toBe(42);
|
||||
expect(output.stdout).toContain("Running pre-clone script...");
|
||||
expect(output.stdout).toContain("Pre-clone script failed");
|
||||
expect(output.stdout).not.toContain("Cloning fake-url to ~/fake-url...");
|
||||
expect(output.stdout).not.toContain(
|
||||
"Cloning fake-url to /root/fake-url...",
|
||||
);
|
||||
});
|
||||
|
||||
it("defaults extra_args to empty", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
url: "fake-url",
|
||||
});
|
||||
const script = findResourceInstance(state, "coder_script").script;
|
||||
expect(script).toContain('EXTRA_ARGS=""');
|
||||
});
|
||||
|
||||
it("passes extra_args to git clone", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
url: "fake-url",
|
||||
extra_args: JSON.stringify([
|
||||
"--recurse-submodules",
|
||||
"--jobs=8",
|
||||
"--config=user.name=Coder User",
|
||||
"-c",
|
||||
"core.sshCommand=ssh -i /tmp/key",
|
||||
]),
|
||||
});
|
||||
const output = await executeScriptInContainer(
|
||||
state,
|
||||
"alpine/git",
|
||||
installFakeGit,
|
||||
);
|
||||
expect(output.exitCode).toBe(0);
|
||||
expect(output.stdout.join("\n")).toContain(
|
||||
[
|
||||
"argv:clone",
|
||||
"argv:--recurse-submodules",
|
||||
"argv:--jobs=8",
|
||||
"argv:--config=user.name=Coder User",
|
||||
"argv:-c",
|
||||
"argv:core.sshCommand=ssh -i /tmp/key",
|
||||
"argv:fake-url",
|
||||
"argv:/root/fake-url",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes extra_args alongside branch_name in the correct order", async () => {
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
agent_id: "foo",
|
||||
url: "fake-url",
|
||||
branch_name: "feat/branch",
|
||||
extra_args: JSON.stringify([
|
||||
"--recurse-submodules",
|
||||
"--config=user.name=Coder User",
|
||||
]),
|
||||
});
|
||||
const output = await executeScriptInContainer(
|
||||
state,
|
||||
"alpine/git",
|
||||
installFakeGit,
|
||||
);
|
||||
expect(output.exitCode).toBe(0);
|
||||
expect(output.stdout.join("\n")).toContain(
|
||||
[
|
||||
"argv:clone",
|
||||
"argv:--recurse-submodules",
|
||||
"argv:--config=user.name=Coder User",
|
||||
"argv:-b",
|
||||
"argv:feat/branch",
|
||||
"argv:fake-url",
|
||||
"argv:/root/fake-url",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
|
||||
it("fails when post-clone script fails", async () => {
|
||||
@ -298,7 +410,6 @@ describe("git-clone", async () => {
|
||||
const output = await executeScriptInContainer(
|
||||
state,
|
||||
"alpine/git",
|
||||
"sh",
|
||||
"mkdir -p /tmp/fake-url && echo 'existing' > /tmp/fake-url/file.txt",
|
||||
);
|
||||
expect(output.exitCode).toBe(43);
|
||||
|
||||
@ -56,10 +56,10 @@ variable "folder_name" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "depth" {
|
||||
description = "If > 0, perform a shallow clone using this depth."
|
||||
type = number
|
||||
default = 0
|
||||
variable "extra_args" {
|
||||
description = "Extra arguments to pass to `git clone`, one element per argument (e.g. `[\"--recurse-submodules\", \"--jobs=8\", \"--filter=blob:none\"]`)."
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "post_clone_script" {
|
||||
@ -97,6 +97,7 @@ locals {
|
||||
encoded_post_clone_script = var.post_clone_script != null ? base64encode(var.post_clone_script) : ""
|
||||
# Encode the pre_clone_script for passing to the shell script
|
||||
encoded_pre_clone_script = var.pre_clone_script != null ? base64encode(var.pre_clone_script) : ""
|
||||
encoded_extra_args = base64encode(join("\n", var.extra_args))
|
||||
}
|
||||
|
||||
output "repo_dir" {
|
||||
@ -135,7 +136,7 @@ resource "coder_script" "git_clone" {
|
||||
CLONE_PATH = local.clone_path,
|
||||
REPO_URL : local.clone_url,
|
||||
BRANCH_NAME : local.branch_name,
|
||||
DEPTH = var.depth,
|
||||
EXTRA_ARGS = local.encoded_extra_args,
|
||||
POST_CLONE_SCRIPT : local.encoded_post_clone_script,
|
||||
PRE_CLONE_SCRIPT : local.encoded_pre_clone_script,
|
||||
})
|
||||
|
||||
@ -7,7 +7,7 @@ CLONE_PATH="${CLONE_PATH}"
|
||||
BRANCH_NAME="${BRANCH_NAME}"
|
||||
# Expand home if it's specified!
|
||||
CLONE_PATH="$${CLONE_PATH/#\~/$${HOME}}"
|
||||
DEPTH="${DEPTH}"
|
||||
EXTRA_ARGS="${EXTRA_ARGS}"
|
||||
POST_CLONE_SCRIPT="${POST_CLONE_SCRIPT}"
|
||||
PRE_CLONE_SCRIPT="${PRE_CLONE_SCRIPT}"
|
||||
|
||||
@ -46,23 +46,25 @@ if [ -n "$PRE_CLONE_SCRIPT" ]; then
|
||||
rm "$PRE_CLONE_TMP"
|
||||
fi
|
||||
|
||||
# Build optional git clone flags
|
||||
extra_args=()
|
||||
if [ -n "$EXTRA_ARGS" ]; then
|
||||
while IFS= read -r arg || [ -n "$arg" ]; do
|
||||
[ -n "$arg" ] && extra_args+=("$arg")
|
||||
done < <(echo "$EXTRA_ARGS" | base64 -d)
|
||||
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
|
||||
git clone --depth "$DEPTH" "$REPO_URL" "$CLONE_PATH"
|
||||
else
|
||||
git clone "$REPO_URL" "$CLONE_PATH"
|
||||
fi
|
||||
echo "Running: git clone $${extra_args[@]:+$${extra_args[@]} }$REPO_URL $CLONE_PATH"
|
||||
git clone $${extra_args[@]+"$${extra_args[@]}"} "$REPO_URL" "$CLONE_PATH"
|
||||
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"
|
||||
else
|
||||
git clone "$REPO_URL" -b "$BRANCH_NAME" "$CLONE_PATH"
|
||||
fi
|
||||
echo "Running: git clone $${extra_args[@]:+$${extra_args[@]} }-b $BRANCH_NAME $REPO_URL $CLONE_PATH"
|
||||
git clone $${extra_args[@]+"$${extra_args[@]}"} -b "$BRANCH_NAME" "$REPO_URL" "$CLONE_PATH"
|
||||
fi
|
||||
else
|
||||
echo "$CLONE_PATH already exists and isn't empty, skipping clone!"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user