refactor(agentapi): use coder-utils and tftpl for install script
Replace the inline coder_script resource with coder-utils module and convert scripts/main.sh to scripts/install.sh.tftpl, matching the pattern used by coder/claude-code and coder-labs/codex. Changes: - Replace coder_script.agentapi with module "coder_utils" (v0.0.1) - Convert scripts/main.sh to scripts/install.sh.tftpl using templatefile() for variable injection instead of ARG_* env vars - Keep coder_script.agentapi_shutdown as-is (coder-utils does not support run_on_stop) - Keep coder_app resources (web and CLI) unchanged - Add output "scripts" for downstream sync dependencies - Simplify agentapi.tftest.hcl assertions (install script content is now base64-encoded inside the coder-utils wrapper)
This commit is contained in:
parent
fafe8cca7b
commit
b8ae549102
@ -27,22 +27,6 @@ run "default_values" {
|
|||||||
error_message = "pid_file_path should default to empty string"
|
error_message = "pid_file_path should default to empty string"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Verify start script contains state persistence ARG_ vars.
|
|
||||||
assert {
|
|
||||||
condition = can(regex("ARG_ENABLE_STATE_PERSISTENCE", coder_script.agentapi.script))
|
|
||||||
error_message = "start script should contain ARG_ENABLE_STATE_PERSISTENCE"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {
|
|
||||||
condition = can(regex("ARG_STATE_FILE_PATH", coder_script.agentapi.script))
|
|
||||||
error_message = "start script should contain ARG_STATE_FILE_PATH"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {
|
|
||||||
condition = can(regex("ARG_PID_FILE_PATH", coder_script.agentapi.script))
|
|
||||||
error_message = "start script should contain ARG_PID_FILE_PATH"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Verify shutdown script contains PID-related ARG_ vars.
|
# Verify shutdown script contains PID-related ARG_ vars.
|
||||||
assert {
|
assert {
|
||||||
condition = can(regex("ARG_PID_FILE_PATH", coder_script.agentapi_shutdown.script))
|
condition = can(regex("ARG_PID_FILE_PATH", coder_script.agentapi_shutdown.script))
|
||||||
@ -67,11 +51,10 @@ run "state_persistence_disabled" {
|
|||||||
error_message = "enable_state_persistence should be false"
|
error_message = "enable_state_persistence should be false"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Even when disabled, the ARG_ vars should still be in the script
|
# Verify shutdown script contains the disabled flag.
|
||||||
# (the shell script handles the conditional logic).
|
|
||||||
assert {
|
assert {
|
||||||
condition = can(regex("ARG_ENABLE_STATE_PERSISTENCE='false'", coder_script.agentapi.script))
|
condition = can(regex("ARG_ENABLE_STATE_PERSISTENCE='false'", coder_script.agentapi_shutdown.script))
|
||||||
error_message = "start script should contain ARG_ENABLE_STATE_PERSISTENCE='false'"
|
error_message = "shutdown script should contain ARG_ENABLE_STATE_PERSISTENCE='false'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,19 +66,18 @@ run "custom_paths" {
|
|||||||
pid_file_path = "/custom/agentapi.pid"
|
pid_file_path = "/custom/agentapi.pid"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {
|
# Verify custom paths appear in shutdown script.
|
||||||
condition = can(regex("/custom/state.json", coder_script.agentapi.script))
|
|
||||||
error_message = "start script should contain custom state_file_path"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert {
|
|
||||||
condition = can(regex("/custom/agentapi.pid", coder_script.agentapi.script))
|
|
||||||
error_message = "start script should contain custom pid_file_path"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Verify custom paths also appear in shutdown script.
|
|
||||||
assert {
|
assert {
|
||||||
condition = can(regex("/custom/agentapi.pid", coder_script.agentapi_shutdown.script))
|
condition = can(regex("/custom/agentapi.pid", coder_script.agentapi_shutdown.script))
|
||||||
error_message = "shutdown script should contain custom pid_file_path"
|
error_message = "shutdown script should contain custom pid_file_path"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run "scripts_output" {
|
||||||
|
command = plan
|
||||||
|
|
||||||
|
assert {
|
||||||
|
condition = length(output.scripts) == 1 && output.scripts[0] == "coder-agentapi-install_script"
|
||||||
|
error_message = "scripts output should list the install script sync name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -173,8 +173,7 @@ locals {
|
|||||||
web_app = var.web_app || local.is_task
|
web_app = var.web_app || local.is_task
|
||||||
|
|
||||||
# we always trim the slash for consistency
|
# we always trim the slash for consistency
|
||||||
workdir = trimsuffix(var.folder, "/")
|
workdir = trimsuffix(var.folder, "/")
|
||||||
agentapi_wait_for_start_script_b64 = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh"))
|
|
||||||
// Chat base path is only set if not using a subdomain.
|
// Chat base path is only set if not using a subdomain.
|
||||||
// NOTE:
|
// NOTE:
|
||||||
// - Initial support for --chat-base-path was added in v0.3.1 but configuration
|
// - Initial support for --chat-base-path was added in v0.3.1 but configuration
|
||||||
@ -182,45 +181,38 @@ locals {
|
|||||||
// - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID
|
// - As CODER_WORKSPACE_AGENT_NAME is a recent addition we use agent ID
|
||||||
// for backward compatibility.
|
// for backward compatibility.
|
||||||
agentapi_chat_base_path = var.agentapi_subdomain ? "" : "/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}.${var.agent_id}/apps/${var.web_app_slug}/chat"
|
agentapi_chat_base_path = var.agentapi_subdomain ? "" : "/@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}.${var.agent_id}/apps/${var.web_app_slug}/chat"
|
||||||
main_script = file("${path.module}/scripts/main.sh")
|
|
||||||
shutdown_script = file("${path.module}/scripts/agentapi-shutdown.sh")
|
shutdown_script = file("${path.module}/scripts/agentapi-shutdown.sh")
|
||||||
lib_script = file("${path.module}/scripts/lib.sh")
|
lib_script = file("${path.module}/scripts/lib.sh")
|
||||||
|
|
||||||
main_script_destination = "${var.module_directory}/main.sh"
|
|
||||||
lib_script_destination = "${var.module_directory}/agentapi-lib.sh"
|
|
||||||
shutdown_script_destination = "${var.module_directory}/agentapi-shutdown.sh"
|
shutdown_script_destination = "${var.module_directory}/agentapi-shutdown.sh"
|
||||||
|
lib_script_destination = "${var.module_directory}/agentapi-lib.sh"
|
||||||
|
|
||||||
|
install_script = templatefile("${path.module}/scripts/install.sh.tftpl", {
|
||||||
|
ARG_MODULE_DIRECTORY = var.module_directory
|
||||||
|
ARG_WORKDIR = local.workdir
|
||||||
|
ARG_INSTALL_AGENTAPI = tostring(var.install_agentapi)
|
||||||
|
ARG_AGENTAPI_VERSION = var.agentapi_version
|
||||||
|
ARG_WAIT_FOR_START_SCRIPT = base64encode(file("${path.module}/scripts/agentapi-wait-for-start.sh"))
|
||||||
|
ARG_AGENTAPI_PORT = tostring(var.agentapi_port)
|
||||||
|
ARG_AGENTAPI_CHAT_BASE_PATH = local.agentapi_chat_base_path
|
||||||
|
ARG_TASK_ID = try(data.coder_task.me.id, "")
|
||||||
|
ARG_TASK_LOG_SNAPSHOT = tostring(var.task_log_snapshot)
|
||||||
|
ARG_ENABLE_STATE_PERSISTENCE = tostring(var.enable_state_persistence)
|
||||||
|
ARG_STATE_FILE_PATH = var.state_file_path
|
||||||
|
ARG_PID_FILE_PATH = var.pid_file_path
|
||||||
|
ARG_LIB_SCRIPT = base64encode(local.lib_script)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_script" "agentapi" {
|
module "coder_utils" {
|
||||||
agent_id = var.agent_id
|
source = "registry.coder.com/coder/coder-utils/coder"
|
||||||
display_name = "Install and start AgentAPI"
|
version = "0.0.1"
|
||||||
icon = var.web_app_icon
|
|
||||||
script = <<-EOT
|
|
||||||
#!/bin/bash
|
|
||||||
set -o errexit
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
mkdir -p "${var.module_directory}"
|
agent_id = var.agent_id
|
||||||
echo -n '${base64encode(local.main_script)}' | base64 -d > "${local.main_script_destination}"
|
module_directory = var.module_directory
|
||||||
chmod +x "${local.main_script_destination}"
|
display_name_prefix = "AgentAPI"
|
||||||
echo -n '${base64encode(local.lib_script)}' | base64 -d > "${local.lib_script_destination}"
|
icon = var.web_app_icon
|
||||||
|
install_script = local.install_script
|
||||||
ARG_MODULE_DIRECTORY='${var.module_directory}' \
|
|
||||||
ARG_WORKDIR="$(echo -n '${base64encode(local.workdir)}' | base64 -d)" \
|
|
||||||
ARG_INSTALL_AGENTAPI='${var.install_agentapi}' \
|
|
||||||
ARG_AGENTAPI_VERSION='${var.agentapi_version}' \
|
|
||||||
ARG_WAIT_FOR_START_SCRIPT="$(echo -n '${local.agentapi_wait_for_start_script_b64}' | base64 -d)" \
|
|
||||||
ARG_AGENTAPI_PORT='${var.agentapi_port}' \
|
|
||||||
ARG_AGENTAPI_CHAT_BASE_PATH='${local.agentapi_chat_base_path}' \
|
|
||||||
ARG_TASK_ID='${try(data.coder_task.me.id, "")}' \
|
|
||||||
ARG_TASK_LOG_SNAPSHOT='${var.task_log_snapshot}' \
|
|
||||||
ARG_ENABLE_STATE_PERSISTENCE='${var.enable_state_persistence}' \
|
|
||||||
ARG_STATE_FILE_PATH='${var.state_file_path}' \
|
|
||||||
ARG_PID_FILE_PATH='${var.pid_file_path}' \
|
|
||||||
ARG_LIB_SCRIPT_PATH="${local.lib_script_destination}" \
|
|
||||||
"${local.main_script_destination}"
|
|
||||||
EOT
|
|
||||||
run_on_start = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_script" "agentapi_shutdown" {
|
resource "coder_script" "agentapi_shutdown" {
|
||||||
@ -289,3 +281,8 @@ resource "coder_app" "agentapi_cli" {
|
|||||||
output "task_app_id" {
|
output "task_app_id" {
|
||||||
value = local.web_app ? coder_app.agentapi_web[0].id : ""
|
value = local.web_app ? coder_app.agentapi_web[0].id : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "scripts" {
|
||||||
|
description = "Ordered list of coder exp sync names for the coder_script resources this module creates, in run order. Scripts that were not configured are absent from the list."
|
||||||
|
value = module.coder_utils.scripts
|
||||||
|
}
|
||||||
|
|||||||
@ -3,29 +3,34 @@ set -e
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
set -o nounset
|
set -o nounset
|
||||||
MODULE_DIRECTORY="$ARG_MODULE_DIRECTORY"
|
|
||||||
WORKDIR="$ARG_WORKDIR"
|
MODULE_DIRECTORY='${ARG_MODULE_DIRECTORY}'
|
||||||
INSTALL_AGENTAPI="$ARG_INSTALL_AGENTAPI"
|
WORKDIR='${ARG_WORKDIR}'
|
||||||
AGENTAPI_VERSION="$ARG_AGENTAPI_VERSION"
|
INSTALL_AGENTAPI='${ARG_INSTALL_AGENTAPI}'
|
||||||
WAIT_FOR_START_SCRIPT="$ARG_WAIT_FOR_START_SCRIPT"
|
AGENTAPI_VERSION='${ARG_AGENTAPI_VERSION}'
|
||||||
AGENTAPI_PORT="$ARG_AGENTAPI_PORT"
|
WAIT_FOR_START_SCRIPT=$(echo -n '${ARG_WAIT_FOR_START_SCRIPT}' | base64 -d)
|
||||||
AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
|
AGENTAPI_PORT='${ARG_AGENTAPI_PORT}'
|
||||||
TASK_ID="${ARG_TASK_ID:-}"
|
AGENTAPI_CHAT_BASE_PATH='${ARG_AGENTAPI_CHAT_BASE_PATH}'
|
||||||
TASK_LOG_SNAPSHOT="${ARG_TASK_LOG_SNAPSHOT:-true}"
|
TASK_ID='${ARG_TASK_ID}'
|
||||||
ENABLE_STATE_PERSISTENCE="${ARG_ENABLE_STATE_PERSISTENCE:-false}"
|
TASK_LOG_SNAPSHOT='${ARG_TASK_LOG_SNAPSHOT}'
|
||||||
STATE_FILE_PATH="${ARG_STATE_FILE_PATH:-}"
|
ENABLE_STATE_PERSISTENCE='${ARG_ENABLE_STATE_PERSISTENCE}'
|
||||||
PID_FILE_PATH="${ARG_PID_FILE_PATH:-}"
|
STATE_FILE_PATH='${ARG_STATE_FILE_PATH}'
|
||||||
LIB_SCRIPT_PATH="$ARG_LIB_SCRIPT_PATH"
|
PID_FILE_PATH='${ARG_PID_FILE_PATH}'
|
||||||
|
LIB_SCRIPT=$(echo -n '${ARG_LIB_SCRIPT}' | base64 -d)
|
||||||
|
|
||||||
set +o nounset
|
set +o nounset
|
||||||
|
|
||||||
|
# Write and source lib.sh
|
||||||
|
LIB_SCRIPT_PATH="$${MODULE_DIRECTORY}/agentapi-lib.sh"
|
||||||
|
echo -n "$LIB_SCRIPT" > "$LIB_SCRIPT_PATH"
|
||||||
# shellcheck source=lib.sh
|
# shellcheck source=lib.sh
|
||||||
source "${LIB_SCRIPT_PATH}"
|
source "$LIB_SCRIPT_PATH"
|
||||||
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" > /dev/null 2>&1
|
command -v "$1" > /dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
mkdir -p "${MODULE_DIRECTORY}/scripts"
|
mkdir -p "$${MODULE_DIRECTORY}/scripts"
|
||||||
|
|
||||||
# Check for jq dependency if task log snapshot is enabled.
|
# Check for jq dependency if task log snapshot is enabled.
|
||||||
if [[ $TASK_LOG_SNAPSHOT == true ]] && [[ -n $TASK_ID ]]; then
|
if [[ $TASK_LOG_SNAPSHOT == true ]] && [[ -n $TASK_ID ]]; then
|
||||||
@ -34,14 +39,14 @@ if [[ $TASK_LOG_SNAPSHOT == true ]] && [[ -n $TASK_ID ]]; then
|
|||||||
echo "Install jq to enable log snapshot functionality when the workspace stops."
|
echo "Install jq to enable log snapshot functionality when the workspace stops."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ ! -d "${WORKDIR}" ]; then
|
if [ ! -d "$${WORKDIR}" ]; then
|
||||||
echo "Warning: The specified folder '${WORKDIR}' does not exist."
|
echo "Warning: The specified folder '$${WORKDIR}' does not exist."
|
||||||
echo "Creating the folder..."
|
echo "Creating the folder..."
|
||||||
mkdir -p "${WORKDIR}"
|
mkdir -p "$${WORKDIR}"
|
||||||
echo "Folder created successfully."
|
echo "Folder created successfully."
|
||||||
fi
|
fi
|
||||||
# Install AgentAPI if enabled
|
# Install AgentAPI if enabled
|
||||||
if [ "${INSTALL_AGENTAPI}" = "true" ]; then
|
if [ "$${INSTALL_AGENTAPI}" = "true" ]; then
|
||||||
echo "Installing AgentAPI..."
|
echo "Installing AgentAPI..."
|
||||||
arch=$(uname -m)
|
arch=$(uname -m)
|
||||||
if [ "$arch" = "x86_64" ]; then
|
if [ "$arch" = "x86_64" ]; then
|
||||||
@ -52,12 +57,12 @@ if [ "${INSTALL_AGENTAPI}" = "true" ]; then
|
|||||||
echo "Error: Unsupported architecture: $arch"
|
echo "Error: Unsupported architecture: $arch"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "${AGENTAPI_VERSION}" = "latest" ]; then
|
if [ "$${AGENTAPI_VERSION}" = "latest" ]; then
|
||||||
# for the latest release the download URL pattern is different than for tagged releases
|
# for the latest release the download URL pattern is different than for tagged releases
|
||||||
# https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases
|
# https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases
|
||||||
download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name"
|
download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name"
|
||||||
else
|
else
|
||||||
download_url="https://github.com/coder/agentapi/releases/download/${AGENTAPI_VERSION}/$binary_name"
|
download_url="https://github.com/coder/agentapi/releases/download/$${AGENTAPI_VERSION}/$binary_name"
|
||||||
fi
|
fi
|
||||||
curl \
|
curl \
|
||||||
--retry 5 \
|
--retry 5 \
|
||||||
@ -76,30 +81,30 @@ if ! command_exists agentapi; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -n "${WAIT_FOR_START_SCRIPT}" > "${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh"
|
echo -n "$${WAIT_FOR_START_SCRIPT}" > "$${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh"
|
||||||
chmod +x "${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh"
|
chmod +x "$${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh"
|
||||||
|
|
||||||
export LANG=en_US.UTF-8
|
export LANG=en_US.UTF-8
|
||||||
export LC_ALL=en_US.UTF-8
|
export LC_ALL=en_US.UTF-8
|
||||||
|
|
||||||
cd "${WORKDIR}"
|
cd "$${WORKDIR}"
|
||||||
|
|
||||||
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
|
export AGENTAPI_CHAT_BASE_PATH="$${AGENTAPI_CHAT_BASE_PATH:-}"
|
||||||
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
|
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
|
||||||
export AGENTAPI_ALLOWED_HOSTS="*"
|
export AGENTAPI_ALLOWED_HOSTS="*"
|
||||||
|
|
||||||
export AGENTAPI_PID_FILE="${PID_FILE_PATH:-${MODULE_DIRECTORY}/agentapi.pid}"
|
export AGENTAPI_PID_FILE="$${PID_FILE_PATH:-$${MODULE_DIRECTORY}/agentapi.pid}"
|
||||||
# Only set state env vars when persistence is enabled and the binary supports
|
# Only set state env vars when persistence is enabled and the binary supports
|
||||||
# it. State persistence requires agentapi >= v0.12.0.
|
# it. State persistence requires agentapi >= v0.12.0.
|
||||||
if [ "${ENABLE_STATE_PERSISTENCE}" = "true" ]; then
|
if [ "$${ENABLE_STATE_PERSISTENCE}" = "true" ]; then
|
||||||
actual_version=$(agentapi_version)
|
actual_version=$(agentapi_version)
|
||||||
if version_at_least 0.12.0 "$actual_version"; then
|
if version_at_least 0.12.0 "$actual_version"; then
|
||||||
export AGENTAPI_STATE_FILE="${STATE_FILE_PATH:-${MODULE_DIRECTORY}/agentapi-state.json}"
|
export AGENTAPI_STATE_FILE="$${STATE_FILE_PATH:-$${MODULE_DIRECTORY}/agentapi-state.json}"
|
||||||
export AGENTAPI_SAVE_STATE="true"
|
export AGENTAPI_SAVE_STATE="true"
|
||||||
export AGENTAPI_LOAD_STATE="true"
|
export AGENTAPI_LOAD_STATE="true"
|
||||||
else
|
else
|
||||||
echo "Warning: State persistence requires agentapi >= v0.12.0 (current: ${actual_version:-unknown}), skipping."
|
echo "Warning: State persistence requires agentapi >= v0.12.0 (current: $${actual_version:-unknown}), skipping."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
nohup "${MODULE_DIRECTORY}/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &> "${MODULE_DIRECTORY}/agentapi-start.log" &
|
nohup "$${MODULE_DIRECTORY}/scripts/agentapi-start.sh" true "$${AGENTAPI_PORT}" &> "$${MODULE_DIRECTORY}/agentapi-start.log" &
|
||||||
"${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}"
|
"$${MODULE_DIRECTORY}/scripts/agentapi-wait-for-start.sh" "$${AGENTAPI_PORT}"
|
||||||
Loading…
x
Reference in New Issue
Block a user