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
8 changes: 8 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,20 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}
run: make init

- name: Verify environment (full CodeQL install)
if: matrix.python-version == '3.14'
run: make env-check

- name: Run make init (skip CodeQL install)
if: matrix.python-version != '3.14'
env:
CODEQL_SKIP_INSTALL: 1
run: make init

- name: Verify environment (skip CodeQL install)
if: matrix.python-version != '3.14'
run: make env-check

- name: Run pytest with coverage
id: pytest
run: |
Expand Down
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@ export PROMPT_EXTRA_FILE
# Pass --thinking to raw opencode run when CODECOME_THINKING=1
OPENCODE_THINKING_FLAG := $(if $(filter 1,$(CODECOME_THINKING)),--thinking,)

# Derive managed CodeQL binary path from the host OS (no inline Python).
UNAME_S := $(shell uname -s 2>/dev/null || printf unknown)
ifeq ($(UNAME_S),Darwin)
CODEQL_PLATFORM := osx64
else ifeq ($(UNAME_S),Linux)
CODEQL_PLATFORM := linux64
else ifneq (,$(findstring MINGW,$(UNAME_S)))
CODEQL_PLATFORM := win64
else ifneq (,$(findstring MSYS,$(UNAME_S)))
CODEQL_PLATFORM := win64
else ifneq (,$(findstring CYGWIN,$(UNAME_S)))
CODEQL_PLATFORM := win64
else
CODEQL_PLATFORM := win64
endif
CODEQL_BIN := $(or $(CODEQL_INSTALL_PATH),.tools/codeql/$(CODEQL_PLATFORM)/current/codeql)

ifndef NO_COLOR
RED := \033[31m
GREEN := \033[32m
Expand Down Expand Up @@ -160,7 +177,7 @@ env-check:
@test -x "$(PYTHON)" || (printf "\n$(BOLD)$(RED)[FAIL]$(RESET) Missing repo virtualenv at .venv\n\nRun:\n\n make init\n\n" && exit 1)
@$(PYTHON) -c "import yaml, rich" >/dev/null 2>&1 || (printf "\n$(BOLD)$(RED)[FAIL]$(RESET) .venv is missing required Python packages\n\nRun:\n\n make init\n\nIf you updated requirements, rerun the same command to resync .venv.\n\n" && exit 1)
@if [ ! -f .tools/codeql/.disabled ]; then \
test -x .tools/codeql/current/codeql || (printf "\n$(BOLD)$(RED)[FAIL]$(RESET) CodeQL is enabled but the managed binary is missing.\n\nRun:\n\n make init\n\nOr to explicitly disable CodeQL:\n\n CODEQL=0 make init\n\n" && exit 1); \
test -x "$(CODEQL_BIN)" || (printf "\n$(BOLD)$(RED)[FAIL]$(RESET) CodeQL is enabled but the managed binary is missing ($(CODEQL_BIN)).\n\nRun:\n\n make init\n\nOr to explicitly disable CodeQL:\n\n CODEQL=0 make init\n\n" && exit 1); \
fi

# ---------------------------------------------------------------------------
Expand Down
1 change: 0 additions & 1 deletion codecome.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ audit:
install:
managed: true
version: "latest"
path: ".tools/codeql/current/codeql"

output_dir: "./itemdb/codeql"
database_dir: "./itemdb/codeql/databases"
Expand Down
20 changes: 20 additions & 0 deletions tests/test_codeql_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,23 @@ def test_resolve_config_falls_back_on_invalid_max_candidates(monkeypatch) -> Non

config = config_module.resolve_config()
assert config.max_candidates == config_module.DEFAULTS["max_candidates"]


def test_install_path_defaults_to_platform_specific(tmp_path: Path, monkeypatch) -> None:
monkeypatch.delenv("CODEQL_INSTALL_PATH", raising=False)
config_path = tmp_path / "codecome.yml"
config_path.write_text(
"audit:\n static_analysis:\n codeql:\n enabled: true\n",
encoding="utf-8",
)
monkeypatch.setattr(config_module, "ROOT", tmp_path)
(tmp_path / "templates").mkdir(parents=True, exist_ok=True)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
(tmp_path / "templates" / "codeql-packs.yml").write_text("", encoding="utf-8")

from codeql.platform import codeql_platform
plat = codeql_platform()

config = config_module.resolve_config()
assert plat in config.install_path
assert config.install_path.endswith("/current/codeql")
assert ".tools/codeql/" in config.install_path
Comment thread
coderabbitai[bot] marked this conversation as resolved.
4 changes: 2 additions & 2 deletions tests/test_mock_llm_parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ def test_normalize_strips_timestamps_and_ids(self):
assert "messageID" not in out["part"]
assert out["part"]["text"] == "hello"

def test_normalize_filters_serve_only_types(self):
def test_normalize_filters_parity_ignored_types(self):
mod = load_parity_module()
for t in mod._SERVE_ONLY_TYPES:
for t in mod._PARITY_IGNORED_TYPES:
assert mod.normalize_event({"type": t}) is None

def test_normalize_truncates_tool_output(self):
Expand Down
24 changes: 19 additions & 5 deletions tools/mock-llm-parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,25 @@
DEFAULT_TIMEOUT_S = 30.0
MOCK_HOST = "127.0.0.1"

# Events that only appear in the serve path and should be ignored for parity.
# Events that should be ignored for parity (serve-only, lifecycle, or volatile config).
# Note: session.status (retry/busy) is NOT serve-only when _CODECOME_INSIDE_HARNESS=1
# because the status-forwarder plugin emits them to stdout.
# session.idle is deprecated and serve-only.
_SERVE_ONLY_TYPES = {"server.connected", "server.heartbeat", "session.idle", "message.updated", "message.part.updated", "file.edited", "file.watcher.updated", "todo.updated"}
_PARITY_IGNORED_TYPES = {
"server.connected",
"server.heartbeat",
"session.idle",
"message.updated",
"message.part.updated",
"file.edited",
"file.watcher.updated",
"todo.updated",
# Volatile opencode startup/config events that vary across builds.
"plugin.added",
"plugin.updated",
"connector.updated",
"reference.updated",
}


def _step_sort_key(ev: dict[str, Any]) -> tuple[int | float, str]:
Expand Down Expand Up @@ -326,7 +340,7 @@ def _consume() -> None:
def normalize_event(ev: dict[str, Any]) -> dict[str, Any] | None:
"""Remove volatile fields and serve-only events for comparison."""
ev_type = ev.get("type", "")
if ev_type in _SERVE_ONLY_TYPES:
if ev_type in _PARITY_IGNORED_TYPES:
return None
out = dict(ev)

Expand Down Expand Up @@ -394,8 +408,8 @@ def normalize_event(ev: dict[str, Any]) -> dict[str, Any] | None:
def compare_events(
run_events: list[dict[str, Any]], serve_events: list[dict[str, Any]]
) -> tuple[bool, str]:
run_norm = [normalize_event(e) for e in run_events if normalize_event(e) is not None]
serve_norm = [normalize_event(e) for e in serve_events if normalize_event(e) is not None]
run_norm = [e for e in map(normalize_event, run_events) if e is not None]
serve_norm = [e for e in map(normalize_event, serve_events) if e is not None]

run_sorted = _sort_events_by_step(run_norm)
serve_sorted = _sort_events_by_step(serve_norm)
Expand Down
Loading