Repository navigation
Description
Blocked on: bazelverse/rules_elixir#2, bazelverse/rules_elixir#3, bazelverse/rules_elixir#4 (open), and bazelverse/rules_erlang#18 for the tree patch. Bot unblock of 2026-11-05 was a false positive (#7581).
Problem
patches/rules_elixir/hermetic_elixir.patch is applied to the pinned bazelverse/rules_elixir (RULES_ELIXIR_COMMIT = 8e13ada..., equal to upstream main today) by the repository rule in elixir/toolchain/repos.bzl:124. It mixes four changes. The fork's issues are disabled, so the only channel is pull requests (maintainer: Marvin Hansen). Split it into separate, tested PRs, and keep in the rules_dx patch only what does not land.
One hazard in the current patch: it renames the internal targets of elixir_app for every user (beam_files becomes <name>_beam_files, ebin becomes <name>_ebin, and so on, even for the default name). An upstream PR must not rename anything for existing users. The name default must stay erlang_app, and with that default the generated target names, output directories (beam_files, ebin, unconsolidated) and the final erlang_app target must be exactly what they are today.
The patch does not reach consumers directly: rules_dx applies it inside its own repo rule, so it is not part of #2283's consumer bug.
Where
patches/rules_elixir/hermetic_elixir.patch,patches/rules_elixir/BUILD.bazelelixir/toolchain/pins.bzl(RULES_ELIXIR_COMMIT,RULES_ELIXIR_SHA256,RULES_ELIXIR_URL),elixir/toolchain/repos.bzl:68,124,elixir/toolchain/elixir.bzl:6(tree)elixir/rules/defs.bzl:73-90: rules_dx'selixir_appwrapper that already forwardstarget_compatible_with;elixir/README.md:52(says--warnings-as-errors);elixir/tests/negative/negative_test.rs:268,282(the warnings-as-errors message)- Upstream files (bazelverse/rules_elixir main):
elixir_app.bzl:7-100,private/elixir_bytecode.bzl:55-90,private/elixir_build.bzl:15-24(ElixirInfo),private/elixir_toolchain.bzl:28-70; CI in.github/workflows/test.yaml(bazelisk test //...intest/, plus! bazelisk test //:failing_teststyle negative checks)
Fix
Separate PRs, each from a fresh branch on upstream main, each with its own test.
elixir_appgetsname,target_compatible_with,testonlyandtags. Rules:name = "erlang_app"by default; whennameis the default, keep the old internal names and output dirs; for any other name, prefix internal targets and output dirs withname, so two apps can live in one package. Forwardtarget_compatible_with,testonlyandtagsto every generated target. Test (intest/): the default call still produces:erlang_app,:beam_files,:ebin; a secondelixir_app(name = "other", ...)in the same package builds;target_compatible_withincompatible skips the target.- Pass
depstoapp_file:app_file(..., deps = deps)so the.appfile lists dependency applications. Todayelixir_appacceptsdepsbut does not hand them toapp_file. Test: build an app with a dep and check the.appapplicationslist. - Warnings as errors. First check whether the problem exists on upstream main: build a source with a warning and
elixirc_opts = ["--warnings-as-errors"]with Elixir 1.20 and see whether the build fails. The patch runselixir +elixirc, captures stderr, grepswarning:and fails withCompilation failed due to warnings while using the --warnings-as-errors option; it also dropsset -x. If upstream already fails correctly, drop this PR and say so. If not, make the smallest change that fails the build, and test with a failing example next totest/test/failing_test.exsusing the negative-check pattern in CI. - Relocatable
treesupport, depends on rules_erlang PR 4 (see the rules_erlang split issue). AddtreetoElixirInfoinprivate/elixir_build.bzlwith a default (the patch adds a bare field, which is absent when a constructor does not pass it; read it withgetattras the patch does, or passtree = Noneinelixir_build.bzl).private/elixir_toolchain.bzl:elixir_dirs,erlang_dirsandmaybe_install_erlangusetreewhen set. Use the same neutral variable name as the rules_erlang PR, not aDX_*name. Open this PR only after the rules_erlang PR is merged and the pin includes it. Test: toolchain fixture withtreeset builds one.exfile. - For each PR: run
bazelisk test //...intest/and the negative checks from CI in the upstream clone and include the output. Draft the PR title and body. No issue text is needed because issues are disabled. - After a PR lands: bump
pins.bzl, regeneratehermetic_elixir.patchwithout the landed hunks, runelixir/tests. Delete the patch andpatches/rules_elixir/when nothing is left.
Upstream rules (read first)
- Do not open, edit or comment on any upstream issue or PR, and do not push to any upstream fork. The maintainer submits everything personally.
- Your output is ready-to-submit material, posted as one comment on this issue: the diff against upstream main (
git format-patchor a fenced diff), the test output from the upstream repo, and the drafted upstream issue and PR text. - Work in a fresh clone of the upstream repo at its current main. Re-check that no equivalent change or open PR exists before drafting.
- Write the upstream text in plain, short sentences. State the problem, the change and how it was tested. Do not paste rules_dx internals.
- The upstream fork has no issue tracker. Do not message the maintainer either.
Done when
- A comment on this issue holds, per PR: the diff against upstream main, the passing test output from the upstream clone, and the PR text. Up to four PRs, or a note saying why one was dropped (for example warnings-as-errors already works).
- With
name = "erlang_app"the generated target names are unchanged, shown by a test. - No upstream PR was opened.
- After the PRs land: the rules_dx patch holds only what did not land, or is gone.
Related
#2283. rules_erlang split issue (the tree PR here waits for its PR 4).
Activity
TrapsterDK commentedon Oct 27, 2026
Upstream PR material for bazelverse/rules_elixir
There are four PRs, each one commit on upstream
main. Nothing was opened, pushed or commented upstream.Setup
bazelverse/rules_elixirat8e13ada. This isRULES_ELIXIR_COMMITand still upstreammain.gh pr list --state allshows only the release PR Drill: preset update-loop proof (skew, red test, regen, review, verify) #1, so no equivalent change exists..bazelversion).PATH, withERLANG_HOMEexported, as the CI does. The OTP 28.5 / Elixir 1.19.5 CI pair was not run as a whole. Elixir 1.19.5's warning output and its--warnings-as-errorsexit status were checked by hand (run on OTP 29).~/.bazelrcwas skipped (--nohome_rc), so nothing ran remotely.main, intest/://...passes 4/4, thetwo_file_testlog grep matches, andfailing_test,broken_testandempty_testfail as expected.Elixir bug behind PR 3
elixirc --warnings-as-errorsexits 0 when there are warnings in every released 1.19 and 1.20 that was checked (1.19.5, 1.20.4). Elixir fixed it on main in de015a25d4, which is not backported. So PR 3 is needed and was not dropped. With a fixed Elixir, its check does nothing.Order
MODULE.bazelfiles point at it.Each PR applies alone to
main(git apply --check). Withgit am -3, all four apply together in the order 1, 2, 3, 4. PR 1 and PR 2 change the sameapp_file(...)call, so PR 2 needs a trivial 3-way merge after PR 1. The resolved call is:With all four applied (and the local rules_erlang override for PR 4),
test///...passes 10/10, the negative checks fail as expected, and each//warnings:*target fails with the message once.PR 2:
fix(elixir_app): pass deps to app_filePR text
Title: fix(elixir_app): pass deps to app_file
Problem
elixir_appacceptsdepsand compiles against them, but does not pass them toapp_file. The generated.appfile lists onlykernel,stdlib,elixirandextra_appsinapplications, so OTP does not start the dependency applications first.Change
Pass
depstoapp_file. One line. The.appfile now lists the dependency apps inapplications, so OTP starts them before the app.Test
test/app_deps: an app that depends on a second app, and ash_testthat checks theapplicationslist of its.appfile. Onmainit fails with{applications,[kernel,stdlib,elixir]}; with the fix it is{applications,[kernel,stdlib,elixir,dep_app]}.test/ output (bazelisk test //..., CI checks, extra checks)
(cached)means the same tree had passed earlier in that output base.exit=3 (expected non-zero)is the CI's! bazelisk testcheck.git format-patch against main (pr2-app-file-deps)
PR 1:
feat(elixir_app): add name, target_compatible_with, testonly and tagsPR text
Title: feat(elixir_app): add name, target_compatible_with, testonly and tags
Problem
elixir_appalways creates the targetsbeam_files,app_file,ebin,elixir_without_app_overlapanderlang_app, with the output dirsbeam_files,ebinandunconsolidated. Two apps cannot share a package, and the generated targets cannot be marked incompatible, testonly or tagged.Change
name, default"erlang_app". With the default, every target name and output dir stays exactly as it is today.<name>_beam_files,<name>_app_file,<name>_ebinand<name>_elixir_without_app_overlap, and the output dirs are<name>/beam_files,<name>/ebinand<name>/unconsolidated.target_compatible_with,testonlyandtags, passed to all five generated targets.Test
In
test/:app_targets_test(build_test) builds:erlang_app,:beam_files,:app_file,:ebin,:elixir_without_app_overlapand the five targets of a second app namedother.app_names_testchecks the output dirs:beam_files,ebin,unconsolidatedfor the default app (no prefix), andother/beam_files,other/ebin,other/unconsolidated.otheristestonly. Iftestonlywere not passed to every target, its final target would fail analysis.incompatible_apphastarget_compatible_with = ["@platforms//:incompatible"].//...skips it, and a new CI step checks thatbazelisk build //:incompatible_appfails.test/MODULE.bazelgainsbazel_skylibandplatforms.On
mainthe package does not load, becauseelixir_apphas nonameargument.test/ output (bazelisk test //..., CI checks, extra checks)
(cached)means the same tree had passed earlier in that output base.exit=3 (expected non-zero)is the CI's! bazelisk testcheck.git format-patch against main (pr1-elixir-app-name)
PR 3:
fix(elixir_bytecode): fail the build on warnings with --warnings-as-errorsPR text
Title: fix(elixir_bytecode): fail the build on warnings with --warnings-as-errors
Problem
In released Elixir,
elixirc --warnings-as-errorsexits 0 when there are warnings.Kernel.CLIpasses the option toKernel.ParallelCompiler, which no longer reads it. This was checked with 1.20.4 and 1.19.5.test/BUILD.bazelalready passes--warnings-as-errorsto thebasicapp, but nothing checks that it fails, so this went unnoticed.Elixir fixed it on main in de015a25d4 ("Fail elixirc on warnings with --warnings-as-errors"). That commit is not in a release yet.
Change
--warnings-as-errorsis inelixirc_opts, the ELIXIRC action captures stderr, prints it again, and fails if elixirc exited 0 but printed awarning:line. It printsCompilation failed due to warnings while using the --warnings-as-errors option, the same message as Mix and the fixed elixirc.+fnu) is not a compiler warning and does not count. Without this exclusion, thebasicapp intest/fails under--incompatible_strict_action_env.bin/elixircis still used. It execselixir +elixirc.set -xis removed. It wrote a trace of the elixirc command to stderr for every action, and Bazel printed it even when the build succeeded.Test
test/warnings: threeelixir_bytecodetargets with--warnings-as-errors, taggedmanual. They cover an unused variable, a call to an undefined function (reported after the module compiles), andIO.warnat compile time (printed without indentation).if, because! cmddoes not stop abash -estep. (The existing! bazelisk testlines have the same issue: only the last one decides the step's status.)//warnings:allowedcompiles the unused-variable source without the option and builds as part of//....main, all three targets build successfully.test/ output (bazelisk test //..., CI checks, extra checks)
(cached)means the same tree had passed earlier in that output base.exit=3 (expected non-zero)is the CI's! bazelisk testcheck.git format-patch against main (pr3-warnings-as-errors)
PR 4:
feat(ElixirInfo): support a relocatable treePR text
Title: feat(ElixirInfo): support a relocatable tree
Submit after the rules_erlang PR "feat(OtpInfo): add an optional relocatable tree" is merged, and
MODULE.bazelandtest/MODULE.bazelpoint at a rules_erlang that contains it. It loadsTREE_ERLANG_HOMEfrom@rules_erlang//tools:erlang_toolchain.bzl.Problem
A toolchain can only provide elixir as an absolute
elixir_homeor as arelease_dir, and erlang as an absoluteerlang_homeor a release tar. An elixir and erlang that live inside the build cannot be used as plain inputs and runfiles.Change
ElixirInfogets an optional, documentedtreefield: a depset of every file of a relocatable elixir whose root directory holdsversion_file. The fields are now a dict with docs. The threeElixirInfo(...)calls in this repository passtree = None, and the field is read withgetattr, so other providers do not need it.private/elixir_toolchain.bzl, the same way as rules_erlang does forOtpInfo.tree:erlang_dirs: withOtpInfo.treeset, returnsTREE_ERLANG_HOMEand runfiles withversion_fileand the tree.maybe_install_erlang: assigns that variable from the directory of the OTPversion_file(path in actions, short path in tests) instead of unpacking a tar.elixir_dirs: withElixirInfo.treeset, returns the directory of the elixirversion_fileand runfiles withversion_fileand the tree.Test
test/treeuses a fake toolchain. Its erlang and elixir are stub scripts in the package.elixirc_with_tree_test(analysis): the ELIXIRC action derives both homes, has every tree file as input, and unpacks no tar.ex_unit_with_tree_test(analysis): the same checks for theex_unit_testscript, using short paths and runfiles.elixirc_runs_from_tree_testbuilds a.exfile with the stubelixircand checks thatelixircanderlran from the tree.To run these before the rules_erlang PR merges,
test/MODULE.bazelwas changed locally (not part of this patch). The change was agit_overrideof rules_erlang at13977b3with that PR'sgit format-patchinpatches.test/ output (bazelisk test //..., CI checks, extra checks)
(cached)means the same tree had passed earlier in that output base.exit=3 (expected non-zero)is the CI's! bazelisk testcheck.git format-patch against main (pr4-elixir-tree)
Full series
test/ output with PRs 1–4 applied
Coverage of
hermetic_elixir.patchEvery hunk is covered.
elixir_app.bzlname,target_compatible_with,testonly,tagselixir_app.bzldeps = depsonapp_fileprivate/elixir_bytecode.bzlwarnings-as-errors, dropset -xprivate/elixir_build.bzltree,private/elixir_toolchain.bzlIntentional differences from the previous patch:
erlang_appkeeps the old target names and output dirs. The old patch renamed them for every user. Non-default names are unchanged.bin/elixircinstead ofelixir +elixirc.warning:.awkcall instead of agreppipe.TREE_ERLANG_HOMEis loaded from rules_erlang instead of copying theRULES_ERLANG_HOMEstring.erlang_dirsreturns that constant.version_file.ElixirInfofields are documented.tree = None.rules_dx side
#4091 replaces
hermetic_elixir.patchwith the combined diff of these four PRs, without their tests. It is stacked on #4072, because PR 4 loadsTREE_ERLANG_HOME, which #4072 adds to the rules_erlang patch.After each upstream merge:
RULES_ELIXIR_COMMITandRULES_ELIXIR_SHA256.//elixir/tests/....When all four have merged, delete
patches/rules_elixir/and thepatchesattribute. When rules_dx pins a released Elixir that contains de015a25d4, PR 3's check is a no-op and can be dropped (or upstream can remove it).rules_elixir: align hermetic_elixir.patch with the four proposed upst…
rules_elixir: align hermetic_elixir.patch with the four proposed upst…
TrapsterDK commentedon Oct 28, 2026
Triage: blocked. Nothing is implementable now. The remaining steps wait on the maintainer submitting and landing four upstream bazelverse/rules_elixir PRs, which the issue forbids the worker from opening, and on rules_erlang PR 4 (#3743, still OPEN) for the tree PR. Not already fixed, since upstream has no equivalent change. Post-landing cleanup only: bump pins in elixir/toolchain/pins.bzl:15-19, delete landed hunks from hermetic_elixir.patch, then remove patches/rules_elixir/ and the repos.bzl patch label when empty. The warnings-as-errors hunk stays needed until Elixir with de015a25d4 is pinned. Read-only; no changes made.
TrapsterDK commentedon Nov 4, 2026
Plan (agent): size S (after landing)
Decision: keep blocked on one owner step. The four upstream PRs are drafted (comment of 2026-10-27) and
hermetic_elixir.patchalready matches them (514fb11, #4091). The only thing left is submitting them. Rejected: an agent opens the PRs (the issue body forbids it, the owner submits personally); splitting the local patch into four files now (churn, cleanup is the same one regen per landing); dropping the patch (consumers lose the fixes).Cause: nothing landed upstream. bazelverse/rules_elixir main is still 8e13ada (=
RULES_ELIXIR_COMMIT, elixir/toolchain/pins.bzl:19) and its only PR is the release PR #1. The 2026-10-27 diffs still apply.Change (per PR, after it merges):
RULES_ELIXIR_COMMIT,RULES_ELIXIR_SHA256.exports_filesentry in patches/rules_elixir/BUILD.bazel and its label in elixir/toolchain/repos.bzl:180.toolchain_type_alias.patchis rules_dx only and stays.Tests:
tools/dx test //elixir/...Host: none (the pin bump runs on the macos_arm64 cell in CI)
Upstream: PR 2, PR 1, PR 3 to bazelverse/rules_elixir now. PR 4 after the rules_erlang tree PR (rules_erlang: split hermetic_otp.patch into four upstream PRs to bazelverse/rules_erlang #3743) lands.
Question for the owner: will you submit PR 2, PR 1 and PR 3 from the 2026-10-27 comment to bazelverse/rules_elixir now and paste the PR URLs here? Recommended: yes. PR 4 waits on #3743. When a PR merges, an agent does the pin bump and patch trim above.
TrapsterDK commentedon Nov 4, 2026
Owner decision: yes. Agents submit the drafted upstream PRs from this account (each ends with 'Assisted by Claude.'), then split our patch so each upstream merge is a pin bump plus deleting one patch file.
TrapsterDK commentedon Nov 4, 2026
Owner: before submitting each upstream PR, take extra care that it fits upstream: read their CONTRIBUTING and recent merged PRs, search for existing issues/PRs on the same thing, match their style and commit format, keep the diff minimal and focused, add a regression test in their test layout, and run their tests locally. Get a review pass before opening. Open the upstream issue first if they expect one.
TrapsterDK commentedon Nov 4, 2026
Opened upstream: bazelverse/rules_elixir#2 (deps to app_file), #3 (name/target_compatible_with/testonly/tags), #4 (warnings-as-errors). Each passes test/ //... plus CI negative checks locally on OTP 29.0 / Elixir 1.20.3. The tree PR waits on rules_erlang#18. Upstream issues are disabled. rules_dx patch split: #6702 (not merged). Part of #3744 remains: relocatable_tree PR and post-merge pin bumps.
patches/rules_elixir: split hermetic_elixir.patch per upstream PR (pa…
TrapsterDK commentedon Nov 5, 2026
Blocked: upstream bazelverse/rules_elixir PRs #2 (deps), #3 (name), #4 (warnings-as-errors) are all still OPEN, so no pin bump or patch deletion can land. Revisit after they merge.
github-actions commentedon Nov 5, 2026
Unblocked: #2, #3, #4 closed
TrapsterDK commentedon Nov 5, 2026
Still blocked. The 2026-11-05 unblock was a false positive: the blocked-issues job read bazelverse/rules_elixir#2, #3, #4 as local issues #2, #3, #4 (old, closed). Upstream PRs #2, #3, #4 are still open, rules_elixir main is still 8e13ada, and rules_erlang#18 is open. Restored the blocked label and rewrote the Blocked on line with full URLs. Parser bug tracked in #7581.
TrapsterDK commentedon Nov 6, 2026
Still blocked as of 2026-11-06: bazelverse/rules_elixir#2, /pull/3 and /pull/4 are open with no reviews. rules_elixir main is still 8e13ada, which equals RULES_ELIXIR_COMMIT. bazelverse/rules_erlang#18 is open. No Elixir release has de015a25d4, so warnings_as_errors.patch stays. Restored the blocked label.