The AzDO schema is only applied by yaml-language-server when the file
URI matches the configured globs (azure-pipelines.yml / *.yaml). The
smoke test was using 'plain-pipeline.yml' which never matched, causing
test [8] to always return 0 completions.
Also increased didOpen → completion sleep from 0.8s to 1.5s to give
yaml-ls time to parse the 1.6MB AzDO schema JSON.
- Inject AzDO schema into yaml-language-server via initializationOptions
at startup (primary mechanism, works without workspace.configuration)
- Respond to workspace/configuration pull requests from yaml-language-server
so schema is applied even when editors declare configuration capability
- Keep post-init workspace/didChangeConfiguration as belt-and-suspenders
- Bake azdo-pipeline-schema.json (~1.6MB, 119 defs) into Docker image
- Add smoke test [8]: AzDO task@version completions (254 items)
- Update smoke test YAML initialize to declare workspace.configuration
- yaml-language-server: rewrite to stdio per WebSocket (fixes crash loop)
vscode-jsonrpc v9 createServerSocketTransport is a TCP client, not server
now spawns yaml-language-server --stdio per connection via asyncio subprocess
- bicep/modules.py: add /iac_source_catalog.json as first path in _IAC_SOURCE_PATHS
Dockerfile copies to /iac_source_catalog.json but path wasn't listed
- server.py: remove YAML_LSP_PORT daemon (no longer needed with stdio mode)
- Makefile: add -e HTTP_PORT=$(HEALTH_PORT) to all docker run commands
server defaulted to :8000 but Makefile exposed :2089 with no override
- Dockerfile: COPY iac_source_catalog.json in builder + final stage
- push_catalogs.sh: cp iac_source_catalog.json to iLSP repo root before SCP
- smoke_test_completions.py: detect WS CLOSE in _recv_until_id; 20s YAML init timeout; clearer error message
- .gitignore: standard Python exclusions
Fixes: iac_source_modules=0 (was never baked into image)
Two bugs fixed in BicepModuleCatalog:
1. as_completion_items() generated 'br/modules:bicep/modules/appservice:...'
but bicepconfig modulePath='bicep' means Bicep prepends 'bicep/' automatically.
Fix: store ref_path = path.removeprefix('bicep/') and use it in insertText.
Correct output: 'br/modules:modules/appservice:2.3.x'
2. version/param lookups used get_module_by_name() which only matches the last
path segment ('appservice'). New-style refs capture 'modules/appservice' from
the regex, so lookup returned empty. Fix: add get_module_by_ref() that matches
both ref_path and bare name.
Also fixes _iac_param_map to strip path prefix so IAC source descriptions
still enrich completions for 'modules/appservice' refs.
All 63 tests pass.
asyncio.open_connection hangs indefinitely when backend port is silently
dropped. Add wait_for(timeout=3.0) so the WS close frame is always sent
within the test's 5s window.
- Start real pylsp process in fixture (retry-loop until port accepts)
- Fake client sends LSP initialize via WebSocket proxy
- Verify real pylsp capabilities + completionProvider in response
- Fix LSP frame parser to handle multi-header responses (Content-Length + Content-Type)
- Test graceful close when backend unreachable
- Remove static ports 2087/2088/2089 from Nomad; use one dynamic port
- pylsp and Bicep LS stay on localhost inside container (not exposed)
- aiohttp adds /python and /bicep WebSocket routes bridging to localhost LSPs
- Nomad env: HTTP_PORT=$NOMAD_PORT_http, internal ports fixed at 2087/2088
- Editors connect via ws://ilsp.i80.dk/python and ws://ilsp.i80.dk/bicep
- Traefik routes ilsp.i80.dk → single dynamic HTTP port
- Remove all DevOpsMCP/aiohttp runtime deps from BicepModuleCatalog
- BicepModuleCatalog.load() reads bicep_modules_catalog.json from disk at startup (sync)
- Fix _load_catalog: catalog uses dict {path: {versions, schema}} not a list
- server.py: call BicepModuleCatalog.load() synchronously, not via asyncio.gather
- Dockerfile: COPY bicep_modules_catalog.json into both builder + runtime stages
- Health endpoint now reports bicep_modules: 27
Verified locally: make run-quick → health returns pypi_packages:40 bicep_modules:27
catalog.py: Fix HTML parsing regex — pypi-server.i80.dk uses relative hrefs
like href="pkg-name/" not /simple/pkg-name/. Use simpler <a> text extractor.
modules.py: Replace /call-tool POST (wrong) with GET /api/bicep-modules (new REST
endpoint added to DevOpsMCP). Simpler, no MCP protocol overhead.
Dockerfile:
- Final stage now FROM mcr.microsoft.com/dotnet/runtime:8.0 instead of python:3.12-slim
+ MS apt repo. Reason: packages.microsoft.com GPG key uses SHA1 which Debian trixie
rejects since 2026-02-01. The official MS container image has no signing issue.
- Add python3 + pip3 + wget on top of dotnet base (bookworm)
- pip3 install --break-system-packages for debian bookworm compatibility
Makefile targets:
make build - build image locally
make rebuild - force no-cache build
make run - build + start container with port mappings
make run-quick - start without rebuilding
make stop/restart - container lifecycle
make logs - follow logs
make shell - bash into running container
make health - curl health endpoint
make smoke - end-to-end TCP + health test (scripts/smoke_test.sh)
make test - unit tests (pytest, no Docker)
make ps/ports - status helpers
make clean - stop + remove image