Skip to content

Commit 9a5d6e3

Browse files
lukaszsamsonclaude
andcommitted
ci: hard-fail release gate; pin elixir_sense 702ff31a; enforce TypeHints facade boundary
- Release gate: the absolute-path-dep check no longer has continue-on-error — the path dependency was replaced by a git pin, so a reintroduction must fail the workflow, not just show a red job. Stale development-phase comments updated. - Pin elixir_sense to PR #334 head 702ff31a (Descr funnel delegates, property harness, short negative ExCk cache TTL, adaptor boundary test, TYPES_ROADMAP architecture contract). - Add inlay_hints_boundary_test.exs: the InlayHints provider must consume types only through the ElixirSense.Core.TypeHints facade — direct calls to Binding or TypePresentation fail the test (checked against compiled BEAM import tables). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NvwMWQayfdxowBDrnt5iYM
1 parent dee2c3b commit 9a5d6e3

4 files changed

Lines changed: 58 additions & 8 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ on:
99
- master
1010

1111
jobs:
12-
# Release gate: absolute local path dependencies must never ship. This is
13-
# EXPECTED to flag on the inlay-hints development branch (elixir_sense is a
14-
# local worktree path dep during development) — continue-on-error keeps the
15-
# branch green while documenting the blocker. Flip continue-on-error to
16-
# false before release.
12+
# Release gate: absolute local path dependencies must never ship. The
13+
# elixir_sense dependency is a git pin (dep_versions.exs), so this job
14+
# hard-fails on any reintroduction of a local path dep.
1715
release-gate:
1816
name: Reject absolute path deps (release gate)
1917
runs-on: ubuntu-22.04
20-
continue-on-error: true
2118
steps:
2219
- uses: actions/checkout@v4
2320
- name: Check for absolute local path dependencies
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
defmodule ElixirLS.LanguageServer.Providers.InlayHintsBoundaryTest do
2+
@moduledoc """
3+
Architecture boundary check: the inlay-hints provider must consume types
4+
exclusively through the `ElixirSense.Core.TypeHints` facade — never through
5+
`ElixirSense.Core.Binding` or `ElixirSense.Core.TypePresentation` directly.
6+
Those are elixir_sense internals; the facade is the only supported LSP-facing
7+
type API, so display trust levels, caching, and shape/descr translation stay
8+
in one place.
9+
10+
Enforced against compiled BEAM import tables (actual remote-call targets),
11+
so doc/comment mentions don't count.
12+
"""
13+
use ExUnit.Case, async: true
14+
15+
@forbidden_prefixes [
16+
"Elixir.ElixirSense.Core.Binding",
17+
"Elixir.ElixirSense.Core.TypePresentation"
18+
]
19+
20+
test "InlayHints provider only uses the TypeHints facade for type access" do
21+
provider_modules =
22+
for module <- Application.spec(:language_server, :modules),
23+
module
24+
|> Atom.to_string()
25+
|> String.starts_with?("Elixir.ElixirLS.LanguageServer.Providers.InlayHints"),
26+
do: module
27+
28+
assert provider_modules != [], "InlayHints provider modules not found in app spec"
29+
30+
offenders =
31+
for module <- provider_modules,
32+
target <- forbidden_call_targets(module),
33+
do: {module, target}
34+
35+
assert offenders == [],
36+
"Inlay-hints provider bypasses the TypeHints facade:\n" <>
37+
Enum.map_join(offenders, "\n", fn {m, {tm, tf, ta}} ->
38+
" #{inspect(m)} -> #{inspect(tm)}.#{tf}/#{ta}"
39+
end)
40+
end
41+
42+
defp forbidden_call_targets(module) do
43+
with path when is_list(path) <- :code.which(module),
44+
{:ok, {^module, [{:imports, imports}]}} <- :beam_lib.chunks(path, [:imports]) do
45+
Enum.filter(imports, fn {target_mod, _f, _a} ->
46+
target = Atom.to_string(target_mod)
47+
Enum.any?(@forbidden_prefixes, &String.starts_with?(target, &1))
48+
end)
49+
else
50+
_ -> []
51+
end
52+
end
53+
end

dep_versions.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[
2-
elixir_sense: "53f8879dcc496f1d19f5d3a5de721ca10b40824f",
2+
elixir_sense: "702ff31a601ddf4f04343db7e244719852586bfe",
33
dialyxir_vendored: "accfec9393079abc4a82b7e79a4997f59f085b67",
44
jason_v: "f1c10fa9c445cb9f300266122ef18671054b2330",
55
erl2ex_vendored: "04f93e55f46d35d0aa3c149616f2c7a6a1ad9311",

mix.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},
33
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
44
"dialyxir_vendored": {:git, "https://github.com/elixir-lsp/dialyxir.git", "accfec9393079abc4a82b7e79a4997f59f085b67", [ref: "accfec9393079abc4a82b7e79a4997f59f085b67"]},
5-
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "53f8879dcc496f1d19f5d3a5de721ca10b40824f", [ref: "53f8879dcc496f1d19f5d3a5de721ca10b40824f"]},
5+
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "702ff31a601ddf4f04343db7e244719852586bfe", [ref: "702ff31a601ddf4f04343db7e244719852586bfe"]},
66
"erl2ex_vendored": {:git, "https://github.com/elixir-lsp/erl2ex.git", "04f93e55f46d35d0aa3c149616f2c7a6a1ad9311", [ref: "04f93e55f46d35d0aa3c149616f2c7a6a1ad9311"]},
77
"erlex_vendored": {:git, "https://github.com/elixir-lsp/erlex.git", "50b8307f90451a5d0288fb239fb6405b5ca1f1a4", [ref: "50b8307f90451a5d0288fb239fb6405b5ca1f1a4"]},
88
"jason_v": {:git, "https://github.com/elixir-lsp/jason.git", "f1c10fa9c445cb9f300266122ef18671054b2330", [ref: "f1c10fa9c445cb9f300266122ef18671054b2330"]},

0 commit comments

Comments
 (0)