Merge branch 'main' into atif/vscode-core-extenions
This commit is contained in:
commit
814224ffa3
14
.github/workflows/ci.yaml
vendored
14
.github/workflows/ci.yaml
vendored
@ -27,6 +27,8 @@ jobs:
|
|||||||
- 'tsconfig.json'
|
- 'tsconfig.json'
|
||||||
- '.github/workflows/ci.yaml'
|
- '.github/workflows/ci.yaml'
|
||||||
- 'scripts/ts_test_auto.sh'
|
- 'scripts/ts_test_auto.sh'
|
||||||
|
- 'scripts/terraform_test_all.sh'
|
||||||
|
- 'scripts/terraform_validate.sh'
|
||||||
modules:
|
modules:
|
||||||
- 'registry/**/modules/**'
|
- 'registry/**/modules/**'
|
||||||
all:
|
all:
|
||||||
@ -49,10 +51,18 @@ jobs:
|
|||||||
ALL_CHANGED_FILES: ${{ steps.filter.outputs.all_files }}
|
ALL_CHANGED_FILES: ${{ steps.filter.outputs.all_files }}
|
||||||
SHARED_CHANGED: ${{ steps.filter.outputs.shared }}
|
SHARED_CHANGED: ${{ steps.filter.outputs.shared }}
|
||||||
MODULE_CHANGED_FILES: ${{ steps.filter.outputs.modules_files }}
|
MODULE_CHANGED_FILES: ${{ steps.filter.outputs.modules_files }}
|
||||||
run: ./scripts/ts_test_auto.sh
|
run: bun tstest
|
||||||
- name: Run Terraform tests
|
- name: Run Terraform tests
|
||||||
run: ./scripts/terraform_test_all.sh
|
env:
|
||||||
|
ALL_CHANGED_FILES: ${{ steps.filter.outputs.all_files }}
|
||||||
|
SHARED_CHANGED: ${{ steps.filter.outputs.shared }}
|
||||||
|
MODULE_CHANGED_FILES: ${{ steps.filter.outputs.modules_files }}
|
||||||
|
run: bun tftest
|
||||||
- name: Run Terraform Validate
|
- name: Run Terraform Validate
|
||||||
|
env:
|
||||||
|
ALL_CHANGED_FILES: ${{ steps.filter.outputs.all_files }}
|
||||||
|
SHARED_CHANGED: ${{ steps.filter.outputs.shared }}
|
||||||
|
MODULE_CHANGED_FILES: ${{ steps.filter.outputs.modules_files }}
|
||||||
run: bun terraform-validate
|
run: bun terraform-validate
|
||||||
validate-style:
|
validate-style:
|
||||||
name: Check for typos and unformatted code
|
name: Check for typos and unformatted code
|
||||||
|
|||||||
@ -4,7 +4,8 @@
|
|||||||
"fmt": "bun x prettier --write . && terraform fmt -recursive -diff",
|
"fmt": "bun x prettier --write . && terraform fmt -recursive -diff",
|
||||||
"fmt:ci": "bun x prettier --check . && terraform fmt -check -recursive -diff",
|
"fmt:ci": "bun x prettier --check . && terraform fmt -check -recursive -diff",
|
||||||
"terraform-validate": "./scripts/terraform_validate.sh",
|
"terraform-validate": "./scripts/terraform_validate.sh",
|
||||||
"test": "./scripts/terraform_test_all.sh",
|
"tftest": "./scripts/terraform_test_all.sh",
|
||||||
|
"tstest": "./scripts/ts_test_auto.sh",
|
||||||
"update-version": "./update-version.sh"
|
"update-version": "./update-version.sh"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -13,10 +13,10 @@ Run Codex CLI in your workspace to access OpenAI's models through the Codex inte
|
|||||||
```tf
|
```tf
|
||||||
module "codex" {
|
module "codex" {
|
||||||
source = "registry.coder.com/coder-labs/codex/coder"
|
source = "registry.coder.com/coder-labs/codex/coder"
|
||||||
version = "2.1.1"
|
version = "3.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
openai_api_key = var.openai_api_key
|
openai_api_key = var.openai_api_key
|
||||||
folder = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -33,10 +33,11 @@ module "codex" {
|
|||||||
module "codex" {
|
module "codex" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder-labs/codex/coder"
|
source = "registry.coder.com/coder-labs/codex/coder"
|
||||||
version = "2.1.1"
|
version = "3.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
openai_api_key = "..."
|
openai_api_key = "..."
|
||||||
folder = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
|
report_tasks = false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -60,11 +61,11 @@ module "coder-login" {
|
|||||||
|
|
||||||
module "codex" {
|
module "codex" {
|
||||||
source = "registry.coder.com/coder-labs/codex/coder"
|
source = "registry.coder.com/coder-labs/codex/coder"
|
||||||
version = "2.1.1"
|
version = "3.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
openai_api_key = "..."
|
openai_api_key = "..."
|
||||||
ai_prompt = data.coder_parameter.ai_prompt.value
|
ai_prompt = data.coder_parameter.ai_prompt.value
|
||||||
folder = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
|
|
||||||
# Custom configuration for full auto mode
|
# Custom configuration for full auto mode
|
||||||
base_config_toml = <<-EOT
|
base_config_toml = <<-EOT
|
||||||
@ -75,7 +76,7 @@ module "codex" {
|
|||||||
```
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> This module configures Codex with a `workspace-write` sandbox that allows AI tasks to read/write files in the specified folder. While the sandbox provides security boundaries, Codex can still modify files within the workspace. Use this module _only_ in trusted environments and be aware of the security implications.
|
> This module configures Codex with a `workspace-write` sandbox that allows AI tasks to read/write files in the specified workdir. While the sandbox provides security boundaries, Codex can still modify files within the workspace. Use this module _only_ in trusted environments and be aware of the security implications.
|
||||||
|
|
||||||
## How it Works
|
## How it Works
|
||||||
|
|
||||||
@ -106,7 +107,7 @@ For custom Codex configuration, use `base_config_toml` and/or `additional_mcp_se
|
|||||||
```tf
|
```tf
|
||||||
module "codex" {
|
module "codex" {
|
||||||
source = "registry.coder.com/coder-labs/codex/coder"
|
source = "registry.coder.com/coder-labs/codex/coder"
|
||||||
version = "2.1.1"
|
version = "3.0.0"
|
||||||
# ... other variables ...
|
# ... other variables ...
|
||||||
|
|
||||||
# Override default configuration
|
# Override default configuration
|
||||||
@ -137,7 +138,7 @@ module "codex" {
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set, and **you create a `coder_parameter` named `"AI Prompt"` and pass its value to the codex module's `ai_prompt` variable**. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker).
|
> To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set, and **you create a `coder_parameter` named `"AI Prompt"` and pass its value to the codex module's `ai_prompt` variable**. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker).
|
||||||
> The module automatically configures Codex with your API key and model preferences.
|
> The module automatically configures Codex with your API key and model preferences.
|
||||||
> folder is a required variable for the module to function correctly.
|
> workdir is a required variable for the module to function correctly.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
|||||||
install_codex: props?.skipCodexMock ? "true" : "false",
|
install_codex: props?.skipCodexMock ? "true" : "false",
|
||||||
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
||||||
codex_model: "gpt-4-turbo",
|
codex_model: "gpt-4-turbo",
|
||||||
folder: "/home/coder",
|
workdir: "/home/coder",
|
||||||
...props?.moduleVariables,
|
...props?.moduleVariables,
|
||||||
},
|
},
|
||||||
registerCleanup,
|
registerCleanup,
|
||||||
@ -166,12 +166,12 @@ describe("codex", async () => {
|
|||||||
expect(postInstallLog).toContain("post-install-script");
|
expect(postInstallLog).toContain("post-install-script");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("folder-variable", async () => {
|
test("workdir-variable", async () => {
|
||||||
const folder = "/tmp/codex-test-folder";
|
const workdir = "/tmp/codex-test-workdir";
|
||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
skipCodexMock: false,
|
skipCodexMock: false,
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
folder,
|
workdir,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
@ -179,7 +179,7 @@ describe("codex", async () => {
|
|||||||
id,
|
id,
|
||||||
"/home/coder/.codex-module/install.log",
|
"/home/coder/.codex-module/install.log",
|
||||||
);
|
);
|
||||||
expect(resp).toContain(folder);
|
expect(resp).toContain(workdir);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("additional-mcp-servers", async () => {
|
test("additional-mcp-servers", async () => {
|
||||||
|
|||||||
@ -36,11 +36,41 @@ variable "icon" {
|
|||||||
default = "/icon/openai.svg"
|
default = "/icon/openai.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "folder" {
|
variable "workdir" {
|
||||||
type = string
|
type = string
|
||||||
description = "The folder to run Codex in."
|
description = "The folder to run Codex in."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "report_tasks" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to enable task reporting to Coder UI via AgentAPI"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "subdomain" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to use a subdomain for AgentAPI."
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cli_app" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to create a CLI app for Codex"
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "web_app_display_name" {
|
||||||
|
type = string
|
||||||
|
description = "Display name for the web app"
|
||||||
|
default = "Codex"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cli_app_display_name" {
|
||||||
|
type = string
|
||||||
|
description = "Display name for the CLI app"
|
||||||
|
default = "Codex CLI"
|
||||||
|
}
|
||||||
|
|
||||||
variable "install_codex" {
|
variable "install_codex" {
|
||||||
type = bool
|
type = bool
|
||||||
description = "Whether to install Codex."
|
description = "Whether to install Codex."
|
||||||
@ -120,6 +150,7 @@ resource "coder_env" "openai_api_key" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
|
workdir = trimsuffix(var.workdir, "/")
|
||||||
app_slug = "codex"
|
app_slug = "codex"
|
||||||
install_script = file("${path.module}/scripts/install.sh")
|
install_script = file("${path.module}/scripts/install.sh")
|
||||||
start_script = file("${path.module}/scripts/start.sh")
|
start_script = file("${path.module}/scripts/start.sh")
|
||||||
@ -131,16 +162,18 @@ module "agentapi" {
|
|||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
folder = var.folder
|
folder = local.workdir
|
||||||
web_app_slug = local.app_slug
|
web_app_slug = local.app_slug
|
||||||
web_app_order = var.order
|
web_app_order = var.order
|
||||||
web_app_group = var.group
|
web_app_group = var.group
|
||||||
web_app_icon = var.icon
|
web_app_icon = var.icon
|
||||||
web_app_display_name = "Codex"
|
web_app_display_name = var.web_app_display_name
|
||||||
cli_app_slug = "${local.app_slug}-cli"
|
cli_app = var.cli_app
|
||||||
cli_app_display_name = "Codex CLI"
|
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
|
||||||
|
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
|
||||||
module_dir_name = local.module_dir_name
|
module_dir_name = local.module_dir_name
|
||||||
install_agentapi = var.install_agentapi
|
install_agentapi = var.install_agentapi
|
||||||
|
agentapi_subdomain = var.subdomain
|
||||||
agentapi_version = var.agentapi_version
|
agentapi_version = var.agentapi_version
|
||||||
pre_install_script = var.pre_install_script
|
pre_install_script = var.pre_install_script
|
||||||
post_install_script = var.post_install_script
|
post_install_script = var.post_install_script
|
||||||
@ -152,8 +185,9 @@ module "agentapi" {
|
|||||||
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
|
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
|
||||||
chmod +x /tmp/start.sh
|
chmod +x /tmp/start.sh
|
||||||
ARG_OPENAI_API_KEY='${var.openai_api_key}' \
|
ARG_OPENAI_API_KEY='${var.openai_api_key}' \
|
||||||
|
ARG_REPORT_TASKS='${var.report_tasks}' \
|
||||||
ARG_CODEX_MODEL='${var.codex_model}' \
|
ARG_CODEX_MODEL='${var.codex_model}' \
|
||||||
ARG_CODEX_START_DIRECTORY='${var.folder}' \
|
ARG_CODEX_START_DIRECTORY='${var.workdir}' \
|
||||||
ARG_CODEX_TASK_PROMPT='${base64encode(var.ai_prompt)}' \
|
ARG_CODEX_TASK_PROMPT='${base64encode(var.ai_prompt)}' \
|
||||||
/tmp/start.sh
|
/tmp/start.sh
|
||||||
EOT
|
EOT
|
||||||
@ -165,12 +199,14 @@ module "agentapi" {
|
|||||||
|
|
||||||
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
|
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
|
||||||
chmod +x /tmp/install.sh
|
chmod +x /tmp/install.sh
|
||||||
|
ARG_OPENAI_API_KEY='${var.openai_api_key}' \
|
||||||
|
ARG_REPORT_TASKS='${var.report_tasks}' \
|
||||||
ARG_INSTALL='${var.install_codex}' \
|
ARG_INSTALL='${var.install_codex}' \
|
||||||
ARG_CODEX_VERSION='${var.codex_version}' \
|
ARG_CODEX_VERSION='${var.codex_version}' \
|
||||||
ARG_BASE_CONFIG_TOML='${base64encode(var.base_config_toml)}' \
|
ARG_BASE_CONFIG_TOML='${base64encode(var.base_config_toml)}' \
|
||||||
ARG_ADDITIONAL_MCP_SERVERS='${base64encode(var.additional_mcp_servers)}' \
|
ARG_ADDITIONAL_MCP_SERVERS='${base64encode(var.additional_mcp_servers)}' \
|
||||||
ARG_CODER_MCP_APP_STATUS_SLUG='${local.app_slug}' \
|
ARG_CODER_MCP_APP_STATUS_SLUG='${local.app_slug}' \
|
||||||
ARG_CODEX_START_DIRECTORY='${var.folder}' \
|
ARG_CODEX_START_DIRECTORY='${var.workdir}' \
|
||||||
ARG_CODEX_INSTRUCTION_PROMPT='${base64encode(var.codex_system_prompt)}' \
|
ARG_CODEX_INSTRUCTION_PROMPT='${base64encode(var.codex_system_prompt)}' \
|
||||||
/tmp/install.sh
|
/tmp/install.sh
|
||||||
EOT
|
EOT
|
||||||
|
|||||||
@ -22,6 +22,8 @@ printf "Start Directory: %s\n" "$ARG_CODEX_START_DIRECTORY"
|
|||||||
printf "Has Base Config: %s\n" "$([ -n "$ARG_BASE_CONFIG_TOML" ] && echo "Yes" || echo "No")"
|
printf "Has Base Config: %s\n" "$([ -n "$ARG_BASE_CONFIG_TOML" ] && echo "Yes" || echo "No")"
|
||||||
printf "Has Additional MCP: %s\n" "$([ -n "$ARG_ADDITIONAL_MCP_SERVERS" ] && echo "Yes" || echo "No")"
|
printf "Has Additional MCP: %s\n" "$([ -n "$ARG_ADDITIONAL_MCP_SERVERS" ] && echo "Yes" || echo "No")"
|
||||||
printf "Has System Prompt: %s\n" "$([ -n "$ARG_CODEX_INSTRUCTION_PROMPT" ] && echo "Yes" || echo "No")"
|
printf "Has System Prompt: %s\n" "$([ -n "$ARG_CODEX_INSTRUCTION_PROMPT" ] && echo "Yes" || echo "No")"
|
||||||
|
printf "OpenAI API Key: %s\n" "$([ -n "$ARG_OPENAI_API_KEY" ] && echo "Provided" || echo "Not provided")"
|
||||||
|
printf "Report Tasks: %s\n" "$ARG_REPORT_TASKS"
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
|
|
||||||
set +o nounset
|
set +o nounset
|
||||||
@ -100,13 +102,20 @@ EOF
|
|||||||
append_mcp_servers_section() {
|
append_mcp_servers_section() {
|
||||||
local config_path="$1"
|
local config_path="$1"
|
||||||
|
|
||||||
|
if [ "${ARG_REPORT_TASKS}" == "false" ]; then
|
||||||
|
ARG_CODER_MCP_APP_STATUS_SLUG=""
|
||||||
|
CODER_MCP_AI_AGENTAPI_URL=""
|
||||||
|
else
|
||||||
|
CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284"
|
||||||
|
fi
|
||||||
|
|
||||||
cat << EOF >> "$config_path"
|
cat << EOF >> "$config_path"
|
||||||
|
|
||||||
# MCP Servers Configuration
|
# MCP Servers Configuration
|
||||||
[mcp_servers.Coder]
|
[mcp_servers.Coder]
|
||||||
command = "coder"
|
command = "coder"
|
||||||
args = ["exp", "mcp", "server"]
|
args = ["exp", "mcp", "server"]
|
||||||
env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL" = "http://localhost:3284", "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}" }
|
env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL" = "${CODER_MCP_AI_AGENTAPI_URL}" , "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}" }
|
||||||
description = "Report ALL tasks and statuses (in progress, done, failed) you are working on."
|
description = "Report ALL tasks and statuses (in progress, done, failed) you are working on."
|
||||||
type = "stdio"
|
type = "stdio"
|
||||||
|
|
||||||
@ -159,7 +168,21 @@ function add_instruction_prompt_if_exists() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function add_auth_json() {
|
||||||
|
AUTH_JSON_PATH="$HOME/.codex/auth.json"
|
||||||
|
mkdir -p "$(dirname "$AUTH_JSON_PATH")"
|
||||||
|
AUTH_JSON=$(
|
||||||
|
cat << EOF
|
||||||
|
{
|
||||||
|
"OPENAI_API_KEY": "${ARG_OPENAI_API_KEY}"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
echo "$AUTH_JSON" > "$AUTH_JSON_PATH"
|
||||||
|
}
|
||||||
|
|
||||||
install_codex
|
install_codex
|
||||||
codex --version
|
codex --version
|
||||||
populate_config_toml
|
populate_config_toml
|
||||||
add_instruction_prompt_if_exists
|
add_instruction_prompt_if_exists
|
||||||
|
add_auth_json
|
||||||
|
|||||||
@ -22,6 +22,7 @@ printf "OpenAI API Key: %s\n" "$([ -n "$ARG_OPENAI_API_KEY" ] && echo "Provided"
|
|||||||
printf "Codex Model: %s\n" "${ARG_CODEX_MODEL:-"Default"}"
|
printf "Codex Model: %s\n" "${ARG_CODEX_MODEL:-"Default"}"
|
||||||
printf "Start Directory: %s\n" "$ARG_CODEX_START_DIRECTORY"
|
printf "Start Directory: %s\n" "$ARG_CODEX_START_DIRECTORY"
|
||||||
printf "Has Task Prompt: %s\n" "$([ -n "$ARG_CODEX_TASK_PROMPT" ] && echo "Yes" || echo "No")"
|
printf "Has Task Prompt: %s\n" "$([ -n "$ARG_CODEX_TASK_PROMPT" ] && echo "Yes" || echo "No")"
|
||||||
|
printf "Report Tasks: %s\n" "$ARG_REPORT_TASKS"
|
||||||
echo "======================================"
|
echo "======================================"
|
||||||
set +o nounset
|
set +o nounset
|
||||||
CODEX_ARGS=()
|
CODEX_ARGS=()
|
||||||
@ -57,7 +58,11 @@ fi
|
|||||||
|
|
||||||
if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then
|
if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then
|
||||||
printf "Running the task prompt %s\n" "$ARG_CODEX_TASK_PROMPT"
|
printf "Running the task prompt %s\n" "$ARG_CODEX_TASK_PROMPT"
|
||||||
|
if [ "${ARG_REPORT_TASKS}" == "true" ]; then
|
||||||
PROMPT="Complete the task at hand in one go. Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT"
|
PROMPT="Complete the task at hand in one go. Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT"
|
||||||
|
else
|
||||||
|
PROMPT="Your task at hand: $ARG_CODEX_TASK_PROMPT"
|
||||||
|
fi
|
||||||
CODEX_ARGS+=("$PROMPT")
|
CODEX_ARGS+=("$PROMPT")
|
||||||
else
|
else
|
||||||
printf "No task prompt given.\n"
|
printf "No task prompt given.\n"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
display_name: Amp CLI
|
display_name: Amp
|
||||||
icon: ../../../../.icons/sourcegraph-amp.svg
|
icon: ../../../../.icons/sourcegraph-amp.svg
|
||||||
description: Sourcegraph's AI coding agent with deep codebase understanding and intelligent code search capabilities
|
description: Sourcegraph's AI coding agent with deep codebase understanding and intelligent code search capabilities
|
||||||
verified: true
|
verified: true
|
||||||
@ -13,7 +13,7 @@ Run [Amp CLI](https://ampcode.com/) in your workspace to access Sourcegraph's AI
|
|||||||
```tf
|
```tf
|
||||||
module "amp-cli" {
|
module "amp-cli" {
|
||||||
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
|
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
|
||||||
version = "1.1.1"
|
version = "2.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
sourcegraph_amp_api_key = var.sourcegraph_amp_api_key
|
sourcegraph_amp_api_key = var.sourcegraph_amp_api_key
|
||||||
install_sourcegraph_amp = true
|
install_sourcegraph_amp = true
|
||||||
@ -23,8 +23,10 @@ module "amp-cli" {
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Include the [Coder Login](https://registry.coder.com/modules/coder-login/coder) module in your template
|
- **Default (official installer)**: No prerequisites - the official installer includes its own runtime (Bun)
|
||||||
- Node.js and npm are automatically installed (via NVM) if not already available
|
- **npm installation (`install_via_npm = true`)**: Requires Node.js and npm to be installed before Amp installation
|
||||||
|
- Required for Alpine Linux or other musl-based systems
|
||||||
|
- Ensure Node.js and npm are available in your workspace image or via earlier provisioning steps
|
||||||
|
|
||||||
## Usage Example
|
## Usage Example
|
||||||
|
|
||||||
@ -35,23 +37,9 @@ data "coder_parameter" "ai_prompt" {
|
|||||||
type = "string"
|
type = "string"
|
||||||
default = ""
|
default = ""
|
||||||
mutable = true
|
mutable = true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set system prompt for Amp CLI via environment variables
|
variable "amp_api_key" {
|
||||||
resource "coder_agent" "main" {
|
|
||||||
# ...
|
|
||||||
env = {
|
|
||||||
SOURCEGRAPH_AMP_SYSTEM_PROMPT = <<-EOT
|
|
||||||
You are an Amp assistant that helps developers debug and write code efficiently.
|
|
||||||
|
|
||||||
Always log task status to Coder.
|
|
||||||
EOT
|
|
||||||
SOURCEGRAPH_AMP_TASK_PROMPT = data.coder_parameter.ai_prompt.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "sourcegraph_amp_api_key" {
|
|
||||||
type = string
|
type = string
|
||||||
description = "Sourcegraph Amp API key. Get one at https://ampcode.com/settings"
|
description = "Sourcegraph Amp API key. Get one at https://ampcode.com/settings"
|
||||||
sensitive = true
|
sensitive = true
|
||||||
@ -60,27 +48,44 @@ variable "sourcegraph_amp_api_key" {
|
|||||||
module "amp-cli" {
|
module "amp-cli" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
|
source = "registry.coder.com/coder-labs/sourcegraph-amp/coder"
|
||||||
version = "1.1.1"
|
amp_version = "2.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
sourcegraph_amp_api_key = var.sourcegraph_amp_api_key # recommended for authenticated usage
|
amp_api_key = var.amp_api_key # recommended for tasks usage
|
||||||
install_sourcegraph_amp = true
|
workdir = "/home/coder/project"
|
||||||
|
instruction_prompt = <<-EOT
|
||||||
|
# Instructions
|
||||||
|
- Start every response with `amp > `
|
||||||
|
EOT
|
||||||
|
ai_prompt = data.coder_parameter.ai_prompt.value
|
||||||
|
base_amp_config = jsonencode({
|
||||||
|
"amp.anthropic.thinking.enabled" = true
|
||||||
|
"amp.todos.enabled" = true
|
||||||
|
"amp.tools.stopTimeout" = 600
|
||||||
|
"amp.git.commit.ampThread.enabled" = true
|
||||||
|
"amp.git.commit.coauthor.enabled" = true
|
||||||
|
"amp.terminal.commands.nodeSpawn.loadProfile" = "daily"
|
||||||
|
"amp.permissions" = [
|
||||||
|
{ "tool" : "mcp__coder__*", "action" : "allow" },
|
||||||
|
{ "tool" : "Bash", "action" : "allow", "context" : "thread" },
|
||||||
|
{ "tool" : "Bash", "matches" : { "cmd" : ["rm -rf /*", "rm -rf ~/*"] }, "action" : "reject", "context" : "subagent" },
|
||||||
|
{ "tool" : "edit_file", "action" : "allow" },
|
||||||
|
{ "tool" : "write_file", "action" : "allow" },
|
||||||
|
{ "tool" : "read_file", "action" : "allow" },
|
||||||
|
{ "tool" : "Grep", "action" : "allow" }
|
||||||
|
]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## How it Works
|
|
||||||
|
|
||||||
- **Install**: Installs Sourcegraph Amp CLI using npm (installs Node.js via NVM if required)
|
|
||||||
- **Start**: Launches Amp CLI in the specified directory, wrapped with AgentAPI to enable tasks and AI interactions
|
|
||||||
- **Environment Variables**: Sets `SOURCEGRAPH_AMP_API_KEY` and `SOURCEGRAPH_AMP_START_DIRECTORY` for the CLI execution
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- If `amp` is not found, ensure `install_sourcegraph_amp = true` and your API key is valid
|
- If `amp` is not found, ensure `install_amp = true` and your API key is valid
|
||||||
- Logs are written under `/home/coder/.sourcegraph-amp-module/` (`install.log`, `agentapi-start.log`) for debugging
|
- Logs are written under `/home/coder/.amp-module/` (`install.log`, `agentapi-start.log`) for debugging
|
||||||
- If AgentAPI fails to start, verify that your container has network access and executable permissions for the scripts
|
- If AgentAPI fails to start, verify that your container has network access and executable permissions for the scripts
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> For using **Coder Tasks** with Amp CLI, make sure to pass the `AI Prompt` parameter and set `sourcegraph_amp_api_key`.
|
> To use tasks with Amp CLI, create a `coder_parameter` named `"AI Prompt"` and pass its value to the amp-cli module's `ai_prompt` variable. The `folder` variable is required for the module to function correctly.
|
||||||
|
> For using **Coder Tasks** with Amp CLI, make sure to set `amp_api_key`.
|
||||||
> This ensures task reporting and status updates work seamlessly.
|
> This ensures task reporting and status updates work seamlessly.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|||||||
@ -43,9 +43,9 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
|||||||
const { id } = await setupUtil({
|
const { id } = await setupUtil({
|
||||||
moduleDir: import.meta.dir,
|
moduleDir: import.meta.dir,
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
install_sourcegraph_amp: props?.skipAmpMock ? "true" : "false",
|
workdir: "/home/coder",
|
||||||
|
install_amp: props?.skipAmpMock ? "true" : "false",
|
||||||
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
||||||
sourcegraph_amp_model: "test-model",
|
|
||||||
...props?.moduleVariables,
|
...props?.moduleVariables,
|
||||||
},
|
},
|
||||||
registerCleanup,
|
registerCleanup,
|
||||||
@ -68,45 +68,94 @@ const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
|||||||
|
|
||||||
setDefaultTimeout(60 * 1000);
|
setDefaultTimeout(60 * 1000);
|
||||||
|
|
||||||
describe("sourcegraph-amp", async () => {
|
describe("amp", async () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("happy-path", async () => {
|
// test("happy-path", async () => {
|
||||||
const { id } = await setup();
|
// const { id } = await setup();
|
||||||
|
// await execModuleScript(id);
|
||||||
|
// await expectAgentAPIStarted(id);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// test("api-key", async () => {
|
||||||
|
// const apiKey = "test-api-key-123";
|
||||||
|
// const { id } = await setup({
|
||||||
|
// moduleVariables: {
|
||||||
|
// amp_api_key: apiKey,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// await execModuleScript(id);
|
||||||
|
// const resp = await readFileContainer(
|
||||||
|
// id,
|
||||||
|
// "/home/coder/.amp-module/agentapi-start.log",
|
||||||
|
// );
|
||||||
|
// expect(resp).toContain("amp_api_key provided !");
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
test("install-latest-version", async () => {
|
||||||
|
const { id } = await setup({
|
||||||
|
skipAmpMock: true,
|
||||||
|
skipAgentAPIMock: true,
|
||||||
|
moduleVariables: {
|
||||||
|
amp_version: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
await expectAgentAPIStarted(id);
|
await expectAgentAPIStarted(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("api-key", async () => {
|
test("install-specific-version", async () => {
|
||||||
const apiKey = "test-api-key-123";
|
|
||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
|
skipAmpMock: true,
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
sourcegraph_amp_api_key: apiKey,
|
amp_version: "0.0.1755964909-g31e083",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
const resp = await readFileContainer(
|
const resp = await readFileContainer(
|
||||||
id,
|
id,
|
||||||
"/home/coder/.sourcegraph-amp-module/agentapi-start.log",
|
"/home/coder/.amp-module/agentapi-start.log",
|
||||||
);
|
);
|
||||||
expect(resp).toContain("sourcegraph_amp_api_key provided !");
|
expect(resp).toContain("0.0.1755964909-g31e08");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("custom-folder", async () => {
|
test("install-via-npm", async () => {
|
||||||
const folder = "/tmp/sourcegraph-amp-test";
|
const { id } = await setup({
|
||||||
|
skipAmpMock: true,
|
||||||
|
moduleVariables: {
|
||||||
|
install_via_npm: "true",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await execModuleScript(id);
|
||||||
|
|
||||||
|
const installLog = await readFileContainer(
|
||||||
|
id,
|
||||||
|
"/home/coder/.amp-module/install.log",
|
||||||
|
);
|
||||||
|
expect(installLog).toContain("Installing Amp via npm");
|
||||||
|
|
||||||
|
const startLog = await readFileContainer(
|
||||||
|
id,
|
||||||
|
"/home/coder/.amp-module/agentapi-start.log",
|
||||||
|
);
|
||||||
|
expect(startLog).toContain("AMP version:");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("custom-workdir", async () => {
|
||||||
|
const workdir = "/tmp/amp-test";
|
||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
folder,
|
workdir,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
const resp = await readFileContainer(
|
const resp = await readFileContainer(
|
||||||
id,
|
id,
|
||||||
"/home/coder/.sourcegraph-amp-module/install.log",
|
"/home/coder/.amp-module/agentapi-start.log",
|
||||||
);
|
);
|
||||||
expect(resp).toContain(folder);
|
expect(resp).toContain(workdir);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("pre-post-install-scripts", async () => {
|
test("pre-post-install-scripts", async () => {
|
||||||
@ -119,39 +168,104 @@ describe("sourcegraph-amp", async () => {
|
|||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
const preLog = await readFileContainer(
|
const preLog = await readFileContainer(
|
||||||
id,
|
id,
|
||||||
"/home/coder/.sourcegraph-amp-module/pre_install.log",
|
"/home/coder/.amp-module/pre_install.log",
|
||||||
);
|
);
|
||||||
expect(preLog).toContain("pre-install-script");
|
expect(preLog).toContain("pre-install-script");
|
||||||
const postLog = await readFileContainer(
|
const postLog = await readFileContainer(
|
||||||
id,
|
id,
|
||||||
"/home/coder/.sourcegraph-amp-module/post_install.log",
|
"/home/coder/.amp-module/post_install.log",
|
||||||
);
|
);
|
||||||
expect(postLog).toContain("post-install-script");
|
expect(postLog).toContain("post-install-script");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("system-prompt", async () => {
|
test("instruction-prompt", async () => {
|
||||||
const prompt = "this is a system prompt for AMP";
|
const prompt = "this is a instruction prompt for AMP";
|
||||||
const { id } = await setup();
|
const { id } = await setup({
|
||||||
await execModuleScript(id, {
|
moduleVariables: {
|
||||||
SOURCEGRAPH_AMP_SYSTEM_PROMPT: prompt,
|
instruction_prompt: prompt,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const resp = await readFileContainer(
|
await execModuleScript(id);
|
||||||
id,
|
const resp = await readFileContainer(id, "/home/coder/.config/AGENTS.md");
|
||||||
"/home/coder/.sourcegraph-amp-module/SYSTEM_PROMPT.md",
|
|
||||||
);
|
|
||||||
expect(resp).toContain(prompt);
|
expect(resp).toContain(prompt);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("task-prompt", async () => {
|
test("ai-prompt", async () => {
|
||||||
const prompt = "this is a task prompt for AMP";
|
const prompt = "this is a task prompt for AMP";
|
||||||
const { id } = await setup();
|
const { id } = await setup({
|
||||||
await execModuleScript(id, {
|
moduleVariables: {
|
||||||
SOURCEGRAPH_AMP_TASK_PROMPT: prompt,
|
ai_prompt: prompt,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
await execModuleScript(id);
|
||||||
const resp = await readFileContainer(
|
const resp = await readFileContainer(
|
||||||
id,
|
id,
|
||||||
"/home/coder/.sourcegraph-amp-module/agentapi-start.log",
|
"/home/coder/.amp-module/agentapi-start.log",
|
||||||
);
|
);
|
||||||
expect(resp).toContain(`sourcegraph amp task prompt provided : ${prompt}`);
|
expect(resp).toContain(`amp task prompt provided : ${prompt}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("custom-base-config", async () => {
|
||||||
|
const customConfig = JSON.stringify({
|
||||||
|
"amp.anthropic.thinking.enabled": false,
|
||||||
|
"amp.todos.enabled": false,
|
||||||
|
"amp.tools.stopTimeout": 900,
|
||||||
|
"amp.git.commit.ampThread.enabled": true,
|
||||||
|
});
|
||||||
|
const customMcp = JSON.stringify({
|
||||||
|
"test-server": {
|
||||||
|
command: "/usr/bin/test-mcp",
|
||||||
|
args: ["--test-arg"],
|
||||||
|
type: "stdio",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { id } = await setup({
|
||||||
|
moduleVariables: {
|
||||||
|
base_amp_config: customConfig,
|
||||||
|
mcp: customMcp,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await execModuleScript(id, {
|
||||||
|
CODER_AGENT_TOKEN: "test-token",
|
||||||
|
CODER_AGENT_URL: "http://test-url:3000",
|
||||||
|
});
|
||||||
|
const settingsContent = await readFileContainer(
|
||||||
|
id,
|
||||||
|
"/home/coder/.config/amp/settings.json",
|
||||||
|
);
|
||||||
|
const settings = JSON.parse(settingsContent);
|
||||||
|
|
||||||
|
expect(settings["amp.anthropic.thinking.enabled"]).toBe(false);
|
||||||
|
expect(settings["amp.todos.enabled"]).toBe(false);
|
||||||
|
expect(settings["amp.tools.stopTimeout"]).toBe(900);
|
||||||
|
expect(settings["amp.git.commit.ampThread.enabled"]).toBe(true);
|
||||||
|
expect(settings["amp.mcpServers"]).toBeDefined();
|
||||||
|
expect(settings["amp.mcpServers"].coder).toBeDefined();
|
||||||
|
expect(settings["amp.mcpServers"]["test-server"]).toBeDefined();
|
||||||
|
expect(settings["amp.mcpServers"]["test-server"].command).toBe(
|
||||||
|
"/usr/bin/test-mcp",
|
||||||
|
);
|
||||||
|
expect(settings["amp.mcpServers"]["test-server"].args).toEqual([
|
||||||
|
"--test-arg",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("default-base-config", async () => {
|
||||||
|
const { id } = await setup();
|
||||||
|
await execModuleScript(id, {
|
||||||
|
CODER_AGENT_TOKEN: "test-token",
|
||||||
|
CODER_AGENT_URL: "http://test-url:3000",
|
||||||
|
});
|
||||||
|
const settingsContent = await readFileContainer(
|
||||||
|
id,
|
||||||
|
"/home/coder/.config/amp/settings.json",
|
||||||
|
);
|
||||||
|
const settings = JSON.parse(settingsContent);
|
||||||
|
|
||||||
|
expect(settings["amp.anthropic.thinking.enabled"]).toBe(true);
|
||||||
|
expect(settings["amp.todos.enabled"]).toBe(true);
|
||||||
|
expect(settings["amp.mcpServers"]).toBeDefined();
|
||||||
|
expect(settings["amp.mcpServers"].coder).toBeDefined();
|
||||||
|
expect(settings["amp.mcpServers"].coder.command).toBe("coder");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,28 +36,9 @@ variable "icon" {
|
|||||||
default = "/icon/sourcegraph-amp.svg"
|
default = "/icon/sourcegraph-amp.svg"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "folder" {
|
variable "workdir" {
|
||||||
type = string
|
type = string
|
||||||
description = "The folder to run sourcegraph_amp in."
|
description = "The folder to run AMP CLI in."
|
||||||
default = "/home/coder"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "install_sourcegraph_amp" {
|
|
||||||
type = bool
|
|
||||||
description = "Whether to install sourcegraph-amp."
|
|
||||||
default = true
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "sourcegraph_amp_api_key" {
|
|
||||||
type = string
|
|
||||||
description = "sourcegraph-amp API Key"
|
|
||||||
default = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "coder_env" "sourcegraph_amp_api_key" {
|
|
||||||
agent_id = var.agent_id
|
|
||||||
name = "SOURCEGRAPH_AMP_API_KEY"
|
|
||||||
value = var.sourcegraph_amp_api_key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "install_agentapi" {
|
variable "install_agentapi" {
|
||||||
@ -72,18 +53,84 @@ variable "agentapi_version" {
|
|||||||
default = "v0.10.0"
|
default = "v0.10.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "cli_app" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to create a CLI app for Claude Code"
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "web_app_display_name" {
|
||||||
|
type = string
|
||||||
|
description = "Display name for the web app"
|
||||||
|
default = "Amp"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cli_app_display_name" {
|
||||||
|
type = string
|
||||||
|
description = "Display name for the CLI app"
|
||||||
|
default = "Amp CLI"
|
||||||
|
}
|
||||||
|
|
||||||
variable "pre_install_script" {
|
variable "pre_install_script" {
|
||||||
type = string
|
type = string
|
||||||
description = "Custom script to run before installing sourcegraph_amp"
|
description = "Custom script to run before installing amp cli"
|
||||||
default = null
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "post_install_script" {
|
variable "post_install_script" {
|
||||||
type = string
|
type = string
|
||||||
description = "Custom script to run after installing sourcegraph_amp."
|
description = "Custom script to run after installing amp cli."
|
||||||
default = null
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "report_tasks" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to enable task reporting to Coder UI"
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "install_amp" {
|
||||||
|
type = bool
|
||||||
|
description = "Whether to install amp cli."
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "install_via_npm" {
|
||||||
|
type = bool
|
||||||
|
description = "Install Amp via npm instead of the official installer."
|
||||||
|
default = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "amp_api_key" {
|
||||||
|
type = string
|
||||||
|
description = "amp cli API Key"
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "amp_version" {
|
||||||
|
type = string
|
||||||
|
description = "The version of amp cli to install."
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ai_prompt" {
|
||||||
|
type = string
|
||||||
|
description = "Task prompt for the Amp CLI"
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "instruction_prompt" {
|
||||||
|
type = string
|
||||||
|
description = "Instruction prompt for the Amp CLI. https://ampcode.com/manual#AGENTS.md"
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "coder_env" "amp_api_key" {
|
||||||
|
agent_id = var.agent_id
|
||||||
|
name = "AMP_API_KEY"
|
||||||
|
value = var.amp_api_key
|
||||||
|
}
|
||||||
|
|
||||||
variable "base_amp_config" {
|
variable "base_amp_config" {
|
||||||
type = string
|
type = string
|
||||||
description = <<-EOT
|
description = <<-EOT
|
||||||
@ -102,22 +149,25 @@ variable "base_amp_config" {
|
|||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "additional_mcp_servers" {
|
variable "mcp" {
|
||||||
type = string
|
type = string
|
||||||
description = "Additional MCP servers configuration in JSON format to append to amp.mcpServers."
|
description = "Additional MCP servers configuration in JSON format to append to amp.mcpServers."
|
||||||
default = null
|
default = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data "external" "env" {
|
||||||
|
program = ["sh", "-c", "echo '{\"CODER_AGENT_TOKEN\":\"'$CODER_AGENT_TOKEN'\",\"CODER_AGENT_URL\":\"'$CODER_AGENT_URL'\"}'"]
|
||||||
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
app_slug = "amp"
|
app_slug = "amp"
|
||||||
|
|
||||||
default_base_config = {
|
default_base_config = jsonencode({
|
||||||
"amp.anthropic.thinking.enabled" = true
|
"amp.anthropic.thinking.enabled" = true
|
||||||
"amp.todos.enabled" = true
|
"amp.todos.enabled" = true
|
||||||
}
|
})
|
||||||
|
|
||||||
# Use provided config or default, then extract base settings (excluding mcpServers)
|
user_config = jsondecode(var.base_amp_config != "" ? var.base_amp_config : local.default_base_config)
|
||||||
user_config = var.base_amp_config != "" ? jsondecode(var.base_amp_config) : local.default_base_config
|
|
||||||
base_amp_settings = { for k, v in local.user_config : k => v if k != "amp.mcpServers" }
|
base_amp_settings = { for k, v in local.user_config : k => v if k != "amp.mcpServers" }
|
||||||
|
|
||||||
coder_mcp = {
|
coder_mcp = {
|
||||||
@ -125,14 +175,16 @@ locals {
|
|||||||
"command" = "coder"
|
"command" = "coder"
|
||||||
"args" = ["exp", "mcp", "server"]
|
"args" = ["exp", "mcp", "server"]
|
||||||
"env" = {
|
"env" = {
|
||||||
"CODER_MCP_APP_STATUS_SLUG" = local.app_slug
|
"CODER_MCP_APP_STATUS_SLUG" = var.report_tasks == true ? local.app_slug : ""
|
||||||
"CODER_MCP_AI_AGENTAPI_URL" = "http://localhost:3284"
|
"CODER_MCP_AI_AGENTAPI_URL" = var.report_tasks == true ? "http://localhost:3284" : ""
|
||||||
|
"CODER_AGENT_TOKEN" = data.external.env.result.CODER_AGENT_TOKEN
|
||||||
|
"CODER_AGENT_URL" = data.external.env.result.CODER_AGENT_URL
|
||||||
}
|
}
|
||||||
"type" = "stdio"
|
"type" = "stdio"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
additional_mcp = var.additional_mcp_servers != null ? jsondecode(var.additional_mcp_servers) : {}
|
additional_mcp = var.mcp != null ? jsondecode(var.mcp) : {}
|
||||||
|
|
||||||
merged_mcp_servers = merge(
|
merged_mcp_servers = merge(
|
||||||
lookup(local.user_config, "amp.mcpServers", {}),
|
lookup(local.user_config, "amp.mcpServers", {}),
|
||||||
@ -146,8 +198,8 @@ locals {
|
|||||||
|
|
||||||
install_script = file("${path.module}/scripts/install.sh")
|
install_script = file("${path.module}/scripts/install.sh")
|
||||||
start_script = file("${path.module}/scripts/start.sh")
|
start_script = file("${path.module}/scripts/start.sh")
|
||||||
module_dir_name = ".sourcegraph-amp-module"
|
module_dir_name = ".amp-module"
|
||||||
folder = trimsuffix(var.folder, "/")
|
workdir = trimsuffix(var.workdir, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
module "agentapi" {
|
module "agentapi" {
|
||||||
@ -155,14 +207,15 @@ module "agentapi" {
|
|||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
folder = local.folder
|
folder = local.workdir
|
||||||
web_app_slug = local.app_slug
|
web_app_slug = local.app_slug
|
||||||
web_app_order = var.order
|
web_app_order = var.order
|
||||||
web_app_group = var.group
|
web_app_group = var.group
|
||||||
web_app_icon = var.icon
|
web_app_icon = var.icon
|
||||||
web_app_display_name = "Sourcegraph Amp"
|
web_app_display_name = var.web_app_display_name
|
||||||
cli_app_slug = "${local.app_slug}-cli"
|
cli_app = var.cli_app
|
||||||
cli_app_display_name = "Sourcegraph Amp CLI"
|
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
|
||||||
|
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
|
||||||
module_dir_name = local.module_dir_name
|
module_dir_name = local.module_dir_name
|
||||||
install_agentapi = var.install_agentapi
|
install_agentapi = var.install_agentapi
|
||||||
agentapi_version = var.agentapi_version
|
agentapi_version = var.agentapi_version
|
||||||
@ -175,8 +228,10 @@ module "agentapi" {
|
|||||||
|
|
||||||
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
|
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
|
||||||
chmod +x /tmp/start.sh
|
chmod +x /tmp/start.sh
|
||||||
SOURCEGRAPH_AMP_API_KEY='${var.sourcegraph_amp_api_key}' \
|
ARG_AMP_API_KEY='${var.amp_api_key}' \
|
||||||
SOURCEGRAPH_AMP_START_DIRECTORY='${var.folder}' \
|
ARG_AMP_START_DIRECTORY='${var.workdir}' \
|
||||||
|
ARG_AMP_TASK_PROMPT='${base64encode(var.ai_prompt)}' \
|
||||||
|
ARG_REPORT_TASKS='${var.report_tasks}' \
|
||||||
/tmp/start.sh
|
/tmp/start.sh
|
||||||
EOT
|
EOT
|
||||||
|
|
||||||
@ -187,9 +242,11 @@ module "agentapi" {
|
|||||||
|
|
||||||
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
|
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
|
||||||
chmod +x /tmp/install.sh
|
chmod +x /tmp/install.sh
|
||||||
ARG_INSTALL_SOURCEGRAPH_AMP='${var.install_sourcegraph_amp}' \
|
ARG_INSTALL_AMP='${var.install_amp}' \
|
||||||
SOURCEGRAPH_AMP_START_DIRECTORY='${var.folder}' \
|
ARG_INSTALL_VIA_NPM='${var.install_via_npm}' \
|
||||||
ARG_AMP_CONFIG="$(echo -n '${base64encode(jsonencode(local.final_config))}' | base64 -d)" \
|
ARG_AMP_CONFIG="${base64encode(jsonencode(local.final_config))}" \
|
||||||
|
ARG_AMP_VERSION='${var.amp_version}' \
|
||||||
|
ARG_AMP_INSTRUCTION_PROMPT='${base64encode(var.instruction_prompt)}' \
|
||||||
/tmp/install.sh
|
/tmp/install.sh
|
||||||
EOT
|
EOT
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,77 +1,119 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$HOME"/.bashrc
|
||||||
|
|
||||||
# ANSI colors
|
# ANSI colors
|
||||||
BOLD='\033[1m'
|
BOLD='\033[1m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
ARG_INSTALL_AMP=${ARG_INSTALL_AMP:-true}
|
||||||
|
ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
|
||||||
|
ARG_AMP_VERSION=${ARG_AMP_VERSION:-}
|
||||||
|
ARG_AMP_INSTRUCTION_PROMPT=$(echo -n "${ARG_AMP_INSTRUCTION_PROMPT:-}" | base64 -d)
|
||||||
|
ARG_AMP_CONFIG=$(echo -n "${ARG_AMP_CONFIG:-}" | base64 -d)
|
||||||
|
|
||||||
echo "--------------------------------"
|
echo "--------------------------------"
|
||||||
echo "Install flag: $ARG_INSTALL_SOURCEGRAPH_AMP"
|
printf "Install flag: %s\n" "$ARG_INSTALL_AMP"
|
||||||
echo "Workspace: $SOURCEGRAPH_AMP_START_DIRECTORY"
|
printf "Install via npm: %s\n" "$ARG_INSTALL_VIA_NPM"
|
||||||
|
printf "Amp Version: %s\n" "$ARG_AMP_VERSION"
|
||||||
|
printf "AMP Config: %s\n" "$ARG_AMP_CONFIG"
|
||||||
|
printf "Instruction Prompt: %s\n" "$ARG_AMP_INSTRUCTION_PROMPT"
|
||||||
echo "--------------------------------"
|
echo "--------------------------------"
|
||||||
|
|
||||||
# Helper function to check if a command exists
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" > /dev/null 2>&1
|
command -v "$1" > /dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_node() {
|
install_amp_npm() {
|
||||||
if ! command_exists npm; then
|
printf "%s${YELLOW}Installing Amp via npm${NC}\n" "${BOLD}"
|
||||||
printf "npm not found, checking for Node.js installation...\n"
|
|
||||||
if ! command_exists node; then
|
# Load nvm if available
|
||||||
printf "Node.js not found, installing Node.js via NVM...\n"
|
# shellcheck source=/dev/null
|
||||||
export NVM_DIR="$HOME/.nvm"
|
if [ -f "$HOME/.nvm/nvm.sh" ]; then
|
||||||
if [ ! -d "$NVM_DIR" ]; then
|
source "$HOME/.nvm/nvm.sh"
|
||||||
mkdir -p "$NVM_DIR"
|
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
||||||
else
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Temporarily disable nounset (-u) for nvm to avoid PROVIDED_VERSION error
|
if ! command_exists node || ! command_exists npm; then
|
||||||
set +u
|
printf "${YELLOW}Warning: Node.js/npm not found. Skipping Amp installation.${NC}\n"
|
||||||
nvm install --lts
|
printf "To install Amp via npm, please install Node.js and npm first.\n"
|
||||||
nvm use --lts
|
return 1
|
||||||
nvm alias default node
|
|
||||||
set -u
|
|
||||||
|
|
||||||
printf "Node.js installed: %s\n" "$(node --version)"
|
|
||||||
printf "npm installed: %s\n" "$(npm --version)"
|
|
||||||
else
|
|
||||||
printf "Node.js is installed but npm is not available. Please install npm manually.\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
printf "Node.js version: %s\n" "$(node --version)"
|
||||||
|
printf "npm version: %s\n" "$(npm --version)"
|
||||||
|
|
||||||
|
NPM_GLOBAL_PREFIX="${HOME}/.npm-global"
|
||||||
|
if [ ! -d "$NPM_GLOBAL_PREFIX" ]; then
|
||||||
|
mkdir -p "$NPM_GLOBAL_PREFIX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
npm config set prefix "$NPM_GLOBAL_PREFIX"
|
||||||
|
export PATH="$NPM_GLOBAL_PREFIX/bin:$PATH"
|
||||||
|
|
||||||
|
if [ -n "$ARG_AMP_VERSION" ]; then
|
||||||
|
npm install -g "@sourcegraph/amp@$ARG_AMP_VERSION"
|
||||||
|
else
|
||||||
|
npm install -g "@sourcegraph/amp"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q 'export PATH="$HOME/.npm-global/bin:$PATH"' "$HOME/.bashrc"; then
|
||||||
|
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "$HOME/.bashrc"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function install_sourcegraph_amp() {
|
install_amp_official() {
|
||||||
if [ "${ARG_INSTALL_SOURCEGRAPH_AMP}" = "true" ]; then
|
printf "%s Installing Amp using official installer\n" "${BOLD}"
|
||||||
install_node
|
|
||||||
|
|
||||||
# If nvm is not used, set up user npm global directory
|
if [ -n "$ARG_AMP_VERSION" ]; then
|
||||||
if ! command_exists nvm; then
|
export AMP_VERSION="$ARG_AMP_VERSION"
|
||||||
mkdir -p "$HOME/.npm-global"
|
printf "Installing Amp version: %s\n" "$AMP_VERSION"
|
||||||
npm config set prefix "$HOME/.npm-global"
|
|
||||||
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
||||||
if ! grep -q "export PATH=$HOME/.npm-global/bin:\$PATH" ~/.bashrc; then
|
|
||||||
echo "export PATH=$HOME/.npm-global/bin:\$PATH" >> ~/.bashrc
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "%s Installing Sourcegraph AMP CLI...\n" "${BOLD}"
|
if curl -fsSL https://ampcode.com/install.sh | bash; then
|
||||||
npm install -g @sourcegraph/amp@0.0.1754179307-gba1f97
|
export PATH="$HOME/.local/bin:$HOME/.amp/bin:$PATH"
|
||||||
printf "%s Successfully installed Sourcegraph AMP CLI. Version: %s\n" "${BOLD}" "$(amp --version)"
|
|
||||||
|
if ! grep -q 'export PATH="$HOME/.local/bin:$PATH"' "$HOME/.bashrc"; then
|
||||||
|
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$HOME/.bashrc"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "${YELLOW}Warning: Official installer failed. Installation skipped.${NC}\n"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup_system_prompt() {
|
function install_amp() {
|
||||||
if [ -n "${SOURCEGRAPH_AMP_SYSTEM_PROMPT:-}" ]; then
|
if [ "${ARG_INSTALL_AMP}" = "true" ]; then
|
||||||
echo "Setting Sourcegraph AMP system prompt..."
|
if [ "${ARG_INSTALL_VIA_NPM}" = "true" ]; then
|
||||||
mkdir -p "$HOME/.sourcegraph-amp-module"
|
install_amp_npm || {
|
||||||
echo "$SOURCEGRAPH_AMP_SYSTEM_PROMPT" > "$HOME/.sourcegraph-amp-module/SYSTEM_PROMPT.md"
|
printf "${YELLOW}Amp installation via npm failed.${NC}\n"
|
||||||
echo "System prompt saved to $HOME/.sourcegraph-amp-module/SYSTEM_PROMPT.md"
|
return 0
|
||||||
|
}
|
||||||
else
|
else
|
||||||
echo "No system prompt provided for Sourcegraph AMP."
|
install_amp_official || {
|
||||||
|
printf "${YELLOW}Amp installation via official installer failed.${NC}\n"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command_exists amp; then
|
||||||
|
printf "%s${GREEN}Successfully installed Sourcegraph Amp CLI. Version: %s${NC}\n" "${BOLD}" "$(amp --version)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "Skipping Sourcegraph Amp CLI installation (install_amp=false)\n"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup_instruction_prompt() {
|
||||||
|
if [ -n "${ARG_AMP_INSTRUCTION_PROMPT:-}" ]; then
|
||||||
|
echo "Setting AMP instruction prompt..."
|
||||||
|
mkdir -p "$HOME/.config"
|
||||||
|
echo "$ARG_AMP_INSTRUCTION_PROMPT" > "$HOME/.config/AGENTS.md"
|
||||||
|
echo "Instruction prompt saved to $HOME/.config/AGENTS.md"
|
||||||
|
else
|
||||||
|
echo "No instruction prompt provided for Sourcegraph AMP."
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +128,17 @@ function configure_amp_settings() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Writing AMP configuration to $SETTINGS_PATH"
|
echo "Writing AMP configuration to $SETTINGS_PATH"
|
||||||
printf '%s\n' "$ARG_AMP_CONFIG" > "$SETTINGS_PATH"
|
UPDATED_CONFIG=$(echo "$ARG_AMP_CONFIG" | jq --arg token "$CODER_AGENT_TOKEN" --arg url "$CODER_AGENT_URL" \
|
||||||
|
".[\"amp.mcpServers\"].coder.env += {
|
||||||
|
\"CODER_AGENT_TOKEN\": \"$CODER_AGENT_TOKEN\",
|
||||||
|
\"CODER_AGENT_URL\": \"$CODER_AGENT_URL\"
|
||||||
|
}")
|
||||||
|
printf "UPDATED_CONFIG: %s\n" "$UPDATED_CONFIG"
|
||||||
|
printf '%s\n' "$UPDATED_CONFIG" > "$SETTINGS_PATH"
|
||||||
|
|
||||||
echo "AMP configuration complete"
|
echo "AMP configuration complete"
|
||||||
}
|
}
|
||||||
|
|
||||||
install_sourcegraph_amp
|
install_amp
|
||||||
setup_system_prompt
|
setup_instruction_prompt
|
||||||
configure_amp_settings
|
configure_amp_settings
|
||||||
|
|||||||
@ -6,11 +6,11 @@ set -euo pipefail
|
|||||||
source "$HOME/.bashrc"
|
source "$HOME/.bashrc"
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
if [ -f "$HOME/.nvm/nvm.sh" ]; then
|
if [ -f "$HOME/.nvm/nvm.sh" ]; then
|
||||||
source "$HOME"/.nvm/nvm.sh
|
source "$HOME/.nvm/nvm.sh"
|
||||||
else
|
|
||||||
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
export PATH="$HOME/.local/bin:$HOME/.amp/bin:$HOME/.npm-global/bin:$PATH"
|
||||||
|
|
||||||
function ensure_command() {
|
function ensure_command() {
|
||||||
command -v "$1" &> /dev/null || {
|
command -v "$1" &> /dev/null || {
|
||||||
echo "Error: '$1' not found." >&2
|
echo "Error: '$1' not found." >&2
|
||||||
@ -18,10 +18,21 @@ function ensure_command() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARG_AMP_START_DIRECTORY=${ARG_AMP_START_DIRECTORY:-"$HOME"}
|
||||||
|
ARG_AMP_API_KEY=${ARG_AMP_API_KEY:-}
|
||||||
|
ARG_AMP_TASK_PROMPT=$(echo -n "${ARG_AMP_TASK_PROMPT:-}" | base64 -d)
|
||||||
|
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
||||||
|
|
||||||
|
echo "--------------------------------"
|
||||||
|
printf "Workspace: %s\n" "$ARG_AMP_START_DIRECTORY"
|
||||||
|
printf "Task Prompt: %s\n" "$ARG_AMP_TASK_PROMPT"
|
||||||
|
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
|
||||||
|
echo "--------------------------------"
|
||||||
|
|
||||||
ensure_command amp
|
ensure_command amp
|
||||||
echo "AMP version: $(amp --version)"
|
echo "AMP version: $(amp --version)"
|
||||||
|
|
||||||
dir="$SOURCEGRAPH_AMP_START_DIRECTORY"
|
dir="$ARG_AMP_START_DIRECTORY"
|
||||||
if [[ -d "$dir" ]]; then
|
if [[ -d "$dir" ]]; then
|
||||||
echo "Using existing directory: $dir"
|
echo "Using existing directory: $dir"
|
||||||
else
|
else
|
||||||
@ -30,20 +41,23 @@ else
|
|||||||
fi
|
fi
|
||||||
cd "$dir"
|
cd "$dir"
|
||||||
|
|
||||||
if [ -n "$SOURCEGRAPH_AMP_API_KEY" ]; then
|
if [ -n "$ARG_AMP_API_KEY" ]; then
|
||||||
printf "sourcegraph_amp_api_key provided !\n"
|
printf "amp_api_key provided !\n"
|
||||||
export AMP_API_KEY=$SOURCEGRAPH_AMP_API_KEY
|
export AMP_API_KEY=$ARG_AMP_API_KEY
|
||||||
else
|
else
|
||||||
printf "sourcegraph_amp_api_key not provided\n"
|
printf "amp_api_key not provided\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${SOURCEGRAPH_AMP_TASK_PROMPT:-}" ]; then
|
if [ -n "$ARG_AMP_TASK_PROMPT" ]; then
|
||||||
printf "sourcegraph amp task prompt provided : $SOURCEGRAPH_AMP_TASK_PROMPT"
|
if [ "$ARG_REPORT_TASKS" == "true" ]; then
|
||||||
PROMPT="Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $SOURCEGRAPH_AMP_TASK_PROMPT"
|
printf "amp task prompt provided : %s" "$ARG_AMP_TASK_PROMPT\n"
|
||||||
|
PROMPT="Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_AMP_TASK_PROMPT"
|
||||||
|
else
|
||||||
|
PROMPT="$ARG_AMP_TASK_PROMPT"
|
||||||
|
fi
|
||||||
# Pipe the prompt into amp, which will be run inside agentapi
|
# Pipe the prompt into amp, which will be run inside agentapi
|
||||||
agentapi server --term-width=67 --term-height=1190 -- bash -c "echo \"$PROMPT\" | amp"
|
agentapi server --type amp --term-width=67 --term-height=1190 -- bash -c "echo \"$PROMPT\" | amp"
|
||||||
else
|
else
|
||||||
printf "No task prompt given.\n"
|
printf "No task prompt given.\n"
|
||||||
agentapi server --term-width=67 --term-height=1190 -- amp
|
agentapi server --type amp --term-width=67 --term-height=1190 -- amp
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Find all directories that contain any .tftest.hcl files and run terraform test in each
|
# Auto-detect which Terraform tests to run based on changed files from paths-filter
|
||||||
|
# Uses paths-filter outputs from GitHub Actions:
|
||||||
|
# ALL_CHANGED_FILES - all files changed in the PR (for logging)
|
||||||
|
# SHARED_CHANGED - boolean indicating if shared infrastructure changed
|
||||||
|
# MODULE_CHANGED_FILES - only files in registry/**/modules/** (for processing)
|
||||||
|
# Runs all tests if shared infrastructure changes, or skips if no changes detected
|
||||||
|
#
|
||||||
|
# This script only runs tests for changed modules. Documentation and template changes are ignored.
|
||||||
|
|
||||||
run_dir() {
|
run_dir() {
|
||||||
local dir="$1"
|
local dir="$1"
|
||||||
@ -9,13 +16,72 @@ run_dir() {
|
|||||||
(cd "$dir" && terraform init -upgrade -input=false -no-color > /dev/null && terraform test -no-color -verbose)
|
(cd "$dir" && terraform init -upgrade -input=false -no-color > /dev/null && terraform test -no-color -verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapfile -t test_dirs < <(find . -type f -name "*.tftest.hcl" -print0 | xargs -0 -I{} dirname {} | sort -u)
|
echo "==> Detecting changed files..."
|
||||||
|
|
||||||
|
if [[ -n "${ALL_CHANGED_FILES:-}" ]]; then
|
||||||
|
echo "Changed files in PR:"
|
||||||
|
echo "$ALL_CHANGED_FILES" | tr ' ' '\n' | sed 's/^/ - /'
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${SHARED_CHANGED:-false}" == "true" ]]; then
|
||||||
|
echo "==> Shared infrastructure changed"
|
||||||
|
echo "==> Running all tests for safety"
|
||||||
|
mapfile -t test_dirs < <(find . -type f -name "*.tftest.hcl" -print0 | xargs -0 -I{} dirname {} | sort -u)
|
||||||
|
elif [[ -z "${MODULE_CHANGED_FILES:-}" ]]; then
|
||||||
|
echo "✓ No module files changed, skipping tests"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
CHANGED_FILES=$(echo "$MODULE_CHANGED_FILES" | tr ' ' '\n')
|
||||||
|
|
||||||
|
MODULE_DIRS=()
|
||||||
|
while IFS= read -r file; do
|
||||||
|
if [[ "$file" =~ \.(md|png|jpg|jpeg|svg)$ ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$file" =~ ^registry/([^/]+)/modules/([^/]+)/ ]]; then
|
||||||
|
namespace="${BASH_REMATCH[1]}"
|
||||||
|
module="${BASH_REMATCH[2]}"
|
||||||
|
module_dir="registry/${namespace}/modules/${module}"
|
||||||
|
|
||||||
|
if [[ -d "$module_dir" ]] && [[ ! " ${MODULE_DIRS[*]} " =~ " ${module_dir} " ]]; then
|
||||||
|
MODULE_DIRS+=("$module_dir")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$CHANGED_FILES"
|
||||||
|
|
||||||
|
if [[ ${#MODULE_DIRS[@]} -eq 0 ]]; then
|
||||||
|
echo "✓ No Terraform tests to run"
|
||||||
|
echo " (documentation, templates, namespace files, or modules without changes)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Finding .tftest.hcl files in ${#MODULE_DIRS[@]} changed module(s):"
|
||||||
|
for dir in "${MODULE_DIRS[@]}"; do
|
||||||
|
echo " - $dir"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
test_dirs=()
|
||||||
|
for module_dir in "${MODULE_DIRS[@]}"; do
|
||||||
|
while IFS= read -r test_file; do
|
||||||
|
test_dir=$(dirname "$test_file")
|
||||||
|
if [[ ! " ${test_dirs[*]} " =~ " ${test_dir} " ]]; then
|
||||||
|
test_dirs+=("$test_dir")
|
||||||
|
fi
|
||||||
|
done < <(find "$module_dir" -type f -name "*.tftest.hcl")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ${#test_dirs[@]} -eq 0 ]]; then
|
if [[ ${#test_dirs[@]} -eq 0 ]]; then
|
||||||
echo "No .tftest.hcl tests found."
|
echo "✓ No .tftest.hcl tests found in changed modules"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "==> Running terraform test in ${#test_dirs[@]} directory(ies)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
status=0
|
status=0
|
||||||
for d in "${test_dirs[@]}"; do
|
for d in "${test_dirs[@]}"; do
|
||||||
if ! run_dir "$d"; then
|
if ! run_dir "$d"; then
|
||||||
|
|||||||
@ -2,36 +2,90 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Auto-detect which Terraform modules to validate based on changed files from paths-filter
|
||||||
|
# Uses paths-filter outputs from GitHub Actions:
|
||||||
|
# ALL_CHANGED_FILES - all files changed in the PR (for logging)
|
||||||
|
# SHARED_CHANGED - boolean indicating if shared infrastructure changed
|
||||||
|
# MODULE_CHANGED_FILES - only files in registry/**/modules/** (for processing)
|
||||||
|
# Validates all modules if shared infrastructure changes, or skips if no changes detected
|
||||||
|
#
|
||||||
|
# This script only validates changed modules. Documentation and template changes are ignored.
|
||||||
|
|
||||||
validate_terraform_directory() {
|
validate_terraform_directory() {
|
||||||
local dir="$1"
|
local dir="$1"
|
||||||
echo "Running \`terraform validate\` in $dir"
|
echo "Running \`terraform validate\` in $dir"
|
||||||
pushd "$dir"
|
pushd "$dir" > /dev/null
|
||||||
terraform init -upgrade
|
terraform init -upgrade
|
||||||
terraform validate
|
terraform validate
|
||||||
popd
|
popd > /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
# Get the directory of the script
|
echo "==> Detecting changed files..."
|
||||||
|
|
||||||
|
if [[ -n "${ALL_CHANGED_FILES:-}" ]]; then
|
||||||
|
echo "Changed files in PR:"
|
||||||
|
echo "$ALL_CHANGED_FILES" | tr ' ' '\n' | sed 's/^/ - /'
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
local script_dir=$(dirname "$(readlink -f "$0")")
|
local script_dir=$(dirname "$(readlink -f "$0")")
|
||||||
|
local registry_dir=$(readlink -f "$script_dir/../registry")
|
||||||
|
|
||||||
# Code assumes that registry directory will always be in same position
|
if [[ "${SHARED_CHANGED:-false}" == "true" ]]; then
|
||||||
# relative to the main script directory
|
echo "==> Shared infrastructure changed"
|
||||||
local registry_dir="$script_dir/../registry"
|
echo "==> Validating all modules for safety"
|
||||||
|
local subdirs=$(find "$registry_dir" -mindepth 3 -maxdepth 3 -path "*/modules/*" -type d | sort)
|
||||||
|
elif [[ -z "${MODULE_CHANGED_FILES:-}" ]]; then
|
||||||
|
echo "✓ No module files changed, skipping validation"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
CHANGED_FILES=$(echo "$MODULE_CHANGED_FILES" | tr ' ' '\n')
|
||||||
|
|
||||||
# Get all module subdirectories in the registry directory. Code assumes that
|
MODULE_DIRS=()
|
||||||
# Terraform module directories won't begin to appear until three levels deep into
|
while IFS= read -r file; do
|
||||||
# the registry (e.g., registry/coder/modules/coder-login, which will then
|
if [[ "$file" =~ \.(md|png|jpg|jpeg|svg)$ ]]; then
|
||||||
# have a main.tf file inside it)
|
continue
|
||||||
local subdirs=$(find "$registry_dir" -mindepth 3 -path "*/modules/*" -type d | sort)
|
fi
|
||||||
|
|
||||||
|
if [[ "$file" =~ ^registry/([^/]+)/modules/([^/]+)/ ]]; then
|
||||||
|
namespace="${BASH_REMATCH[1]}"
|
||||||
|
module="${BASH_REMATCH[2]}"
|
||||||
|
module_dir="registry/${namespace}/modules/${module}"
|
||||||
|
|
||||||
|
if [[ -d "$module_dir" ]] && [[ ! " ${MODULE_DIRS[*]} " =~ " ${module_dir} " ]]; then
|
||||||
|
MODULE_DIRS+=("$module_dir")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "$CHANGED_FILES"
|
||||||
|
|
||||||
|
if [[ ${#MODULE_DIRS[@]} -eq 0 ]]; then
|
||||||
|
echo "✓ No modules to validate"
|
||||||
|
echo " (documentation, templates, namespace files, or modules without changes)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Validating ${#MODULE_DIRS[@]} changed module(s):"
|
||||||
|
for dir in "${MODULE_DIRS[@]}"; do
|
||||||
|
echo " - $dir"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local subdirs="${MODULE_DIRS[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
status=0
|
||||||
for dir in $subdirs; do
|
for dir in $subdirs; do
|
||||||
# Skip over any directories that obviously don't have the necessary
|
# Skip over any directories that obviously don't have the necessary
|
||||||
# files
|
# files
|
||||||
if test -f "$dir/main.tf"; then
|
if test -f "$dir/main.tf"; then
|
||||||
validate_terraform_directory "$dir"
|
if ! validate_terraform_directory "$dir"; then
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
exit $status
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user