Skip to content

Commit 08a231f

Browse files
committed
feat: use closest available engine version for the project node
1 parent 8580b28 commit 08a231f

File tree

8 files changed

+186
-16
lines changed

8 files changed

+186
-16
lines changed

.github/workflows/release.yml

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
release_created: ${{ steps.release.outputs.release_created }}
1818
tag_name: ${{ steps.release.outputs.tag_name }}
1919
steps:
20-
id: release
20+
id: release
2121

2222
draft:
2323
name: draft
@@ -59,9 +59,64 @@ jobs:
5959
GITHUB_TOKEN: ${{ secrets.token }}
6060
run: gh release upload ${{ needs.release.outputs.tag_name }} ./apps/expert/burrito_out/*
6161

62+
build_engine:
63+
needs: [release, draft]
64+
runs-on: ubuntu-latest
65+
name: build engine
66+
strategy:
67+
matrix:
68+
include:
69+
- elixir: "1.18.1"
70+
otp: "27"
71+
- elixir: "1.18.1"
72+
otp: "26"
73+
- elixir: "1.17"
74+
otp: "27"
75+
- elixir: "1.17"
76+
otp: "26"
77+
- elixir: "1.17"
78+
otp: "25"
79+
- elixir: "1.16"
80+
otp: "26"
81+
- elixir: "1.16"
82+
otp: "25"
83+
- elixir: "1.15.8"
84+
otp: "26"
85+
- elixir: "1.15.8"
86+
otp: "25"
87+
steps:
88+
- name: Checkout
89+
uses: actions/checkout@v4
90+
91+
- name: Set up Elixir
92+
uses: erlef/setup-beam@v1
93+
with:
94+
otp-version: ${{ matrix.otp }}
95+
elixir-version: ${{ matrix.elixir }}
96+
97+
- name: Cache deps
98+
id: cache-deps
99+
uses: actions/cache@v3
100+
with:
101+
path: |
102+
apps/**/deps
103+
apps/**/_build
104+
105+
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('apps/**/mix.lock') }}
106+
restore-keys: |
107+
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
108+
109+
- name: Build Engine
110+
run: make build.engine.zip
111+
112+
- name: Upload Engine
113+
env:
114+
GITHUB_TOKEN: ${{ secrets.token }}
115+
run: gh release upload ${{ needs.release.outputs.tag_name }} ./apps/engine/engine-*.zip
116+
62117
publish:
63118
name: publish
64-
needs: [release, draft, build]
119+
needs: [release, draft, build, build_engine]
65120
runs-on: ubuntu-latest
66121
env:
67122
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ dialyzer.poncho: compile.poncho compile.protocols.poncho
7373
build.engine:
7474
cd apps/engine && mix deps.get && MIX_ENV=dev mix build
7575

76+
build.engine.zip:
77+
cd apps/engine && mix deps.get && MIX_ENV=dev mix build --zip
78+
7679
release: build.engine
7780
cd apps/expert &&\
7881
mix deps.get &&\
@@ -81,7 +84,7 @@ release: build.engine
8184
release.local: build.engine
8285
cd apps/expert &&\
8386
mix deps.get &&\
84-
EXPERT_RELEASE_MODE=burrito BURRITO_TARGET=$(local_target) MIX_ENV=prod mix release expert --overwrite
87+
EXPERT_RELEASE_MODE=burrito BURRITO_TARGET=$(local_target) MIX_ENV=dev mix release expert --overwrite
8588

8689
release.plain: build.engine
8790
cd apps/expert &&\

apps/engine/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ engine-*.tar
2424

2525
# Temporary files, for example, from tests.
2626
/tmp/
27+
28+
# Ignore the package zip built via `mix build --zip`.
29+
engine-*.zip

apps/engine/.tool-versions

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
elixir 1.17.2

apps/engine/lib/mix/tasks/build.ex

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
11
defmodule Mix.Tasks.Build do
22
use Mix.Task
33

4-
def run(_args) do
5-
Mix.Task.run("compile", [])
4+
@options [
5+
strict: [
6+
path: :string,
7+
zip: :boolean
8+
]
9+
]
10+
11+
def run(args) do
12+
{opts, _, _} = OptionParser.parse(args, @options)
13+
default_path = Path.join(["_build", "package", "engine"])
14+
package_root = Keyword.get(opts, :path, default_path)
615

7-
namespaced_dir = "_build/#{Mix.env()}_ns"
16+
Mix.Task.run("compile", [])
817

918
# Remove the existing namespaced dir
10-
File.rm_rf(namespaced_dir)
19+
File.rm_rf(package_root)
1120
# Create our namespaced area
12-
File.mkdir_p(namespaced_dir)
21+
File.mkdir_p(package_root)
1322

1423
# Move our build artifacts from safekeeping to the build area
15-
File.cp_r!("_build/#{Mix.env()}", namespaced_dir)
24+
File.cp_r!("_build/#{Mix.env()}", package_root)
1625

1726
# Namespace the new code
18-
Mix.Task.run(:namespace, [
19-
namespaced_dir
20-
])
27+
Mix.Task.run(:namespace, [package_root])
28+
29+
if Keyword.get(opts, :zip, false) do
30+
zip(package_root)
31+
File.rm_rf(package_root)
32+
end
33+
end
34+
35+
defp zip(package_root) do
36+
package_name = Path.basename(package_root)
37+
versions = Forge.VM.Versions.current()
38+
zip_output = Path.join(File.cwd!(), "engine-#{versions.elixir}-otp-#{versions.erlang}.zip")
39+
40+
package_root
41+
|> Path.dirname()
42+
|> File.cd!(fn ->
43+
System.cmd("zip", ["-r", zip_output, package_name])
44+
end)
45+
46+
IO.puts("Engine package created at: #{zip_output}")
2147
end
2248
end

apps/expert/lib/expert.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ defmodule Expert do
2424

2525
@dialyzer {:nowarn_function, apply_to_state: 2}
2626

27+
@expert_vsn Expert.MixProject.project()[:version]
28+
29+
def vsn, do: @expert_vsn
30+
2731
def get_lsp, do: :persistent_term.get(:expert_lsp, nil)
2832

2933
def start_link(args) do

apps/expert/lib/expert/engine_node.ex

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ defmodule Expert.EngineNode do
115115
bootstrap_args = [project, Document.Store.entropy(), all_app_configs()]
116116

117117
with {:ok, node_pid} <- EngineSupervisor.start_project_node(project),
118-
:ok <- start_node(project, glob_paths()),
118+
:ok <- start_node(project, glob_paths(project)),
119119
:ok <- :rpc.call(node_name, Engine.Bootstrap, :init, bootstrap_args),
120120
:ok <- ensure_apps_started(node_name) do
121121
{:ok, node_name, node_pid}
@@ -152,7 +152,7 @@ defmodule Expert.EngineNode do
152152
["/**/priv" | app_globs]
153153
end
154154

155-
def glob_paths do
155+
def glob_paths(_) do
156156
for entry <- :code.get_path(),
157157
entry_string = List.to_string(entry),
158158
entry_string != ".",
@@ -165,9 +165,87 @@ defmodule Expert.EngineNode do
165165
# separately and copied to expert's priv directory.
166166
# When Engine is built in CI for a version matrix, we'll need to check if
167167
# we have the right version downloaded, and if not, we should download it.
168-
defp glob_paths do
168+
defp glob_paths(%Project{} = project) do
169+
{:ok, elixir, _} = Expert.Port.elixir_executable(project)
170+
171+
{output, _} = System.cmd(elixir, ["-e", get_versions_code()])
172+
173+
case output |> String.trim() |> String.split("-") do
174+
[elixir_vsn, erlang_vsn] ->
175+
Logger.info(
176+
"Detected project Elixir version: #{elixir_vsn}, Erlang version: #{erlang_vsn}"
177+
)
178+
179+
find_engine_build(elixir_vsn, erlang_vsn)
180+
181+
_ ->
182+
Logger.warning(
183+
"Failed to detect Elixir and Erlang versions, using default Engine build"
184+
)
185+
186+
default_glob_paths()
187+
end
188+
end
189+
190+
defp get_versions_code() do
191+
~S"""
192+
major = :otp_release |> :erlang.system_info() |> List.to_string()
193+
version_file = Path.join([:code.root_dir(), "releases", major, "OTP_VERSION"])
194+
195+
erlang_vsn =
196+
try do
197+
{:ok, contents} = File.read(version_file)
198+
String.split(contents, "\n", trim: true)
199+
else
200+
[full] -> full
201+
_ -> major
202+
catch
203+
:error ->
204+
major
205+
end
206+
207+
IO.puts("#{System.version()}-#{erlang_vsn}")
208+
"""
209+
end
210+
211+
defp find_engine_build(elixir_vsn, erlang_vsn) do
212+
user_data_path = :filename.basedir(:user_data, "Expert", %{version: Expert.vsn()})
213+
engine_path = Path.join([user_data_path, "engine", "*-otp-*"])
214+
215+
Logger.info("Looking for Engine build at: #{engine_path}")
216+
217+
candidates =
218+
for entry <- Path.wildcard(engine_path),
219+
versions = entry |> Path.basename(entry) |> String.split("-"),
220+
match?([_, "otp", _], versions),
221+
[elixir, "otp", erlang] = versions,
222+
Version.match?(elixir, "~> #{elixir_vsn}"),
223+
Version.match?(erlang, "~> #{erlang_vsn}") do
224+
entry
225+
end
226+
227+
case List.first(candidates) do
228+
nil ->
229+
Logger.warning(
230+
"No matching Engine build found for Elixir #{elixir_vsn} and Erlang #{erlang_vsn}"
231+
)
232+
233+
default_glob_paths()
234+
235+
engine_path ->
236+
Logger.info("Using Engine build at: #{engine_path}")
237+
ebin_paths(engine_path)
238+
end
239+
end
240+
241+
defp default_glob_paths do
169242
:expert
170243
|> :code.priv_dir()
244+
|> ebin_paths()
245+
end
246+
247+
defp ebin_paths(base_path) do
248+
base_path
171249
|> Path.join("lib/**/ebin")
172250
|> Path.wildcard()
173251
end

apps/expert/lib/expert/release.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Expert.Release do
44

55
engine_path = Path.expand("../../../engine", __DIR__)
66

7-
source = Path.join([engine_path, "_build/dev_ns"])
7+
source = Path.join([engine_path, "_build/package/engine"])
88

99
dest =
1010
Path.join([

0 commit comments

Comments
 (0)