Commit Graph

38 Commits

Author SHA1 Message Date
Henrik Jess Nielsen
578f88a0e8 fix(bicep): support multi-line array completion for roles and other enums
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m35s
Adds support for autocomplete in multi-line array syntax like:

    roles: [
      'KEY_VAULT_  ← cursor triggers completion here
    ]

Previously only worked on same line as opening bracket:
    roles: ['KEY_VAULT_  ← only this worked

Changes:
- Walk backwards up to 10 lines to find array opening (e.g. "roles: [")
- Detect if cursor is inside array based on indentation and quotes
- Stop lookback if closing bracket found (not in array anymore)
- Add test case for nested multi-line array completion
- Improve lsp_bridge.py error handling with traceback logging
- Add lsp_bridge_debug.sh wrapper for easier IntelliJ debugging
- Update EDITOR_SETUP.md with correct IntelliJ LSP4IJ config

Fixes autocomplete for deeply nested structures like:
    assignments: [{ roles: ['APP_CONFIGURATION_...'] }]

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-19 13:51:17 +02:00
Henrik Jess Nielsen
d4cb9ae8fb Roles
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 48s
2026-05-19 11:46:54 +02:00
Henrik Jess Nielsen
aa37c259ad Roles
All checks were successful
Build and Deploy iLSP / test (push) Successful in 23s
Build and Deploy iLSP / build-and-deploy (push) Successful in 3m13s
2026-05-19 10:28:22 +02:00
Henrik Jess Nielsen
fc40157a77 add lsp_bridge.py: proper LSP-aware WebSocket bridge replacing websocat
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 57s
websocat reads stdin line-by-line and waits for \n before sending each chunk.
LSP JSON bodies have no trailing \n, causing a deadlock: websocat holds the
body, the server waits for the body, LSP4IJ waits for the response.

lsp_bridge.py reads complete Content-Length-framed messages before sending,
then sends each as a single binary WebSocket frame. This fixes autocomplete
in IntelliJ IDEA via LSP4IJ.
2026-05-11 12:29:33 +02:00
Henrik Jess Nielsen
cb914dd990 docs: add IntelliJ LSP4IJ setup + Azure Toolkit conflict warning
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 51s
When Azure Toolkit is also installed, IDEA merges its native Bicep
completions with LSP4IJ completions — causing resource/projectName/schema
noise that bypasses the iLSP proxy entirely.
2026-05-11 11:44:31 +02:00
Henrik Jess Nielsen
5cb3e04b6e fix(modules): case-insensitive module ref lookup
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m20s
Catalog stores lowercase paths (e.g. modules/keyvault) but .bicep files
may use camelCase (e.g. modules/keyVault). Make get_module_by_ref
case-insensitive so completions work regardless of casing.
2026-05-11 11:41:35 +02:00
Henrik Jess Nielsen
02c09f1d18 fix(bicep): always suppress LS noise in version/param/param_value contexts
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m31s
Previously the 'if lru_items:' guard meant that when our catalog returned
an empty list (e.g. a param with no allowed enum values), the Bicep LS
completions (Bicep keywords, schema types) would leak through unchanged.

Now the replace is unconditional for version/param/param_value contexts.
The Bicep LS has no knowledge of our private ACR registry so its output
in these positions is always noise — suppress it even when we have nothing
better to show.
2026-05-11 11:37:50 +02:00
Henrik Jess Nielsen
445ccb5769 fix(bicep): detect param context when module ref has empty version
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m24s
The lookback regex used ([^']+) requiring one or more version chars,
so 'br/modules:modules/keyvault:' (no version yet) silently fell through
to the unknown context and injected all module names instead of params.

Change + to * to allow empty version string. The param_completion_items
fallback already handles empty version by picking the closest schema.
2026-05-11 11:20:54 +02:00
Henrik Jess Nielsen
e7471e5dfd fix(bicep): replace LS completions entirely for version/param/param_value contexts
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m24s
For specific LRU contexts (version, param, param_value), the Bicep LS was
appending its own random/irrelevant completions alongside the LRU catalog
items. The LS has no knowledge of the private ACR registry, so its suggestions
in these positions are noise.

Now the LS items are discarded entirely for these three contexts. LS items
are still kept (below LRU items) for module_path and unknown contexts.
2026-05-11 11:08:47 +02:00
Henrik Jess Nielsen
3b7c29d1e6 feat: add landing page at /
All checks were successful
Build and Deploy iLSP / test (push) Successful in 22s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m49s
Clean HTML page describing iLSP, what LSP is, the WebSocket endpoints,
and setup instructions for Neovim, IntelliJ and VS Code. Live stats
(Bicep modules, pipeline templates, PyPI packages) are rendered from
the in-memory catalog at request time.

No emojis. Minimal CSS, no external dependencies.
2026-05-10 18:17:14 +02:00
Henrik Jess Nielsen
ba477d5e97 test: add real-file integration test for Bicep + YAML pipelines
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m0s
Opens actual IaC repo files (roleAssignments.bicep, Matematikfessor.bicep,
azure-pipelines.yml from UmbPlatform/MitAlinea/Next etc.) and verifies
that completions are returned from the live ilsp.i80.dk service.

14/14 tests pass:
- 7 Bicep files: 27 module completions each
- 7 azure-pipelines.yml: 254 task@version completions each

Run: python3 scripts/test_real_files.py [https://ilsp.i80.dk]
2026-05-10 18:05:23 +02:00
Henrik Jess Nielsen
703d5d67a8 fix(smoke): use azure-pipelines.yml URI to match AzDO schema glob
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 58s
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.
2026-05-10 17:27:44 +02:00
Henrik Jess Nielsen
e5ba01a52b feat: add AzDO pipeline schema completions (task@version, inputs, steps)
All checks were successful
Build and Deploy iLSP / test (push) Successful in 20s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m27s
- 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
2026-05-10 16:55:54 +02:00
Henrik Jess Nielsen
acccd9ba20 docs: add README and EDITOR_SETUP with YAML pipeline template section
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 46s
- README.md: project overview, quick start, deploy instructions
- EDITOR_SETUP.md: editor config (neovim, VS Code), full feature matrix
  - Bicep: internal module registry, versions, params, allowed values
  - YAML AzDO: pipeline template completions with parameter injection
  - YAML GHA: reusable workflow completions with input injection
  - Health check fields explained, smoke test usage
2026-05-10 16:40:58 +02:00
Henrik Jess Nielsen
ae751f944c fix: yaml stdio per-connection, iac catalog path, makefile port
All checks were successful
Build and Deploy iLSP / test (push) Successful in 25s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m37s
- 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
2026-05-10 16:37:42 +02:00
Henrik Jess Nielsen
2a1717ff81 feat: bake iac_source_catalog into Docker image; fix YAML smoke test; add .gitignore
All checks were successful
Build and Deploy iLSP / test (push) Successful in 23s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m35s
- 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)
2026-05-10 16:23:02 +02:00
Henrik Jess Nielsen
ef3535048b fix: Bicep module ref path — strip bicep/ prefix, lookup by ref_path
All checks were successful
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m29s
Build and Deploy iLSP / test (push) Successful in 24s
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.
2026-05-10 16:10:36 +02:00
Henrik Jess Nielsen
333d986e76 feat: YAML pipeline template autocomplete (AzDO + GHA)
All checks were successful
Build and Deploy iLSP / test (push) Successful in 25s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m34s
- Add scripts/sync_pipeline_templates.py — scans LRU AzDO and GHA template
  repos; outputs unified pipeline_templates_catalog.json (48 templates: 45
  AzDO + 3 GHA)
- Add scripts/template_sources.yml — source config (AzDO alias, GHA org)
- Add pipeline_templates_catalog.json — baked catalog (49 KB)
- Add ilsp/yaml_lsp/catalog.py — PipelineTemplateCatalog with completion item
  generators for template paths, param names, allowed values, GHA inputs
- Add ilsp/yaml_lsp/proxy.py — async WS↔TCP bridge with LSP frame buffering,
  per-connection document tracking, AzDO/GHA context detection, and completion
  injection (LRU items sortText 0_, standard items downgraded to 9_)
- Wire yaml_ws_handler into server.py (replaces raw _ws_proxy call)
- Load PipelineTemplateCatalog at startup; reload + health report template count
- Update push_catalogs.sh to push pipeline_templates_catalog.json
- Update Dockerfile to bake pipeline_templates_catalog.json as image fallback
- Add tests/test_yaml_catalog.py (14 tests) + tests/test_yaml_proxy.py (18 tests)
  All 67 tests green
2026-05-10 15:59:37 +02:00
Henrik Jess Nielsen
5501254b55 feat: YAML LSP (/yaml endpoint) + IAC source catalog enrichment
All checks were successful
Build and Deploy iLSP / test (push) Successful in 21s
Build and Deploy iLSP / build-and-deploy (push) Successful in 2m49s
- Add yaml-language-server (Node.js) to Dockerfile stage 3
- Add YAML_LSP_PORT=2090 env var (Dockerfile + ilsp.nomad)
- Start yaml-language-server in background thread (_serve_yaml_lsp)
- Expose /yaml WebSocket endpoint (same WS→TCP proxy as /python and /bicep)
- Load iac_source_catalog.json alongside bicep_modules_catalog.json
- Enrich param_completion_items() with descriptions + required flag from IAC source
  - Required params sorted first (sortText 0_lru_param_0_...) and marked with *
  - detail field shows * prefix for required params
- Update /health to report iac_source_modules + yaml_lsp fields
- Rewrite EDITOR_SETUP.md: WebSocket URLs, YAML schemas config for all editors
  (Helix, Neovim, PyCharm, VS Code) with azure-pipelines + gitea actions schemas
- All 35 tests pass
2026-05-10 15:40:13 +02:00
Henrik Jess Nielsen
b93aa84737 feat: param_value context — enum/allowed completions for principalType, environmentType etc.
All checks were successful
Build and Deploy iLSP / test (push) Successful in 20s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m20s
- Add _KNOWN_ENUMS dict (principalType, principalObjectType, environmentType fallbacks)
- Add param_value_completion_items() to BicepModuleCatalog
- Detect 'param_value' context in _detect_context() (cursor after 'param: ' inside params block)
- Wire param_value into _inject_completions()
- 9 new unit tests (context detection, catalog allowed, known enum fallback, injection)
- Fix modules.py edit regression (param_completion_items was orphaned)
- All 35 tests pass
2026-05-10 15:30:31 +02:00
Henrik Jess Nielsen
bf24b5677f fix: backend unavailable test — use free port + ws.close(timeout=2s)
All checks were successful
Build and Deploy iLSP / test (push) Successful in 20s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m32s
- Use _free_port() instead of hardcoded 19999 (avoids CI port conflicts)
- Add timeout=2.0 to ws.close() so close handshake never blocks >2s
2026-05-10 15:16:55 +02:00
Henrik Jess Nielsen
404bd1418d fix: add 3s timeout to LSP backend connection (handles CI firewall drops)
Some checks failed
Build and Deploy iLSP / test (push) Failing after 27s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
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.
2026-05-10 15:13:03 +02:00
Henrik Jess Nielsen
a708cb779e fix: push_catalogs.sh uses ilsp.i80.dk (not lsp.i80.dk)
Some checks failed
Build and Deploy iLSP / test (push) Failing after 26s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
2026-05-10 15:07:22 +02:00
Henrik Jess Nielsen
27947e4f7f feat: context-aware Bicep completions (version + param injection)
Some checks failed
Build and Deploy iLSP / test (push) Failing after 27s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
- ProxySession tracks open documents per TCP connection
- _detect_context() identifies version, param, and module_path contexts
- version context: autocomplete versions for 'br/modules:NAME:' cursor positions
- param context: autocomplete params for specific module+version (with version fallback)
- modules.py: added get_module_by_name(), version_completion_items(), param_completion_items()
- 28/28 tests passing
2026-05-10 15:04:11 +02:00
Henrik Jess Nielsen
eafddb6f4a test: fake LSP client integration tests against real pylsp
Some checks failed
Build and Deploy iLSP / test (push) Failing after 27s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
- 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
2026-05-10 14:52:43 +02:00
Henrik Jess Nielsen
941d7b003f refactor: single HTTP port, WebSocket proxy for LSP endpoints
All checks were successful
Build and Deploy iLSP / test (push) Successful in 19s
Build and Deploy iLSP / build-and-deploy (push) Successful in 1m27s
- 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
2026-05-10 14:44:33 +02:00
Henrik Jess Nielsen
83817229e0 fix: remove host volume requirement until autobox is configured
All checks were successful
Build and Deploy iLSP / test (push) Successful in 18s
Build and Deploy iLSP / build-and-deploy (push) Successful in 49s
Volume can be added back once ilsp-data is registered in nomad.hcl.
Baked-in catalog (27 modules) is sufficient for initial deploy.
2026-05-10 14:20:31 +02:00
Henrik Jess Nielsen
0527df717c feat: volume-based catalog refresh with hot-reload
Some checks failed
Build and Deploy iLSP / test (push) Successful in 18s
Build and Deploy iLSP / build-and-deploy (push) Failing after 12m14s
- modules.py: check /data/ volume first, then baked-in /bicep_modules_catalog.json
- server.py: add POST /reload endpoint — reloads catalogs without restart
- ilsp.nomad: add 'ilsp-data' host volume mounted at /data
- Makefile: add push-catalogs, health-prod, run-with-data targets; DEVOPS_MCP_REPO var
- scripts/push_catalogs.sh: SCP both catalogs to autobox + call /reload

Workflow: sync scripts on Mac → make push-catalogs → completions live in <5s
2026-05-10 13:51:01 +02:00
Henrik Jess Nielsen
6b38cbd70c feat: bake bicep catalog into image; fix dict-based modules parsing
Some checks failed
Build and Deploy iLSP / build-and-deploy (push) Has been cancelled
Build and Deploy iLSP / test (push) Successful in 18s
- 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
2026-05-10 13:40:48 +02:00
Henrik Jess Nielsen
44da791f5a fix: update tests to match thread-based proxy API (_frame, _inject_completions)
All checks were successful
Build and Deploy iLSP / build-and-deploy (push) Successful in 2m6s
Build and Deploy iLSP / test (push) Successful in 19s
BicepProxy class and _ContentLengthFramer no longer exist after rewrite.
Tests now call module-level functions directly.
2026-05-10 13:35:40 +02:00
Henrik Jess Nielsen
c48ea919f6 ci: add --ignore-installed to avoid typing_extensions uninstall error
Some checks failed
Build and Deploy iLSP / test (push) Failing after 19s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
2026-05-10 13:33:03 +02:00
Henrik Jess Nielsen
6385e159ff fix: replace asyncio subprocess proxy with thread-based Popen proxy
Some checks failed
Build and Deploy iLSP / test (push) Failing after 12s
Build and Deploy iLSP / build-and-deploy (push) Has been skipped
asyncio subprocess PIPE unreliable for long-lived stdio bridging. Use Popen + threads instead. Also fix smoke_test.sh stdin handling.
2026-05-10 13:02:52 +02:00
Henrik Jess Nielsen
c550a4963e Fix PyPI regex and switch Bicep modules to /api/bicep-modules endpoint
Some checks failed
Build and Deploy iLSP / test (push) Successful in 7s
Build and Deploy iLSP / build-and-deploy (push) Failing after 25s
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.
2026-05-10 12:48:13 +02:00
Henrik Jess Nielsen
1a594c78c3 Fix Dockerfile: use mcr.microsoft.com/dotnet/runtime:8.0 as base, add Makefile
Some checks failed
Build and Deploy iLSP / test (push) Successful in 6s
Build and Deploy iLSP / build-and-deploy (push) Failing after 20s
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
2026-05-10 12:43:09 +02:00
Henrik Jess Nielsen
cd17e9bfaa Add unit tests, smoke test script, fix CI to debian-host + test job
Some checks failed
Build and Deploy iLSP / test (push) Successful in 19s
Build and Deploy iLSP / build-and-deploy (push) Failing after 29s
- tests/test_catalog.py: 5 unit tests for PypiCatalog (fetch, cache, sort prefix, completions)
- tests/test_proxy.py: 5 unit tests for BicepProxy (framing, injection, list result, passthrough)
- tests/conftest.py: pytest asyncio_mode=auto config
- scripts/smoke_test.sh: end-to-end TCP + health smoke test script
- .gitea/workflows/ci.yml: split into test + build-and-deploy jobs (test blocks deploy)
  - runs-on: debian-host (was ubuntu-latest = broken)
  - test job installs deps + runs pytest before building image
- pyproject.toml: [project.optional-dependencies] dev = pytest + pytest-asyncio
2026-05-10 12:38:41 +02:00
Henrik Jess Nielsen
d8536468ab feat: initial iLSP project scaffolding
Some checks failed
CI / deploy (push) Has been cancelled
CI / build-and-push (push) Has been cancelled
- Python LSP (pylsp + pylsp_i80 plugin): i80 pypi package completions
- Bicep LSP (asyncio TCP proxy → Bicep.LangServer.dll): LRU module injection
- Health HTTP endpoint (:2089) for Consul/Nomad checks
- Startup catalog fetch from pypi-server.i80.dk + DevOpsMCP (no volume needed)
- Multi-stage Dockerfile: downloads Bicep LS at build time, dotnet-runtime-8.0 + python3.12
- Nomad job: static TCP ports 2087/2088, health check on 2089
- Gitea Actions CI: build + push + deploy pipeline
- Editor configs: Helix / nvim / LSP4IJ / VS Code
2026-05-10 12:23:05 +02:00
Henrik Jess Nielsen
e8708191f6 First run 2026-05-10 12:16:38 +02:00
Henrik Jess Nielsen
3db41fb251 first commit 2026-05-10 12:13:32 +02:00