Fix broken mix_release build script #1104
Labels
No labels
1week
2weeks
Failed compliance check
IP cameras
NATS
Possible security concern
Review effort 1/5
Review effort 2/5
Review effort 3/5
Review effort 4/5
Review effort 5/5
UI
aardvark
accessibility
amd64
api
arm64
auth
back-end
bgp
blog
bug
build
checkers
ci-cd
cleanup
cnpg
codex
core
dependencies
device-management
documentation
duplicate
dusk
ebpf
enhancement
eta 1d
eta 1hr
eta 3d
eta 3hr
feature
fieldsurvey
github_actions
go
good first issue
help wanted
invalid
javascript
k8s
log-collector
mapper
mtr
needs-triage
netflow
network-sweep
observability
oracle
otel
plug-in
proton
python
question
reddit
redhat
research
rperf
rperf-checker
rust
sdk
security
serviceradar-agent
serviceradar-agent-gateway
serviceradar-web
serviceradar-web-ng
siem
snmp
sysmon
topology
ubiquiti
wasm
wontfix
zen-engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
carverauto/serviceradar#1104
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Imported from GitHub.
Original GitHub issue: #3028
Original author: @marvin-hansen
Original URL: https://github.com/carverauto/serviceradar/issues/3028
Original created: 2026-03-11T04:23:28Z
Currently all Elixir targets are built with a completely broken mix release custom build target that bypassed Bazels dependency management, cache management, and basically works like a giant bash script that blocks parallel build execution. A hot fix can be applied but that would violate Bazels hermetic philosophy.
Instead, a complete refactoring of all Elixir targets would be necessary to resolve this issue and eliminate the broken rule. below a high-level AI generated outline of the refactoring task.
And notice there are some caveats here:
Therefore, the outline servers more as a road map and needs refinement for each stage to add the missing details.
How to Build a Proper Bazel-native Elixir Setup
If we were to eliminate the monolithic mix_release script and convert the Elixir (web-ng) project into a proper, idiomatic Bazel build structure, we would need to decompose the pipeline into isolated, granular Bazel targets.
Here is exactly what needs to be done and why:
1. Manage Dependencies via Bazel (Workspace/Bzlmod)
Currently, mix deps.get dynamically resolves and downloads dependencies during the build action. This violates hermeticity.
The Solution: We must translate the mix.lock file into Bazel repository rules. Tools like rules_beam or custom macros would read mix.lock during the workspace evaluation phase and fetch every Hex package and Git repository as an external Bazel workspace (e.g., @hex_jason//:pkg).
Why: This ensures Bazel knows about every downloaded file before the action phase begins, allowing it to accurately track input hashes and cache the downloaded archives globally.
2. Granular BEAM Compilation (rules_elixir / rules_beam)
Currently, mix deps.compile compiles the entire universe sequentially inside one action.
The Solution: We must use (or write) Bazel rules that compile Elixir code package-by-package. Each dependency becomes a discrete elixir_library Bazel target. The core web-ng app would also be an elixir_library that explicitly depends on the compiled .beam outputs of its dependencies.
Why: This leverages Bazel's core strength: incremental caching and parallelism. If you change one file in web-ng, Bazel immediately knows that the 100 dependencies haven't changed. It instantly reuses the cached .beam files for the dependencies and only recompiles web-ng. Because the actions are small and isolated, they are perfectly hermetic and can be distributed across remote executors.
3. Decouple Rustler NIF Compilation
Currently, Elixir dependencies that use Rust (via rustler) spawn Cargo commands internally during mix deps.compile.
The Solution: We extract the compilation of Rust code into native rust_library and rust_shared_library targets using rules_rust. The resulting .so or .dylib files are then passed as data attributes to the elixir_library that needs them.
Why: rules_rust already handles Cargo dependency resolution, rustc invocation, and Bazel caching perfectly. By separating this from Mix, we avoid the massive Cargo bootstrapping overhead we saw earlier, and we let Bazel orchestrate the C toolchains and cross-compilation hermetically.
4. Decouple Frontend Asset Compilation (rules_js)
Currently, mix release pauses to run bun install, tailwind, and esbuild.
The Solution: We define the JS/CSS build using standard Bazel rules (like aspect_rules_js / aspect_rules_ts). We create targets for npm_install, tailwind bundling, and esbuild bundling.
Why: Frontend bundling is notorious for having massive dependency trees (node_modules). By isolating this to rules_js, Bazel can aggressively cache the Webpack/Vite/Esbuild outputs. Changes to an .ex file won't invalidate the CSS compile step, and changes to a .css file won't trigger Elixir recompilation.
5. The Final Assembly (Release Generation)
The Solution: Once all the discrete pieces are built by Bazel (Elixir .beam files, Rust NIF .so files, and static CSS/JS assets), a final elixir_release rule simply stitches them together. It takes all those pre-compiled artifacts and invokes standard Erlang/Mix release scripts purely to generate the release directory layout and boot scripts.
Why: At this stage, absolutely no compilation happens. It is a purely I/O bound operation that runs in milliseconds. It reads the inputs Bazel provides and spits out the serviceradar-web-ng-release.tar.gz.
Imported GitHub comment.
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/3028#issuecomment-4040225808
Original created: 2026-03-11T15:54:00Z
fixed in PR #3027