Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,60 @@ jobs:
--cache-to type=gha,mode=max \
.

- name: Prepare SSH target on runner
run: |
set -euxo pipefail
APX_SSH_USER="apxci"
KEY_PATH="${RUNNER_TEMP}/apx_ci_key"
KNOWN_HOSTS_PATH="${RUNNER_TEMP}/apx_ci_known_hosts"

ssh-keygen -t ed25519 -N "" -f "${KEY_PATH}" -C "apx-ci@github-actions"
chmod 600 "${KEY_PATH}"

if ! id -u "${APX_SSH_USER}" >/dev/null 2>&1; then
sudo useradd -m -s /bin/bash "${APX_SSH_USER}"
fi
sudo mkdir -p "/home/${APX_SSH_USER}/.ssh"
sudo chmod 700 "/home/${APX_SSH_USER}/.ssh"
sudo touch "/home/${APX_SSH_USER}/.ssh/authorized_keys"
sudo chmod 600 "/home/${APX_SSH_USER}/.ssh/authorized_keys"
sudo sh -lc "cat '${KEY_PATH}.pub' >> '/home/${APX_SSH_USER}/.ssh/authorized_keys'"
sudo chown -R "${APX_SSH_USER}:${APX_SSH_USER}" "/home/${APX_SSH_USER}/.ssh"
echo "${APX_SSH_USER} ALL=(ALL) NOPASSWD:ALL" | sudo tee "/etc/sudoers.d/90-${APX_SSH_USER}-nopasswd"
sudo chmod 440 "/etc/sudoers.d/90-${APX_SSH_USER}-nopasswd"
sudo visudo -cf "/etc/sudoers.d/90-${APX_SSH_USER}-nopasswd"

sudo apt-get update
sudo apt-get install -y openssh-server
sudo mkdir -p /run/sshd
sudo sed -i 's/^#\?PubkeyAuthentication .*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?KbdInteractiveAuthentication .*/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config
if grep -q '^AllowUsers ' /etc/ssh/sshd_config; then
sudo sed -i "s/^AllowUsers .*/AllowUsers ${APX_SSH_USER}/" /etc/ssh/sshd_config
else
echo "AllowUsers ${APX_SSH_USER}" | sudo tee -a /etc/ssh/sshd_config
fi
sudo systemctl restart ssh || sudo service ssh restart || sudo /usr/sbin/sshd

ssh-keyscan -H 127.0.0.1 > "${KNOWN_HOSTS_PATH}" || true
ssh-keyscan -H 172.17.0.1 >> "${KNOWN_HOSTS_PATH}" || true
if [ ! -s "${KNOWN_HOSTS_PATH}" ]; then
touch "${KNOWN_HOSTS_PATH}"
fi
chmod 644 "${KNOWN_HOSTS_PATH}"

ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile="${KNOWN_HOSTS_PATH}" -i "${KEY_PATH}" "${APX_SSH_USER}@127.0.0.1" "sudo -n true && echo ssh_ok"

Comment on lines +82 to +92
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ssh-keyscan is allowed to fail (|| true) and there is no retry/backoff after restarting sshd. If sshd is not ready yet, the known_hosts file can end up empty and the subsequent ssh -o StrictHostKeyChecking=yes ... will fail, causing flaky CI. Add a small retry loop for ssh-keyscan (and/or wait for port 22) and fail early if the file is still empty after retries.

Copilot uses AI. Check for mistakes.
{
echo "APX_TEST_SSH_KEY_PATH=${KEY_PATH}"
echo "APX_TEST_KNOWN_HOSTS_PATH=${KNOWN_HOSTS_PATH}"
echo "APX_TEST_REMOTE_USER=${APX_SSH_USER}"
echo "APX_TEST_REMOTE_IP=172.17.0.1"
Comment thread
JoeStech marked this conversation as resolved.
} >> "${GITHUB_ENV}"

- name: Run integration tests
env:
APX_DEBUG_TRACE: "1"
MCP_IMAGE: arm-mcp:latest
run: pytest -s mcp-local/tests/test_mcp.py --platform ${{ matrix.platform }}
7 changes: 5 additions & 2 deletions mcp-local/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential libelf-dev \
ca-certificates file tar xz-utils jq libmagic1 && \
rm -rf /var/lib/apt/lists/*
RUN curl -sSL https://raw.githubusercontent.com/JoeStech/arm-linux-migration-tools/main/scripts/install.sh | bash
RUN bash -o pipefail -c 'set -eux; \
curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors \
https://raw.githubusercontent.com/JoeStech/arm-linux-migration-tools/main/scripts/install.sh | bash'
Comment on lines +41 to +43
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build still downloads and executes a remote install script via curl ... | bash from a moving branch (main). Even with -fsSL and retries, this is a supply-chain risk and can break builds when upstream changes. Prefer pinning to a specific commit/tag (or vendoring the script) and verifying integrity (e.g., checksum/signature) before execution.

Copilot uses AI. Check for mistakes.
# Temp until migrate-ease is updated
RUN curl -sSL https://raw.githubusercontent.com/JoeStech/migrate-ease/main/js/advisor/main.py \
RUN curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors \
https://raw.githubusercontent.com/JoeStech/migrate-ease/main/js/advisor/main.py \
-o /opt/arm-migration-tools/migrate-ease/js/advisor/main.py

RUN rm -f /usr/local/bin/aperf \
Expand Down
20 changes: 18 additions & 2 deletions mcp-local/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ def apx_recipe_run(cmd:str, remote_ip_addr:str, remote_usr:str, recipe:str="code
},
)
apx_dir = os.environ.get("APX_HOME", "/opt/apx")
include_debug_trace = os.getenv("APX_DEBUG_TRACE", "").strip().lower() in {"1", "true", "yes", "on"}
ssh_mount_env = resolve_apx_ssh_mount_env()
key_path = ssh_mount_env["key_path"]
known_hosts_path = ssh_mount_env["known_hosts_path"]
Expand All @@ -319,7 +320,7 @@ def apx_recipe_run(cmd:str, remote_ip_addr:str, remote_usr:str, recipe:str="code

target_add_res = prepare_target(remote_ip_addr, remote_usr, key_path, apx_dir)
if "error" in target_add_res:
return {
error_response = {
"status": "error",
"recipe": recipe,
"stage": "target_prepare",
Expand All @@ -331,10 +332,14 @@ def apx_recipe_run(cmd:str, remote_ip_addr:str, remote_usr:str, recipe:str="code
"details": target_add_res.get("details", ""),
"raw_output": target_add_res.get("raw_output", ""),
}
if include_debug_trace:
error_response["debug_trace"] = target_add_res.get("debug_trace", [])
return error_response
prepare_debug_trace = target_add_res.get("debug_trace", [])

run_res = run_workload(cmd, target_add_res["target_id"], recipe, apx_dir)
if "error" in run_res:
return {
error_response = {
"status": "error",
"recipe": recipe,
"stage": "workload_run",
Expand All @@ -345,8 +350,19 @@ def apx_recipe_run(cmd:str, remote_ip_addr:str, remote_usr:str, recipe:str="code
),
"details": run_res.get("details", ""),
}
if include_debug_trace:
error_response["debug_trace"] = {
"prepare_target": prepare_debug_trace,
"run_workload": run_res.get("debug_trace", []),
}
return error_response

results = get_results(run_res["run_id"], recipe, apx_dir)
if include_debug_trace:
results["debug_trace"] = {
"prepare_target": prepare_debug_trace,
"run_workload": run_res.get("debug_trace", []),
}

return results

Expand Down
16 changes: 16 additions & 0 deletions mcp-local/tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,19 @@
}'''

EXPECTED_CHECK_MCA_TOOL_RESPONSE_STATUS = "ok"

CHECK_APX_RECIPE_RUN_REQUEST = {
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "apx_recipe_run",
"arguments": {
"cmd": "python3 -c \"print('Hello, world!')\"",
"remote_ip_addr": "localhost",
"remote_usr": "base",
"recipe": "code_hotspots",
"invocation_reason": "Run APX code hotspots recipe against the local test workload requested by the user.",
},
},
Comment thread
JoeStech marked this conversation as resolved.
}
Loading
Loading