From 2543d959d4b111fd7a881784244d9103684ecb4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:43:09 +0000 Subject: [PATCH 1/3] Initial plan From 2f4b24e775d7b30620345ecf4996181851cea774 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:48:42 +0000 Subject: [PATCH 2/3] test: add regression tests for devcontainer JSON validity Add tests/test_devcontainer_config.py to verify that both .devcontainer/devcontainer.json and .devcontainer/devcontainer-lock.json are valid JSON with no trailing data. Root cause of build-and-test CI failure (job 86511118286): a stray ` }` appended after the root object in devcontainer-lock.json caused @devcontainers/cli to throw: SyntaxError: Unexpected non-whitespace character after JSON at position 2143 aborting the devcontainer build step entirely. The structural fix (removing the stray brace) was applied in commit 6b470e38. These tests prevent regression. --- tests/test_devcontainer_config.py | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/test_devcontainer_config.py diff --git a/tests/test_devcontainer_config.py b/tests/test_devcontainer_config.py new file mode 100644 index 000000000..5075353f4 --- /dev/null +++ b/tests/test_devcontainer_config.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import json +from pathlib import Path + +import pytest + +pytestmark = pytest.mark.unit + +_DEVCONTAINER_DIR = Path(__file__).resolve().parents[1] / ".devcontainer" + + +def test_devcontainer_json_is_valid_json() -> None: + """devcontainer.json must be valid JSON; the @devcontainers/cli parses it with JSON.parse().""" + config_path = _DEVCONTAINER_DIR / "devcontainer.json" + content = config_path.read_text(encoding="utf-8") + try: + json.loads(content) + except json.JSONDecodeError as exc: + pytest.fail( + f".devcontainer/devcontainer.json is not valid JSON: {exc}\n" + "This will cause `devcontainer build` (and the build-and-test CI job) to fail." + ) + + +def test_devcontainer_lock_json_is_valid_json() -> None: + """devcontainer-lock.json must be valid JSON with no trailing data. + + A stray closing brace after the root object caused: + SyntaxError: Unexpected non-whitespace character after JSON at position 2143 + which broke the build-and-test devcontainer CI job. + """ + lock_path = _DEVCONTAINER_DIR / "devcontainer-lock.json" + if not lock_path.exists(): + pytest.skip("devcontainer-lock.json not present") + content = lock_path.read_text(encoding="utf-8") + try: + json.loads(content) + except json.JSONDecodeError as exc: + pytest.fail( + f".devcontainer/devcontainer-lock.json is not valid JSON: {exc}\n" + "Trailing or extra characters after the root object will cause " + "`devcontainer build` (and the build-and-test CI job) to fail." + ) + + +def test_devcontainer_json_no_trailing_data() -> None: + """Ensure no extra characters appear after the root JSON object in devcontainer.json.""" + config_path = _DEVCONTAINER_DIR / "devcontainer.json" + content = config_path.read_text(encoding="utf-8").strip() + parsed = json.loads(content) + # Re-serialise and confirm round-trip is clean (catches extra non-whitespace data) + assert isinstance(parsed, dict), "devcontainer.json root must be a JSON object" + + +def test_devcontainer_lock_json_no_trailing_data() -> None: + """Ensure no extra characters appear after the root JSON object in devcontainer-lock.json.""" + lock_path = _DEVCONTAINER_DIR / "devcontainer-lock.json" + if not lock_path.exists(): + pytest.skip("devcontainer-lock.json not present") + content = lock_path.read_text(encoding="utf-8").strip() + parsed = json.loads(content) + assert isinstance(parsed, dict), "devcontainer-lock.json root must be a JSON object" From b2a3e56e498bcf8c43af8df9af2926b128bc2bcf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 11 Jul 2026 04:49:45 +0000 Subject: [PATCH 3/3] test: improve trailing-data detection using JSONDecoder.raw_decode Use json.JSONDecoder().raw_decode() in the trailing-data tests so that the end position of the JSON value is explicitly checked, rather than relying on json.loads() implicitly. This makes the test intent clear and produces a descriptive failure message showing exactly what trailing characters were found and at which offset. --- tests/test_devcontainer_config.py | 39 ++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/test_devcontainer_config.py b/tests/test_devcontainer_config.py index 5075353f4..4595a34e4 100644 --- a/tests/test_devcontainer_config.py +++ b/tests/test_devcontainer_config.py @@ -44,20 +44,43 @@ def test_devcontainer_lock_json_is_valid_json() -> None: ) +def _assert_no_trailing_data(path: Path, content: str) -> None: + """Assert that *content* contains a single JSON value with only whitespace after it. + + Uses ``JSONDecoder.raw_decode()`` which returns the end-position of the + parsed value. Anything remaining after that position (beyond whitespace) + is trailing data and indicates a corrupted file. + """ + decoder = json.JSONDecoder() + try: + _parsed, end_index = decoder.raw_decode(content) + except json.JSONDecodeError as exc: + pytest.fail(f"{path.name} is not valid JSON: {exc}") + + trailing = content[end_index:] + assert trailing.strip() == "", ( + f"{path.name} has trailing non-whitespace data after the root JSON object " + f"(at offset {end_index}): {trailing!r}\n" + "This will cause `devcontainer build` to fail with a SyntaxError." + ) + + def test_devcontainer_json_no_trailing_data() -> None: """Ensure no extra characters appear after the root JSON object in devcontainer.json.""" config_path = _DEVCONTAINER_DIR / "devcontainer.json" - content = config_path.read_text(encoding="utf-8").strip() - parsed = json.loads(content) - # Re-serialise and confirm round-trip is clean (catches extra non-whitespace data) - assert isinstance(parsed, dict), "devcontainer.json root must be a JSON object" + content = config_path.read_text(encoding="utf-8") + _assert_no_trailing_data(config_path, content) def test_devcontainer_lock_json_no_trailing_data() -> None: - """Ensure no extra characters appear after the root JSON object in devcontainer-lock.json.""" + """Ensure no extra characters appear after the root JSON object in devcontainer-lock.json. + + Regression test for the stray `` }`` that caused: + SyntaxError: Unexpected non-whitespace character after JSON at position 2143 + in CI job 86511118286. + """ lock_path = _DEVCONTAINER_DIR / "devcontainer-lock.json" if not lock_path.exists(): pytest.skip("devcontainer-lock.json not present") - content = lock_path.read_text(encoding="utf-8").strip() - parsed = json.loads(content) - assert isinstance(parsed, dict), "devcontainer-lock.json root must be a JSON object" + content = lock_path.read_text(encoding="utf-8") + _assert_no_trailing_data(lock_path, content)