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
32 changes: 20 additions & 12 deletions .github/workflows/agent-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
name: Agent E2E

# Runs the agent e2e suites (e2e/) against the released Agentspan
# server JAR — a full Conductor server with the agent runtime baked in.
# Runs the agent e2e suites (e2e/) against the Conductor OSS server boot
# JAR (Maven Central) — the server this SDK ships against, with the agent
# runtime on by default from 3.32.0-rc.8 onward. The Agentspan server JAR
# is no longer used here; the Agentspan CLI binary is still downloaded
# (its own separate pin) since e2e/test_suite16_cli_skills.py drives it
# against this same server.
# tests/integration/ai stays manual-only (same as upstream Agentspan,
# which never ran its tests/integration in CI): run it locally against
# a live server with `pytest tests/integration/ai`.
Expand All @@ -12,12 +16,18 @@ name: Agent E2E

on: [pull_request, workflow_dispatch]

# least privilege: the token is only used to download the public agentspan
# CLI release asset (gh release download) — read access suffices
permissions:
contents: read

concurrency:
group: agent-e2e-${{ github.ref }}
cancel-in-progress: true

env:
AGENTSPAN_VERSION: "0.4.3" # pinned server/CLI release — bump deliberately
CONDUCTOR_SERVER_VERSION: "3.32.0-rc.8" # pinned server release — bump deliberately
AGENTSPAN_CLI_VERSION: "0.4.4" # pinned CLI release — independent of the server (revert to 0.4.3 if this breaks)

jobs:
agent-e2e:
Expand Down Expand Up @@ -48,22 +58,20 @@ jobs:
id: jar_cache
uses: actions/cache@v4
with:
path: agentspan-server.jar
key: agentspan-server-${{ env.AGENTSPAN_VERSION }}
path: conductor-server.jar
key: conductor-oss-server-${{ env.CONDUCTOR_SERVER_VERSION }}

- name: Download server JAR from release
- name: Download server JAR from Maven Central
if: steps.jar_cache.outputs.cache-hit != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release download "v${AGENTSPAN_VERSION}" --repo agentspan-ai/agentspan \
--pattern "agentspan-server-${AGENTSPAN_VERSION}.jar" --output agentspan-server.jar
curl -fL --retry 3 -o conductor-server.jar \
"https://repo1.maven.org/maven2/org/conductoross/conductor-server/${CONDUCTOR_SERVER_VERSION}/conductor-server-${CONDUCTOR_SERVER_VERSION}-boot.jar"

- name: Download CLI binary from release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release download "v${AGENTSPAN_VERSION}" --repo agentspan-ai/agentspan \
gh release download "v${AGENTSPAN_CLI_VERSION}" --repo agentspan-ai/agentspan \
--pattern "agentspan_linux_amd64" --output agentspan
chmod +x agentspan

Expand All @@ -80,7 +88,7 @@ jobs:

- name: Start server
run: |
java -jar agentspan-server.jar --server.port=8080 > server.log 2>&1 &
java -jar conductor-server.jar --server.port=8080 > server.log 2>&1 &

- name: Wait for server health
run: |
Expand Down
30 changes: 20 additions & 10 deletions e2e/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,23 @@ def _run(self, *args: str) -> subprocess.CompletedProcess:

def set(self, name: str, value: str) -> None:
result = self._run("credentials", "set", name, value)
assert result.returncode == 0, (
f"credentials set {name} failed: {result.stderr}"
)
if result.returncode != 0:
# conductor-oss standalone serves secrets from the server process
# env — the store is read-only there, so the write-dependent
# lifecycle steps cannot run (a server-flavor capability, not an
# SDK regression; mirrors the Java/C# ports' assumption-skip).
if "read-only" in result.stderr.lower():
pytest.skip(
"server secret store is read-only (env-backed) — "
"skipping write-dependent step"
)
raise AssertionError(f"credentials set {name} failed: {result.stderr}")

def delete(self, name: str) -> None:
result = self._run("credentials", "delete", name)
# Ignore "not found" errors during cleanup
if result.returncode != 0 and "not found" not in result.stderr.lower():
# Ignore "not found" and "read-only" errors during cleanup — best-effort
stderr = result.stderr.lower()
if result.returncode != 0 and "not found" not in stderr and "read-only" not in stderr:
raise AssertionError(
f"credentials delete {name} failed: {result.stderr}"
)
Expand Down Expand Up @@ -162,9 +171,10 @@ def get_task_by_name(execution_id: str, task_ref_prefix: str) -> list:
# reason unrelated to the SDK. Probe the running server once and skip those tests
# when unsupported.
#
# TODO: once agentspan cuts a release that implements runtimeMetadata, bump
# AGENTSPAN_VERSION in .github/workflows/agent-e2e.yml — this guard then lets the
# credential tests run automatically (no test change needed).
# conductor-oss implements this from 3.32.0-rc.8 onward (see
# .github/workflows/agent-e2e.yml's CONDUCTOR_SERVER_VERSION) — this guard lets the
# credential tests run automatically against any server that has it, no test
# change needed.

_RUNTIME_METADATA_SUPPORT = None

Expand Down Expand Up @@ -208,6 +218,6 @@ def requires_runtime_metadata():
pytest.skip(
"server does not persist/deliver TaskDef.runtimeMetadata "
"(conductor-oss PR #1255) — worker credential injection requires it. "
"TODO: bump AGENTSPAN_VERSION in .github/workflows/agent-e2e.yml once a "
"release ships runtimeMetadata support."
"Needs conductor-oss >= 3.32.0-rc.8 (see CONDUCTOR_SERVER_VERSION in "
".github/workflows/agent-e2e.yml)."
)
12 changes: 11 additions & 1 deletion e2e/test_suite26_worker_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,17 @@ def _put_secret(name: str, value: str) -> None:
headers={"Content-Type": "text/plain"},
timeout=10,
)
r.raise_for_status()
if not r.ok:
# conductor-oss standalone serves secrets from the server process
# env — the store is read-only there, so the write-dependent
# lifecycle steps cannot run (a server-flavor capability, not an
# SDK regression; mirrors the Java/C# ports' assumption-skip).
if "read-only" in r.text.lower():
pytest.skip(
"server secret store is read-only (env-backed) — "
"skipping write-dependent step"
)
r.raise_for_status()


def _delete_secret(name: str) -> None:
Expand Down
7 changes: 4 additions & 3 deletions e2e/test_suite5_http_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,12 @@ def test_http_lifecycle(self, runtime, cli_credentials, model):
["mcp-testkit", "--help"],
capture_output=True,
text=True,
timeout=5,
timeout=15,
)
except FileNotFoundError:
except (FileNotFoundError, subprocess.TimeoutExpired):
pytest.skip(
"mcp-testkit not installed — required for Suite 5 HTTP tools test"
"mcp-testkit not installed or unresponsive — required for "
"Suite 5 HTTP tools test"
)

server_proc = None
Expand Down
Loading