Skip to content

Commit d2a77ab

Browse files
Add support for EXTENSIONS_REF environment variable (#2607)
Co-authored-by: openhands <openhands@all-hands.dev>
1 parent c78c3c2 commit d2a77ab

File tree

3 files changed

+105
-3
lines changed

3 files changed

+105
-3
lines changed

.github/workflows/run-eval.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ on:
6363
required: false
6464
default: main
6565
type: string
66+
extensions_branch:
67+
description: Extensions repo branch to use (for testing feature branches with skills/plugins)
68+
required: false
69+
default: main
70+
type: string
6671
instance_ids:
6772
description: >-
6873
Comma-separated instance IDs to evaluate.
@@ -157,6 +162,7 @@ jobs:
157162
echo "reason: ${{ github.event.inputs.reason || 'N/A' }}"
158163
echo "eval_branch: ${{ github.event.inputs.eval_branch || 'main' }}"
159164
echo "benchmarks_branch: ${{ github.event.inputs.benchmarks_branch || 'main' }}"
165+
echo "extensions_branch: ${{ github.event.inputs.extensions_branch || 'main' }}"
160166
echo "instance_ids: ${{ github.event.inputs.instance_ids || 'N/A' }}"
161167
echo "num_infer_workers: ${{ github.event.inputs.num_infer_workers || '(default)' }}"
162168
echo "num_eval_workers: ${{ github.event.inputs.num_eval_workers || '(default)' }}"
@@ -341,6 +347,7 @@ jobs:
341347
EVAL_WORKFLOW: ${{ env.EVAL_WORKFLOW }}
342348
EVAL_BRANCH: ${{ github.event.inputs.eval_branch || 'main' }}
343349
BENCHMARKS_BRANCH: ${{ github.event.inputs.benchmarks_branch || 'main' }}
350+
EXTENSIONS_BRANCH: ${{ github.event.inputs.extensions_branch || 'main' }}
344351
BENCHMARK: ${{ github.event.inputs.benchmark || 'swebench' }}
345352
TRIGGER_REASON: ${{ github.event.inputs.reason }}
346353
PR_NUMBER: ${{ steps.params.outputs.pr_number }}
@@ -357,7 +364,7 @@ jobs:
357364
# Normalize instance_ids: strip all spaces
358365
INSTANCE_IDS=$(printf '%s' "$INSTANCE_IDS" | tr -d ' ')
359366
360-
echo "Dispatching evaluation workflow with SDK commit: $SDK_SHA (benchmark: $BENCHMARK, eval branch: $EVAL_BRANCH, benchmarks branch: $BENCHMARKS_BRANCH, tool preset: $TOOL_PRESET)"
367+
echo "Dispatching evaluation workflow with SDK commit: $SDK_SHA (benchmark: $BENCHMARK, eval branch: $EVAL_BRANCH, benchmarks branch: $BENCHMARKS_BRANCH, extensions branch: $EXTENSIONS_BRANCH, tool preset: $TOOL_PRESET)"
361368
PAYLOAD=$(jq -n \
362369
--arg sdk "$SDK_SHA" \
363370
--arg sdk_run_id "${{ github.run_id }}" \
@@ -367,6 +374,7 @@ jobs:
367374
--arg reason "$TRIGGER_REASON" \
368375
--arg pr "$PR_NUMBER" \
369376
--arg benchmarks "$BENCHMARKS_BRANCH" \
377+
--arg extensions "$EXTENSIONS_BRANCH" \
370378
--arg benchmark "$BENCHMARK" \
371379
--arg instance_ids "$INSTANCE_IDS" \
372380
--arg num_infer_workers "$NUM_INFER_WORKERS" \
@@ -377,7 +385,7 @@ jobs:
377385
--arg agent_type "$AGENT_TYPE" \
378386
--arg partial_archive_url "$PARTIAL_ARCHIVE_URL" \
379387
--arg triggered_by "$TRIGGERED_BY" \
380-
'{ref: $ref, inputs: {sdk_commit: $sdk, sdk_workflow_run_id: $sdk_run_id, eval_limit: $eval_limit, models_json: ($models | tostring), trigger_reason: $reason, pr_number: $pr, benchmarks_branch: $benchmarks, benchmark: $benchmark, instance_ids: $instance_ids, num_infer_workers: $num_infer_workers, num_eval_workers: $num_eval_workers, enable_conversation_event_logging: $enable_conversation_event_logging, max_retries: $max_retries, tool_preset: $tool_preset, agent_type: $agent_type, partial_archive_url: $partial_archive_url, triggered_by: $triggered_by}}')
388+
'{ref: $ref, inputs: {sdk_commit: $sdk, sdk_workflow_run_id: $sdk_run_id, eval_limit: $eval_limit, models_json: ($models | tostring), trigger_reason: $reason, pr_number: $pr, benchmarks_branch: $benchmarks, extensions_branch: $extensions, benchmark: $benchmark, instance_ids: $instance_ids, num_infer_workers: $num_infer_workers, num_eval_workers: $num_eval_workers, enable_conversation_event_logging: $enable_conversation_event_logging, max_retries: $max_retries, tool_preset: $tool_preset, agent_type: $agent_type, partial_archive_url: $partial_archive_url, triggered_by: $triggered_by}}')
381389
RESPONSE=$(curl -sS -o /tmp/dispatch.out -w "%{http_code}" -X POST \
382390
-H "Authorization: token $PAT_TOKEN" \
383391
-H "Accept: application/vnd.github+json" \

openhands-sdk/openhands/sdk/context/skills/skill.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import io
22
import json
3+
import os
34
import re
45
from pathlib import Path
56
from typing import Annotated, ClassVar, Literal, Union
@@ -891,7 +892,9 @@ def load_project_skills(work_dir: str | Path) -> list[Skill]:
891892

892893
# Public skills repository configuration
893894
PUBLIC_SKILLS_REPO = "https://github.com/OpenHands/extensions"
894-
PUBLIC_SKILLS_BRANCH = "main"
895+
# Allow overriding the branch via EXTENSIONS_REF environment variable
896+
# (used by evaluation/benchmarks workflows to test feature branches)
897+
PUBLIC_SKILLS_BRANCH = os.environ.get("EXTENSIONS_REF", "main")
895898
DEFAULT_MARKETPLACE_PATH = "marketplaces/default.json"
896899

897900

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""Tests for EXTENSIONS_REF environment variable support.
2+
3+
These tests use subprocess to run each test in an isolated Python process,
4+
avoiding module state pollution that would affect other tests.
5+
"""
6+
7+
import subprocess
8+
import sys
9+
10+
11+
def _run_in_subprocess(test_code: str, env_extra: dict | None = None) -> None:
12+
"""Run test code in a subprocess with the given environment variables."""
13+
import os
14+
15+
env = os.environ.copy()
16+
if env_extra:
17+
env.update(env_extra)
18+
19+
result = subprocess.run(
20+
[sys.executable, "-c", test_code],
21+
env=env,
22+
capture_output=True,
23+
text=True,
24+
)
25+
if result.returncode != 0:
26+
raise AssertionError(
27+
f"Subprocess test failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
28+
)
29+
30+
31+
def test_extensions_ref_default():
32+
"""PUBLIC_SKILLS_BRANCH should default to 'main' when EXTENSIONS_REF is not set."""
33+
code = """
34+
import os
35+
if "EXTENSIONS_REF" in os.environ:
36+
del os.environ["EXTENSIONS_REF"]
37+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
38+
assert PUBLIC_SKILLS_BRANCH == "main", (
39+
f"Expected 'main' but got '{PUBLIC_SKILLS_BRANCH}'"
40+
)
41+
"""
42+
_run_in_subprocess(code)
43+
44+
45+
def test_extensions_ref_custom_branch():
46+
"""PUBLIC_SKILLS_BRANCH should use EXTENSIONS_REF when set."""
47+
code = """
48+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
49+
assert PUBLIC_SKILLS_BRANCH == "feature-branch", (
50+
f"Expected 'feature-branch' but got '{PUBLIC_SKILLS_BRANCH}'"
51+
)
52+
"""
53+
_run_in_subprocess(code, {"EXTENSIONS_REF": "feature-branch"})
54+
55+
56+
def test_extensions_ref_with_load_public_skills():
57+
"""load_public_skills should respect EXTENSIONS_REF environment variable."""
58+
code = """
59+
from unittest import mock
60+
from openhands.sdk.context.skills.skill import (
61+
PUBLIC_SKILLS_BRANCH,
62+
load_public_skills,
63+
)
64+
assert PUBLIC_SKILLS_BRANCH == "test-branch", (
65+
f"Expected 'test-branch' but got '{PUBLIC_SKILLS_BRANCH}'"
66+
)
67+
with mock.patch(
68+
"openhands.sdk.context.skills.skill.update_skills_repository"
69+
) as mock_update:
70+
mock_update.return_value = None
71+
load_public_skills()
72+
mock_update.assert_called_once()
73+
call_args = mock_update.call_args
74+
# branch is 2nd positional arg: (repo_url, branch, cache_dir)
75+
assert call_args[0][1] == "test-branch", (
76+
f"Expected branch='test-branch' but got {call_args[0][1]}"
77+
)
78+
"""
79+
_run_in_subprocess(code, {"EXTENSIONS_REF": "test-branch"})
80+
81+
82+
def test_extensions_ref_empty_string():
83+
"""Empty EXTENSIONS_REF should fall back to 'main'."""
84+
code = """
85+
from openhands.sdk.context.skills.skill import PUBLIC_SKILLS_BRANCH
86+
# Empty string returns empty string per os.environ.get behavior
87+
assert PUBLIC_SKILLS_BRANCH == "", (
88+
f"Expected '' but got '{PUBLIC_SKILLS_BRANCH}'"
89+
)
90+
"""
91+
_run_in_subprocess(code, {"EXTENSIONS_REF": ""})

0 commit comments

Comments
 (0)