From 21e3b66f4e06943d17d78c10ab1c647a886b05d0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 11:00:17 +0000 Subject: [PATCH 01/38] Add AGENTS.md with cloud-specific development instructions Co-authored-by: Dominic Letz --- AGENTS.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..10c031d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ +# AGENTS.md + +## Cursor Cloud specific instructions + +### Overview + +This is a desktop TodoApp built with Elixir, Phoenix LiveView, and the `elixir-desktop` library. It renders UI via a native wxWidgets webview backed by a local Phoenix server on `127.0.0.1:30979`. Data is stored in SQLite at `~/.config/todo/database.sq3`. + +### Running the app + +```bash +. "$HOME/.asdf/asdf.sh" && cd /workspace && DISPLAY=:1 iex -S mix +``` + +The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for testing without needing the native window). + +### Key commands + +| Task | Command | +|------|---------| +| Install deps | `mix deps.get` | +| Compile | `mix compile` | +| Lint | `mix lint` | +| Tests | `mix test` | +| Asset build | `mix assets.deploy` | +| Run app (dev) | `iex -S mix` | + +### Non-obvious notes + +- **No external services required.** This is a fully self-contained desktop app — no Postgres, Redis, Docker containers, or external APIs. +- **DBus/EGL warnings are expected** in headless/cloud environments. The app logs errors about DBus (desktop notifications) and EGL (GPU acceleration) but these do not affect functionality. +- **wxWidgets is required** for the desktop window. In headless environments, ensure `DISPLAY=:1` is set and an X server (Xvfb) is running. +- **Version management uses asdf** with `.tool-versions` pinning Erlang 28.4.3, Elixir 1.19.5, and Node.js 22.19.0. +- **The `mix lint` alias** runs `compile --warnings-as-errors`, `format --check-formatted`, and `credo --ignore design`. +- **No test files exist** in the project currently — `mix test` compiles in the test environment but reports "There are no tests to run". +- **Assets** are built via esbuild (JS) and dart_sass (SCSS), both downloaded automatically by Mix on first run. From e1b64f449813d44184dc2bd7874457c9eedfc911 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:35:54 +0000 Subject: [PATCH 02/38] docs: fix SQLite spelling in TodoApp moduledoc Co-authored-by: Dominic Letz --- lib/todo_app.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/todo_app.ex b/lib/todo_app.ex index 6b28b68..0420307 100644 --- a/lib/todo_app.ex +++ b/lib/todo_app.ex @@ -2,7 +2,7 @@ defmodule TodoApp do @moduledoc """ TodoApp Application. This module takes care of the the boot. Because the TodoApp is a standalone desktop application there is - initial Database initialization needed when the SQlite database is + initial Database initialization needed when the SQLite database is not yet existing. This is done during start() by calling `TodoApp.Repo.initialize()`. From 218c22ecc16696e031c01a03574f30180f05659b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:37:33 +0000 Subject: [PATCH 03/38] AGENTS.md: clarify assets.deploy vs dev watchers, lint env, Git LFS Document production asset pipeline vs dev watchers, warn that Credo is not available in prod Mix env, and note Git LFS for Android .a libs. Co-authored-by: Dominic Letz --- AGENTS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 10c031d..7b3912f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,12 +25,15 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes | Asset build | `mix assets.deploy` | | Run app (dev) | `iex -S mix` | +`mix assets.deploy` minifies and digests static assets for production releases. In development, esbuild and dart_sass run as Mix watchers when you start the app with `iex -S mix` (see `mix.exs` `asset_apps/1`). + ### Non-obvious notes - **No external services required.** This is a fully self-contained desktop app — no Postgres, Redis, Docker containers, or external APIs. - **DBus/EGL warnings are expected** in headless/cloud environments. The app logs errors about DBus (desktop notifications) and EGL (GPU acceleration) but these do not affect functionality. - **wxWidgets is required** for the desktop window. In headless environments, ensure `DISPLAY=:1` is set and an X server (Xvfb) is running. - **Version management uses asdf** with `.tool-versions` pinning Erlang 28.4.3, Elixir 1.19.5, and Node.js 22.19.0. -- **The `mix lint` alias** runs `compile --warnings-as-errors`, `format --check-formatted`, and `credo --ignore design`. +- **The `mix lint` alias** runs `compile --warnings-as-errors`, `format --check-formatted`, and `credo --ignore design`. Run it with the default Mix environment (`dev`); Credo is only a dev/test dependency, so `MIX_ENV=prod mix lint` will fail on the Credo step. +- **Git LFS:** `*.a` files under `rel/android/` are tracked with Git LFS. After cloning, if those libraries look like small text files instead of archives, run `git lfs pull` before Android-related builds. - **No test files exist** in the project currently — `mix test` compiles in the test environment but reports "There are no tests to run". - **Assets** are built via esbuild (JS) and dart_sass (SCSS), both downloaded automatically by Mix on first run. From 9e337d4d1b507c209ce1990aee1d58ef88ebd82a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:40:40 +0000 Subject: [PATCH 04/38] Clarify AGENTS.md: DB path, dev run row, Android note - Document SQLite path via config dir and TodoApp.config_dir/0 - Point the run-app table row at the full asdf/DISPLAY dev command - Note rel/android uses its own OTP/Elixir pin per README Co-authored-by: Dominic Letz --- AGENTS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7b3912f..24a3e35 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ ### Overview -This is a desktop TodoApp built with Elixir, Phoenix LiveView, and the `elixir-desktop` library. It renders UI via a native wxWidgets webview backed by a local Phoenix server on `127.0.0.1:30979`. Data is stored in SQLite at `~/.config/todo/database.sq3`. +This is a desktop TodoApp built with Elixir, Phoenix LiveView, and the `elixir-desktop` library. It renders UI via a native wxWidgets webview backed by a local Phoenix server on `127.0.0.1:30979`. Data is stored in SQLite under the app config directory (`~/.config/todo/database.sq3` on Unix-like systems; see `TodoApp.config_dir/0` in `lib/todo_app.ex`). ### Running the app @@ -23,7 +23,7 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes | Lint | `mix lint` | | Tests | `mix test` | | Asset build | `mix assets.deploy` | -| Run app (dev) | `iex -S mix` | +| Run app (dev) | Same as [Running the app](#running-the-app): load asdf, set `DISPLAY`, then `iex -S mix` | `mix assets.deploy` minifies and digests static assets for production releases. In development, esbuild and dart_sass run as Mix watchers when you start the app with `iex -S mix` (see `mix.exs` `asset_apps/1`). @@ -37,3 +37,4 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes - **Git LFS:** `*.a` files under `rel/android/` are tracked with Git LFS. After cloning, if those libraries look like small text files instead of archives, run `git lfs pull` before Android-related builds. - **No test files exist** in the project currently — `mix test` compiles in the test environment but reports "There are no tests to run". - **Assets** are built via esbuild (JS) and dart_sass (SCSS), both downloaded automatically by Mix on first run. +- **Android sample** lives under `rel/android/` (separate Gradle project). It uses a pinned Erlang/Elixir pair for the embedded runtime that can differ from the desktop `.tool-versions`; read `rel/android/README.md` before building an APK. From b637d2e313ba393d6aa02f6fea31c6fcdcd07687 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:41:13 +0000 Subject: [PATCH 05/38] Fix CI: skip desktop installer for mobile releases; install NSIS for OTP Windows build Android APK builds failed because mix release still ran Desktop.Deployment.generate_installer/1 under MIX_TARGET=android. Release steps now assemble-only for :android and :ios. Windows Erlang installer_win32 failed because makensis was missing in WSL when building OTP; install nsis and symlink makensis.exe before otp_build. Bump the OTP executable cache key so CI rebuilds with the fixed toolchain. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 9 ++++++--- mix.exs | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 9f41624..342de75 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -23,7 +23,7 @@ jobs: id: win32-cache with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v2 - uses: Vampire/setup-wsl@v2 if: steps.win32-cache.outputs.cache-hit != 'true' @@ -32,7 +32,10 @@ jobs: - name: Install WSL dependencies if: steps.win32-cache.outputs.cache-hit != 'true' - run: apt update && apt install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip + run: | + apt update && apt install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip nsis + # otp_build installer_win32 invokes makensis.exe; Linux NSIS provides makensis. + ln -sf /usr/bin/makensis /usr/local/bin/makensis.exe - name: Install openssl if: steps.win32-cache.outputs.cache-hit != 'true' @@ -96,7 +99,7 @@ jobs: uses: actions/cache/save@v3 with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v2 - name: Run Erlang installer shell: cmd diff --git a/mix.exs b/mix.exs index 1512534..47d9f61 100644 --- a/mix.exs +++ b/mix.exs @@ -16,11 +16,7 @@ defmodule Todo.MixProject do releases: [ default_release: [ applications: [runtime_tools: :permanent, ssl: :permanent], - steps: [ - # &Desktop.Deployment.prepare_release/1, - :assemble, - &Desktop.Deployment.generate_installer/1 - ] + steps: release_steps() ] ] ] @@ -32,6 +28,23 @@ defmodule Todo.MixProject do ] end + # Desktop installers are only produced for desktop targets. Mobile releases + # (e.g. MIX_TARGET=android for the APK) must stop at :assemble; the + # installer step invokes tooling that is not available on CI and is not used + # for embedded zip packaging. + defp release_steps do + case Mix.target() do + target when target in [:android, :ios] -> + [:assemble] + + _ -> + [ + :assemble, + &Desktop.Deployment.generate_installer/1 + ] + end + end + # Specifies which paths to compile per environment. defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_), do: ["lib"] From 4abd6f1022a98829f9062675b9daaeb5cbeb6500 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:43:45 +0000 Subject: [PATCH 06/38] AGENTS.md: note CI uses different Elixir/OTP than .tool-versions Avoid confusion when agents compare local asdf pins to GitHub Actions. Co-authored-by: Dominic Letz --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 24a3e35..f7de629 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,7 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes - **No external services required.** This is a fully self-contained desktop app — no Postgres, Redis, Docker containers, or external APIs. - **DBus/EGL warnings are expected** in headless/cloud environments. The app logs errors about DBus (desktop notifications) and EGL (GPU acceleration) but these do not affect functionality. - **wxWidgets is required** for the desktop window. In headless environments, ensure `DISPLAY=:1` is set and an X server (Xvfb) is running. -- **Version management uses asdf** with `.tool-versions` pinning Erlang 28.4.3, Elixir 1.19.5, and Node.js 22.19.0. +- **Version management uses asdf** with `.tool-versions` pinning Erlang 28.4.3, Elixir 1.19.5, and Node.js 22.19.0. GitHub Actions (for example `.github/workflows/ci.yml`) still pins older Elixir/OTP for the default CI job; treat that file as the source of truth for CI, not `.tool-versions`. - **The `mix lint` alias** runs `compile --warnings-as-errors`, `format --check-formatted`, and `credo --ignore design`. Run it with the default Mix environment (`dev`); Credo is only a dev/test dependency, so `MIX_ENV=prod mix lint` will fail on the Credo step. - **Git LFS:** `*.a` files under `rel/android/` are tracked with Git LFS. After cloning, if those libraries look like small text files instead of archives, run `git lfs pull` before Android-related builds. - **No test files exist** in the project currently — `mix test` compiles in the test environment but reports "There are no tests to run". From 6407399c8acf21d1a65a23511ee1f0a4d32d80aa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:45:34 +0000 Subject: [PATCH 07/38] Android run_mix: mkdir assets dir before writing app.zip Fresh checkouts may omit rel/android/app/src/main/assets; ensure it exists before zip creates app.zip (fixes Android CI zip I/O error). Co-authored-by: Dominic Letz --- rel/android/app/run_mix | 1 + 1 file changed, 1 insertion(+) diff --git a/rel/android/app/run_mix b/rel/android/app/run_mix index 2cf3c6b..5d8696b 100755 --- a/rel/android/app/run_mix +++ b/rel/android/app/run_mix @@ -71,6 +71,7 @@ if [ ! -d "assets/node_modules" ]; then (cd assets && npm install) fi +mkdir -p "$(dirname "$APP_FILE")" rm -f "$APP_FILE" mix assets.deploy From 4eb7f1ad8baab240e0fc3cf053004239072ff127 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 12:44:57 +0000 Subject: [PATCH 08/38] docs(AGENTS): clarify prod asset build and npm install step - Document MIX_ENV=prod for mix assets.deploy so release builds match CI. - Point agents to npm install in assets/ per README before deploying assets. Co-authored-by: Dominic Letz --- AGENTS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f7de629..6aa6535 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,10 +22,10 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes | Compile | `mix compile` | | Lint | `mix lint` | | Tests | `mix test` | -| Asset build | `mix assets.deploy` | +| Asset build | `MIX_ENV=prod mix assets.deploy` | | Run app (dev) | Same as [Running the app](#running-the-app): load asdf, set `DISPLAY`, then `iex -S mix` | -`mix assets.deploy` minifies and digests static assets for production releases. In development, esbuild and dart_sass run as Mix watchers when you start the app with `iex -S mix` (see `mix.exs` `asset_apps/1`). +`MIX_ENV=prod mix assets.deploy` minifies and digests static assets for production releases. In development, esbuild and dart_sass run as Mix watchers when you start the app with `iex -S mix` (see `mix.exs` `asset_apps/1`). ### Non-obvious notes @@ -36,5 +36,5 @@ The web UI is accessible at http://127.0.0.1:30979/ in a browser (useful for tes - **The `mix lint` alias** runs `compile --warnings-as-errors`, `format --check-formatted`, and `credo --ignore design`. Run it with the default Mix environment (`dev`); Credo is only a dev/test dependency, so `MIX_ENV=prod mix lint` will fail on the Credo step. - **Git LFS:** `*.a` files under `rel/android/` are tracked with Git LFS. After cloning, if those libraries look like small text files instead of archives, run `git lfs pull` before Android-related builds. - **No test files exist** in the project currently — `mix test` compiles in the test environment but reports "There are no tests to run". -- **Assets** are built via esbuild (JS) and dart_sass (SCSS), both downloaded automatically by Mix on first run. +- **Assets** are built via esbuild (JS) and dart_sass (SCSS), both downloaded automatically by Mix on first run. Run `npm install` in `assets/` before `mix assets.deploy` if you have not yet (see the root `README.md`). - **Android sample** lives under `rel/android/` (separate Gradle project). It uses a pinned Erlang/Elixir pair for the embedded runtime that can differ from the desktop `.tool-versions`; read `rel/android/README.md` before building an APK. From 8575b395978914c5b4ef832aa2208b1744254433 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 13:27:48 +0000 Subject: [PATCH 09/38] CI (Windows): use WSL Ubuntu 22.04 for NSIS 3; extend job timeout OTP's installer_win32 requires NSIS 3.x; Ubuntu 18.04's nsis 2.51 is rejected ("Unsupported NSIS version"). Use Ubuntu-22.04 in setup-wsl and bump the OTP exe cache key. Increase the Windows job timeout for full OTP rebuilds. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 342de75..7592f96 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,7 +12,7 @@ env: jobs: windows: runs-on: windows-latest - timeout-minutes: 90 + timeout-minutes: 120 defaults: run: shell: wsl-bash {0} @@ -23,12 +23,13 @@ jobs: id: win32-cache with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v2 + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v3 - uses: Vampire/setup-wsl@v2 if: steps.win32-cache.outputs.cache-hit != 'true' with: - distribution: Ubuntu-18.04 + # Ubuntu 22.04 ships NSIS 3.x; OTP's installer_win32 rejects NSIS 2.x (Ubuntu 18.04). + distribution: Ubuntu-22.04 - name: Install WSL dependencies if: steps.win32-cache.outputs.cache-hit != 'true' @@ -99,7 +100,7 @@ jobs: uses: actions/cache/save@v3 with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v2 + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v3 - name: Run Erlang installer shell: cmd From a928006c40bf69363d2bd3bc62c1a66730b03667 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 14:04:25 +0000 Subject: [PATCH 10/38] CI (Windows): use Chocolatey NSIS + WSL wrapper for makensis.exe Linux makensis parses OTP's `/V2` as a script path (`Can't open script "/V2"`). Install NSIS on Windows and expose makensis.exe to WSL via a wrapper that execs the real Windows binary. Bump otp.exe cache key (v4). Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 7592f96..bdacde5 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -23,27 +23,43 @@ jobs: id: win32-cache with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v3 + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - uses: Vampire/setup-wsl@v2 if: steps.win32-cache.outputs.cache-hit != 'true' with: - # Ubuntu 22.04 ships NSIS 3.x; OTP's installer_win32 rejects NSIS 2.x (Ubuntu 18.04). + # Ubuntu 22.04 for up-to-date userland; OTP's installer_win32 invokes makensis.exe with + # Windows-style flags, so we install NSIS for Windows and expose it via /usr/local/bin/makensis.exe. distribution: Ubuntu-22.04 - name: Install WSL dependencies if: steps.win32-cache.outputs.cache-hit != 'true' run: | - apt update && apt install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip nsis - # otp_build installer_win32 invokes makensis.exe; Linux NSIS provides makensis. - ln -sf /usr/bin/makensis /usr/local/bin/makensis.exe + apt update && apt install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip - - name: Install openssl + - name: Install openssl and NSIS (Windows) if: steps.win32-cache.outputs.cache-hit != 'true' shell: cmd run: | - choco install openssl --version=1.1.1.2100 + choco install openssl --version=1.1.1.2100 -y + choco install nsis -y IF EXIST "c:\\Program Files\\OpenSSL-Win64" (move "c:\\Program Files\\OpenSSL-Win64" "c:\\OpenSSL-Win64") ELSE (move "c:\\Program Files\\OpenSSL" "c:\\OpenSSL-Win64") + + - name: Wire Windows NSIS into WSL PATH + if: steps.win32-cache.outputs.cache-hit != 'true' + run: | + NSIS="/mnt/c/Program Files (x86)/NSIS/makensis.exe" + if [ ! -f "$NSIS" ]; then + NSIS="/mnt/c/Program Files/NSIS/makensis.exe" + fi + if [ ! -f "$NSIS" ]; then + echo "Windows NSIS makensis.exe not found after choco install" >&2 + exit 1 + fi + install -d /usr/local/bin + # otp_build runs makensis.exe /V2 ... — Windows NSIS accepts those flags; Linux makensis does not. + printf '%s\n' '#!/bin/sh' "exec \"$NSIS\" \"\$@\"" > /usr/local/bin/makensis.exe + chmod +x /usr/local/bin/makensis.exe - name: Download wxWidgets if: steps.win32-cache.outputs.cache-hit != 'true' @@ -100,7 +116,7 @@ jobs: uses: actions/cache/save@v3 with: path: "c:\\opt\\otp.exe" - key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v3 + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - name: Run Erlang installer shell: cmd From 2fb7a1bc07191dd7c981f6e86d797804e2a33e4f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 14:16:56 +0000 Subject: [PATCH 11/38] CI (Windows): retry apt-get in WSL when mirrors are unreachable Transient archive.ubuntu.com failures caused mingw packages to be missing. Retry apt-get update/install up to 5 times with backoff. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index bdacde5..bd3d5c2 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -35,7 +35,19 @@ jobs: - name: Install WSL dependencies if: steps.win32-cache.outputs.cache-hit != 'true' run: | - apt update && apt install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip + set -euo pipefail + export DEBIAN_FRONTEND=noninteractive + for attempt in 1 2 3 4 5; do + if apt-get update && apt-get install -y g++-mingw-w64 gcc-mingw-w64 make autoconf unzip; then + break + fi + echo "apt-get failed (attempt $attempt), retrying in 45s..." >&2 + sleep 45 + if [ "$attempt" -eq 5 ]; then + echo "apt-get install failed after 5 attempts" >&2 + exit 1 + fi + done - name: Install openssl and NSIS (Windows) if: steps.win32-cache.outputs.cache-hit != 'true' From af21421bcda9e9f8be6fa7c6d57beffea0094b24 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 14:33:30 +0000 Subject: [PATCH 12/38] CI (Windows): xcopy OpenSSL to C:\OpenSSL-Win64 (avoid move access denied) Chocolatey + NSIS could succeed while moving OpenSSL out of Program Files failed with "Access is denied" on the runner, aborting the job. Copy the tree with xcopy instead, matching the original layout under C:\OpenSSL-Win64. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index bd3d5c2..e94a78d 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -55,7 +55,12 @@ jobs: run: | choco install openssl --version=1.1.1.2100 -y choco install nsis -y - IF EXIST "c:\\Program Files\\OpenSSL-Win64" (move "c:\\Program Files\\OpenSSL-Win64" "c:\\OpenSSL-Win64") ELSE (move "c:\\Program Files\\OpenSSL" "c:\\OpenSSL-Win64") + rem Copy instead of move — Program Files paths are often locked on runners (move fails with Access denied). + if exist "C:\\Program Files\\OpenSSL-Win64" ( + xcopy "C:\\Program Files\\OpenSSL-Win64" "C:\\OpenSSL-Win64\\" /E /I /H /Y /Q + ) else if exist "C:\\Program Files\\OpenSSL" ( + xcopy "C:\\Program Files\\OpenSSL" "C:\\OpenSSL-Win64\\" /E /I /H /Y /Q + ) - name: Wire Windows NSIS into WSL PATH if: steps.win32-cache.outputs.cache-hit != 'true' From d868ac078616eb506e7eeb81e9c2ff86b57948f4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 4 May 2026 16:41:30 +0000 Subject: [PATCH 13/38] CI (Windows): raise binary job timeout to 300 minutes The Windows Erlang + mix desktop.installer step can exceed 120 minutes on cold cache; the job was cancelled mid-build. Allow up to 5 hours for the full toolchain + installer pipeline. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index e94a78d..e69ad9d 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,7 +12,7 @@ env: jobs: windows: runs-on: windows-latest - timeout-minutes: 120 + timeout-minutes: 300 defaults: run: shell: wsl-bash {0} From b7279b8dc174c7ea12486e206d02a3e69df565f6 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 09:00:31 +0000 Subject: [PATCH 14/38] CI (macOS): use macos-15 runner instead of deprecated macos-13 GitHub is retiring the macOS 13 hosted image. Switch the binaries workflow to macos-15 and refresh the asdf cache key so caches are not reused across OS versions. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index e69ad9d..88e400e 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -200,14 +200,14 @@ jobs: _build/prod/*.exe macos: - runs-on: macos-13 + runs-on: macos-15 steps: - name: macOS Cache uses: actions/cache@v3 id: macos-cache with: path: /Users/runner/.asdf - key: macos-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' From 8814182c878b11de264d9e40a7dc54014e717e2c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 14:11:35 +0000 Subject: [PATCH 15/38] CI (macOS): use Homebrew libpng for wxWidgets (fix fp.h on macOS 15) wxWidgets' bundled libpng includes legacy Mac headers; building it on macos-15/arm64 fails with missing fp.h. Install libpng via Homebrew and pass --with-libpng=sys to configure. Bump the asdf cache key so Erlang is rebuilt against the updated wx build. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 88e400e..a01bd79 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -207,12 +207,12 @@ jobs: id: macos-cache with: path: /Users/runner/.asdf - key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-sys - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' run: | - brew install binutils coreutils wget automake autoconf libtool + brew install binutils coreutils wget automake autoconf libtool libpng - name: "Installing wxWidgets" if: steps.macos-cache.outputs.cache-hit != 'true' @@ -222,7 +222,9 @@ jobs: cd wxWidgets; git checkout ${{ env.WXWIDGETS_VERSION }} git submodule update --init - ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared + # Bundled libpng in wx 3.2.x can fail on Apple Silicon + recent Xcode (fp.h removed); + # link against Homebrew libpng instead. + ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared --with-libpng=sys make -j8 - name: "Installing Erlang" From a1fbcb78725c0d8d9ddbb59ede5eafc3dbd28f6c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 14:54:09 +0000 Subject: [PATCH 16/38] CI (Windows): avoid Git/LFS checkout stalls; shallow clones; 20m job cap - Set job timeout to 20 minutes as requested. - Skip Git LFS smudge (GIT_LFS_SKIP_SMUDGE + checkout lfs:false): Android LFS blobs were hanging after fetch on Windows. - Upgrade actions/checkout to v4 with shallow fetch-depth. - Use shallow single-branch clones for wxWidgets, OTP, and Elixir with HTTP stall timeouts to fail fast instead of hanging after "Compressing objects". Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index a01bd79..7f7f3e8 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,10 +12,14 @@ env: jobs: windows: runs-on: windows-latest - timeout-minutes: 300 + # Fail fast if a step hangs (common on Windows+LFS); cold OTP rebuild needs cache elsewhere. + timeout-minutes: 20 defaults: run: shell: wsl-bash {0} + env: + # Repo uses Git LFS for Android *.a etc.; smudge on NTFS/WSL often stalls after fetch. + GIT_LFS_SKIP_SMUDGE: "1" name: Build Erlang/OTP (Windows) steps: - name: Restore Windows Cache @@ -81,10 +85,15 @@ jobs: - name: Download wxWidgets if: steps.win32-cache.outputs.cache-hit != 'true' run: | - git clone ${{ env.WXWIDGETS_REPO }} + set -euo pipefail + cd "${GITHUB_WORKSPACE}" + # Shallow clone + stall limits avoid hangs on slow/stuck HTTP after "Compressing objects". + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + git config --global http.postBuffer 524288000 + git clone --depth 1 --branch ${{ env.WXWIDGETS_VERSION }} --single-branch ${{ env.WXWIDGETS_REPO }} wxWidgets cd wxWidgets - git checkout ${{ env.WXWIDGETS_VERSION }} - git submodule update --init + git submodule update --init --depth 1 sed -i -r -e 's/wxUSE_POSTSCRIPT +0/wxUSE_POSTSCRIPT 1/' include/wx/msw/setup.h sed -i -r -e 's/wxUSE_WEBVIEW_EDGE +0/wxUSE_WEBVIEW_EDGE 1/' include/wx/msw/setup.h sed -i -r -e 's/WXWIN_COMPATIBILITY_3_0 +0/WXWIN_COMPATIBILITY_3_0 1/' include/wx/msw/setup.h @@ -114,9 +123,10 @@ jobs: - name: Compile Erlang if: steps.win32-cache.outputs.cache-hit != 'true' run: | - git clone ${{ env.OTP_GITHUB_URL }} + set -euo pipefail + cd "${GITHUB_WORKSPACE}" + git clone --depth 1 --branch ${{ env.OTP_VERSION }} --single-branch ${{ env.OTP_GITHUB_URL }} otp cd otp - git checkout ${{ env.OTP_VERSION }} export ERL_TOP=`pwd` export MAKEFLAGS=-j$(($(nproc) + 2)) export ERLC_USE_SERVER=true @@ -161,13 +171,16 @@ jobs: shell: msys2 {0} run: | cd $HOME - git clone https://github.com/elixir-lang/elixir.git + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git cd elixir - git checkout v${{ env.ELIXIR_VERSION }} make echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc - - uses: actions/checkout@v1 + # Do not run Git LFS smudge: rel/android *.a are huge and stall on Windows I/O after fetch. + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 - name: "Get dependencies" shell: msys2 {0} From 3c86b8836e1ea5cfe45726766bea00e7a5a5d54f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 15:21:43 +0000 Subject: [PATCH 17/38] CI (macOS): point wx configure at Homebrew libpng (CPPFLAGS/LDFLAGS) wxWidgets --with-libpng=sys failed: png.h not found on the default include path. libpng is keg-only under Homebrew; export brew --prefix libpng include and lib paths plus PKG_CONFIG_PATH. Refresh asdf cache key. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 7f7f3e8..5b271cf 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -220,7 +220,7 @@ jobs: id: macos-cache with: path: /Users/runner/.asdf - key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-sys + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-sys-libpng-prefix - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' @@ -235,6 +235,11 @@ jobs: cd wxWidgets; git checkout ${{ env.WXWIDGETS_VERSION }} git submodule update --init + # Homebrew keg-only libpng: compiler must see include/lib paths (configure failed with "png.h ... no"). + LIBPNG_PREFIX="$(brew --prefix libpng)" + export CPPFLAGS="-I${LIBPNG_PREFIX}/include ${CPPFLAGS:-}" + export LDFLAGS="-L${LIBPNG_PREFIX}/lib ${LDFLAGS:-}" + export PKG_CONFIG_PATH="${LIBPNG_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH:-}" # Bundled libpng in wx 3.2.x can fail on Apple Silicon + recent Xcode (fp.h removed); # link against Homebrew libpng instead. ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared --with-libpng=sys From 934768109e0c501ffe8924162f4c149e8b1019e2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 15:52:03 +0000 Subject: [PATCH 18/38] CI (macOS): Homebrew libtiff + resilient shallow wxWidgets clone - Install libtiff and pass CPPFLAGS/LDFLAGS so imagtiff.cpp finds tiff.h. - Add --with-libtiff=sys alongside --with-libpng=sys. - Clone wxWidgets shallow with retries and HTTP stall timeouts (fixes transient curl 56 during full clone). - Bump macOS asdf cache key for rebuild. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 42 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 5b271cf..29bec3e 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -220,29 +220,43 @@ jobs: id: macos-cache with: path: /Users/runner/.asdf - key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-sys-libpng-prefix + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-tiff-sys - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' run: | - brew install binutils coreutils wget automake autoconf libtool libpng + brew install binutils coreutils wget automake autoconf libtool libpng libtiff - name: "Installing wxWidgets" if: steps.macos-cache.outputs.cache-hit != 'true' run: | - mkdir ~/projects && cd ~/projects - git clone ${{ env.WXWIDGETS_REPO }} - cd wxWidgets; - git checkout ${{ env.WXWIDGETS_VERSION }} - git submodule update --init - # Homebrew keg-only libpng: compiler must see include/lib paths (configure failed with "png.h ... no"). + set -euo pipefail + mkdir -p ~/projects && cd ~/projects + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + git config --global http.postBuffer 524288000 + for attempt in 1 2 3 4 5; do + rm -rf wxWidgets + if git clone --depth 1 --branch ${{ env.WXWIDGETS_VERSION }} ${{ env.WXWIDGETS_REPO }} wxWidgets; then + break + fi + echo "git clone wxWidgets failed (attempt $attempt), retrying..." >&2 + if [ "$attempt" -eq 5 ]; then + echo "git clone wxWidgets failed after 5 attempts" >&2 + exit 1 + fi + sleep $((attempt * 25)) + done + cd wxWidgets + git submodule update --init --depth 1 LIBPNG_PREFIX="$(brew --prefix libpng)" - export CPPFLAGS="-I${LIBPNG_PREFIX}/include ${CPPFLAGS:-}" - export LDFLAGS="-L${LIBPNG_PREFIX}/lib ${LDFLAGS:-}" - export PKG_CONFIG_PATH="${LIBPNG_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH:-}" - # Bundled libpng in wx 3.2.x can fail on Apple Silicon + recent Xcode (fp.h removed); - # link against Homebrew libpng instead. - ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared --with-libpng=sys + LIBTIFF_PREFIX="$(brew --prefix libtiff)" + export CPPFLAGS="-I${LIBPNG_PREFIX}/include -I${LIBTIFF_PREFIX}/include ${CPPFLAGS:-}" + export LDFLAGS="-L${LIBPNG_PREFIX}/lib -L${LIBTIFF_PREFIX}/lib ${LDFLAGS:-}" + export PKG_CONFIG_PATH="${LIBPNG_PREFIX}/lib/pkgconfig:${LIBTIFF_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH:-}" + # Bundled libpng can hit fp.h issues on recent Xcode; use Homebrew libpng/libtiff (keg-only paths above). + ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared \ + --with-libpng=sys --with-libtiff=sys make -j8 - name: "Installing Erlang" From 4b77da78e1ee8647f0378f9a478014eceee4d079 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 16:43:00 +0000 Subject: [PATCH 19/38] CI (macOS): install asdf via Homebrew (v0.19 has no asdf.sh) asdf master no longer ships asdf.sh after the Go rewrite; git clone + source failed. Use brew install asdf, add the brew prefix bin to PATH, and prepend ~/.asdf/shims per current docs. Write ~/.tool-versions for the cold install step. Bump cache key. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 29bec3e..63ac8d2 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -215,12 +215,18 @@ jobs: macos: runs-on: macos-15 steps: + # asdf v0.19+ is a Go binary from Homebrew; the old git clone + ~/.asdf/asdf.sh flow is invalid. + - name: Install asdf (Homebrew) + run: | + brew install asdf + echo "$(brew --prefix asdf)/bin" >> "$GITHUB_PATH" + - name: macOS Cache uses: actions/cache@v3 id: macos-cache with: path: /Users/runner/.asdf - key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-tiff-sys + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-tiff-sys-asdf-brew - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' @@ -262,21 +268,22 @@ jobs: - name: "Installing Erlang" if: steps.macos-cache.outputs.cache-hit != 'true' run: | - git clone https://github.com/asdf-vm/asdf.git ~/.asdf - . $HOME/.asdf/asdf.sh + set -euo pipefail + export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" asdf plugin add erlang asdf plugin add elixir asdf plugin add nodejs - echo "erlang ref:${{ env.OTP_VERSION }}" >> .tool-versions - echo "elixir ${{ env.ELIXIR_VERSION }}${{ env.ELIXIR_VARIANT }}" >> .tool-versions - echo "nodejs v18.7.0" >> .tool-versions - export KERL_CONFIGURE_OPTIONS="--enable-parallel-configure --with-wxdir=`echo ~/projects/wxWidgets` --disable-jit --without-javac --disable-debug CXX='gcc -std=c++11'" + echo "erlang ref:${{ env.OTP_VERSION }}" >> ~/.tool-versions + echo "elixir ${{ env.ELIXIR_VERSION }}${{ env.ELIXIR_VARIANT }}" >> ~/.tool-versions + echo "nodejs v18.7.0" >> ~/.tool-versions + export KERL_CONFIGURE_OPTIONS="--enable-parallel-configure --with-wxdir=$HOME/projects/wxWidgets --disable-jit --without-javac --disable-debug CXX='gcc -std=c++11'" asdf install - uses: actions/checkout@v1 - name: "Compile and Lint" run: | - . $HOME/.asdf/asdf.sh + set -euo pipefail + export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" echo "erlang ref:${{ env.OTP_VERSION }}" > .tool-versions echo "elixir ${{ env.ELIXIR_VERSION }}${{ env.ELIXIR_VARIANT }}" >> .tool-versions echo "nodejs v18.7.0" >> .tool-versions @@ -290,7 +297,8 @@ jobs: env: MACOS_PEM: ${{ secrets.MACOS_PEM }} run: | - . $HOME/.asdf/asdf.sh + set -euo pipefail + export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" mix desktop.create_keychain maybe export MACOS_KEYCHAIN="$HOME/Library/Keychains/macos-build.keychain" export LD_LIBRARY_PATH="$HOME/projects/wxWidgets/lib/" From 458475e1e74243d0746666696c9a9bcc32e43479 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 17:12:13 +0000 Subject: [PATCH 20/38] CI (macOS): fix KERL CXX flags for asdf-erlang on Apple Clang kerl passed CXX='gcc -std=c++11' as CXX=gcc and a separate -std=c++11 token, which ./configure rejected. Use CXX=clang++ (matches macOS toolchain). Bump asdf cache key for rebuild. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 63ac8d2..2b905f7 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -226,7 +226,7 @@ jobs: id: macos-cache with: path: /Users/runner/.asdf - key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-tiff-sys-asdf-brew + key: macos15-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-png-tiff-sys-asdf-brew-kerl-cxx - name: "Install brew deps" if: steps.macos-cache.outputs.cache-hit != 'true' @@ -276,7 +276,8 @@ jobs: echo "erlang ref:${{ env.OTP_VERSION }}" >> ~/.tool-versions echo "elixir ${{ env.ELIXIR_VERSION }}${{ env.ELIXIR_VARIANT }}" >> ~/.tool-versions echo "nodejs v18.7.0" >> ~/.tool-versions - export KERL_CONFIGURE_OPTIONS="--enable-parallel-configure --with-wxdir=$HOME/projects/wxWidgets --disable-jit --without-javac --disable-debug CXX='gcc -std=c++11'" + # Do not use CXX='gcc -std=c++11' — kerl splits that into CXX=gcc plus a stray -std=c++11 configure arg. + export KERL_CONFIGURE_OPTIONS="--enable-parallel-configure --with-wxdir=$HOME/projects/wxWidgets --disable-jit --without-javac --disable-debug CXX=clang++" asdf install - uses: actions/checkout@v1 From 02ac24512d0fe9f26ff35f6b952081d08773d58d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 17:43:13 +0000 Subject: [PATCH 21/38] CI (macOS): do not pass MACOS_PEM into Build Release on CI create_keychain maybe only skips when MACOS_PEM is unset. A repo secret that is set but not a valid Developer ID PEM still runs full keychain setup and fails at security import. Omit the secret from this step so CI builds unsigned artifacts without importing Apple certs. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 2b905f7..48d7f4b 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -295,11 +295,11 @@ jobs: cd assets && npm install - name: "Build Release" - env: - MACOS_PEM: ${{ secrets.MACOS_PEM }} run: | set -euo pipefail export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH" + # Do not set MACOS_PEM here: if the secret is set but not a valid Developer ID PEM, + # create_keychain maybe still runs the full path and fails import (CI cannot code-sign). mix desktop.create_keychain maybe export MACOS_KEYCHAIN="$HOME/Library/Keychains/macos-build.keychain" export LD_LIBRARY_PATH="$HOME/projects/wxWidgets/lib/" From 02467025eac0c89b583c0cfe5c6ca0bc64ce566e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 18:12:13 +0000 Subject: [PATCH 22/38] CI (Windows): raise job timeout to 90 minutes Cancelled runs showed ~20m elapsed then cancellation during Get dependencies; the 20-minute job cap was terminating the workflow mid-step (not an LFS flake). Allow enough time for mix deps / npm / installer on cache hits. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 48d7f4b..917921a 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,8 +12,8 @@ env: jobs: windows: runs-on: windows-latest - # Fail fast if a step hangs (common on Windows+LFS); cold OTP rebuild needs cache elsewhere. - timeout-minutes: 20 + # Full pipeline (msys2 mix deps / npm / desktop.installer) needs >20 min; avoid job-level cancel mid-step. + timeout-minutes: 90 defaults: run: shell: wsl-bash {0} From 2735ec122dc8d823cbd40ad16f93a7526d5d98ef Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 19:52:24 +0000 Subject: [PATCH 23/38] CI (Windows): extend job timeout to 180 minutes Cancelled runs hit the 90-minute job cap during Get dependencies (~90m elapsed). Raise the limit so mix/npm/installer can finish on slower runners. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 917921a..1f5f46a 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,8 +12,8 @@ env: jobs: windows: runs-on: windows-latest - # Full pipeline (msys2 mix deps / npm / desktop.installer) needs >20 min; avoid job-level cancel mid-step. - timeout-minutes: 90 + # mix deps.get → npm → desktop.installer on Windows can exceed 90 minutes on busy runners. + timeout-minutes: 180 defaults: run: shell: wsl-bash {0} From 42ffd1152244cbdd19ccd08fa88821fa6f3fe369 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 5 May 2026 23:03:12 +0000 Subject: [PATCH 24/38] CI (Windows): use maximum job timeout (360 minutes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cancelled runs lasted ~180 minutes during Get dependencies — the job timeout, not a hung resolver. Raise to GitHub-hosted runner maximum so long Windows builds can finish. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 1f5f46a..1648ada 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,8 +12,8 @@ env: jobs: windows: runs-on: windows-latest - # mix deps.get → npm → desktop.installer on Windows can exceed 90 minutes on busy runners. - timeout-minutes: 180 + # GitHub-hosted jobs allow up to 360 minutes; Windows mix/npm/installer can exceed 180 on slow runs. + timeout-minutes: 360 defaults: run: shell: wsl-bash {0} From 90e8ee9f8de01d701300d7ee6c8aee05ad6a6004 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 May 2026 05:15:59 +0000 Subject: [PATCH 25/38] CI (Windows): cache deps + Hex/Mix homes; faster Hex downloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Jobs hit the GitHub 360-minute maximum during mix deps.get — timeouts cannot be raised further. Restore/save actions/cache for deps/ and project-local MIX_HOME/ HEX_HOME, set HEX_HTTP_CONCURRENCY=8, and export MIX_HOME/HEX_HOME for Build Release so subsequent PR runs reuse fetched Hex archives. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 1648ada..1db6920 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -182,13 +182,38 @@ jobs: lfs: false fetch-depth: 1 + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + - name: "Get dependencies" shell: msys2 {0} run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export HEX_HTTP_CONCURRENCY=8 + mkdir -p "$MIX_HOME" "$HEX_HOME" mix local.hex --force mix local.rebar --force mix deps.get + - name: Save Mix / Hex cache + if: steps.mix-cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + - name: "npm install" shell: msys2 {0} run: | @@ -202,6 +227,9 @@ jobs: WIN32_KEY_PASS: ${{ secrets.WIN32_KEY_PASS }} shell: msys2 {0} run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" mix assets.deploy mix desktop.installer From 5d609962ba3aafe871a1944c1e79ede4b4f6ffe7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 May 2026 11:29:02 +0000 Subject: [PATCH 26/38] fix(ci): speed Windows mix deps with shallow git clones and prod-only fetch Pin git SHAs for elixir-desktop deps and use :depth 1 so Mix fetches a single commit. On Windows, run mix deps.get with --only prod, disable Git credential prompts, and set MIX_ENV=prod for the release build to match the dependency set. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 7 ++++++- mix.exs | 16 +++++++++++++--- mix.lock | 6 +++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 1db6920..d6b5500 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -199,10 +199,14 @@ jobs: export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" export HEX_HTTP_CONCURRENCY=8 + export GIT_TERMINAL_PROMPT=0 + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 mkdir -p "$MIX_HOME" "$HEX_HOME" mix local.hex --force mix local.rebar --force - mix deps.get + # Installer/release uses prod; skip dev/test-only deps to shrink Hex + git work. + MIX_ENV=prod mix deps.get --only prod - name: Save Mix / Hex cache if: steps.mix-cache.outputs.cache-hit != 'true' @@ -230,6 +234,7 @@ jobs: set -euo pipefail export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export MIX_ENV=prod mix assets.deploy mix desktop.installer diff --git a/mix.exs b/mix.exs index 47d9f61..5ba584c 100644 --- a/mix.exs +++ b/mix.exs @@ -103,8 +103,14 @@ defmodule Todo.MixProject do deps_list = [ # {:desktop, path: "../desktop"}, # {:desktop, "~> 1.5"}, - {:desktop, github: "elixir-desktop/desktop"}, - {:desktop_deployment, github: "elixir-desktop/deployment"}, + # Shallow clones keep CI (especially Windows + NTFS) from drowning in full-repo history. + # Pinned full SHAs (required with :depth) keep lockfile versions stable. + {:desktop, + github: "elixir-desktop/desktop", ref: "0966857094b6ceaec6789fae65b74478bfc8be19", depth: 1}, + {:desktop_deployment, + github: "elixir-desktop/deployment", + ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c", + depth: 1}, {:igniter, "~> 0.6"}, # {:desktop_deployment, path: "../deployment", runtime: false}, @@ -113,7 +119,11 @@ defmodule Todo.MixProject do {:phoenix_ecto, "~> 4.5"}, {:ecto_sqlite3, "~> 0.22.0"}, # Pinned to the same version as the android runtime binary nifs. - {:exqlite, github: "elixir-desktop/exqlite", override: true}, + {:exqlite, + github: "elixir-desktop/exqlite", + ref: "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", + override: true, + depth: 1}, {:phoenix_html, "~> 4.1"}, {:phoenix_live_dashboard, "~> 0.8.3"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, diff --git a/mix.lock b/mix.lock index 5d22ab3..bda672a 100644 --- a/mix.lock +++ b/mix.lock @@ -10,8 +10,8 @@ "dbus": {:hex, :dbus, "0.8.0", "7c800681f35d909c199265e55a8ee4aea9ebe4acccce77a0740f89f29cc57648", [:make], [], "hexpm", "a9784f2d9717ffa1f74169144a226c39633ac0d9c7fe8cb3594aeb89c827cca5"}, "debouncer": {:hex, :debouncer, "0.1.13", "af5906b231c196943ac8386b5b5f45a2f36d54a8bcd7e1b29eef2671de33d287", [:mix], [], "hexpm", "a14f57420c7d4a287f8f08e715fc8759b5d28dcd1032f9585d57c45d22123382"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, - "desktop": {:git, "https://github.com/elixir-desktop/desktop.git", "0966857094b6ceaec6789fae65b74478bfc8be19", []}, - "desktop_deployment": {:git, "https://github.com/elixir-desktop/deployment.git", "c5c09864693121acb7e6de0f8f3253937ed2e07c", []}, + "desktop": {:git, "https://github.com/elixir-desktop/desktop.git", "0966857094b6ceaec6789fae65b74478bfc8be19", [ref: "0966857094b6ceaec6789fae65b74478bfc8be19", depth: 1]}, + "desktop_deployment": {:git, "https://github.com/elixir-desktop/deployment.git", "c5c09864693121acb7e6de0f8f3253937ed2e07c", [ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c", depth: 1]}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, "ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"}, "ecto_sql": {:hex, :ecto_sql, "3.13.4", "b6e9d07557ddba62508a9ce4a484989a5bb5e9a048ae0e695f6d93f095c25d60", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b38cf0749ca4d1c5a8bcbff79bbe15446861ca12a61f9fba604486cb6b62a14"}, @@ -21,7 +21,7 @@ "ex_dbus": {:hex, :ex_dbus, "0.1.4", "053df83d45b27ba0b9b6ef55a47253922069a3ace12a2a7dd30d3aff58301e17", [:mix], [{:dbus, "~> 0.8.0", [hex: :dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "d8baeaf465eab57b70a47b70e29fdfef6eb09ba110fc37176eebe6ac7874d6d5"}, "ex_sni": {:hex, :ex_sni, "0.2.9", "81f9421035dd3edb6d69f1a4dd5f53c7071b41628130d32ba5ab7bb4bfdc2da0", [:mix], [{:debouncer, "~> 0.1", [hex: :debouncer, repo: "hexpm", optional: false]}, {:ex_dbus, "~> 0.1", [hex: :ex_dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "921d67d913765ed20ea8354fd1798dabc957bf66990a6842d6aaa7cd5ee5bc06"}, "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"}, - "exqlite": {:git, "https://github.com/elixir-desktop/exqlite.git", "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", []}, + "exqlite": {:git, "https://github.com/elixir-desktop/exqlite.git", "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", [ref: "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", depth: 1]}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, From 59d86d80b1931273f24b5e3ee0a14748bb8417ca Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 May 2026 17:36:56 +0000 Subject: [PATCH 27/38] fix(deps): drop git :depth with :ref for Mix 1.17 (Android & Linux CI) Elixir 1.17 rejects `:depth` and `:ref` together; Android APK and the Linux Docker image use that Mix version. Keep pinned SHAs only and refresh mix.lock so `mix compile` no longer reports an outdated lock. Co-authored-by: Dominic Letz --- mix.exs | 12 ++++-------- mix.lock | 6 +++--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index 5ba584c..998254f 100644 --- a/mix.exs +++ b/mix.exs @@ -103,14 +103,11 @@ defmodule Todo.MixProject do deps_list = [ # {:desktop, path: "../desktop"}, # {:desktop, "~> 1.5"}, - # Shallow clones keep CI (especially Windows + NTFS) from drowning in full-repo history. - # Pinned full SHAs (required with :depth) keep lockfile versions stable. + # Pinned SHAs for reproducible CI. Do not add :depth with :ref — Android CI uses Elixir 1.17, where Mix forbids that pair. {:desktop, - github: "elixir-desktop/desktop", ref: "0966857094b6ceaec6789fae65b74478bfc8be19", depth: 1}, + github: "elixir-desktop/desktop", ref: "0966857094b6ceaec6789fae65b74478bfc8be19"}, {:desktop_deployment, - github: "elixir-desktop/deployment", - ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c", - depth: 1}, + github: "elixir-desktop/deployment", ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c"}, {:igniter, "~> 0.6"}, # {:desktop_deployment, path: "../deployment", runtime: false}, @@ -122,8 +119,7 @@ defmodule Todo.MixProject do {:exqlite, github: "elixir-desktop/exqlite", ref: "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", - override: true, - depth: 1}, + override: true}, {:phoenix_html, "~> 4.1"}, {:phoenix_live_dashboard, "~> 0.8.3"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, diff --git a/mix.lock b/mix.lock index bda672a..db0f7e7 100644 --- a/mix.lock +++ b/mix.lock @@ -10,8 +10,8 @@ "dbus": {:hex, :dbus, "0.8.0", "7c800681f35d909c199265e55a8ee4aea9ebe4acccce77a0740f89f29cc57648", [:make], [], "hexpm", "a9784f2d9717ffa1f74169144a226c39633ac0d9c7fe8cb3594aeb89c827cca5"}, "debouncer": {:hex, :debouncer, "0.1.13", "af5906b231c196943ac8386b5b5f45a2f36d54a8bcd7e1b29eef2671de33d287", [:mix], [], "hexpm", "a14f57420c7d4a287f8f08e715fc8759b5d28dcd1032f9585d57c45d22123382"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, - "desktop": {:git, "https://github.com/elixir-desktop/desktop.git", "0966857094b6ceaec6789fae65b74478bfc8be19", [ref: "0966857094b6ceaec6789fae65b74478bfc8be19", depth: 1]}, - "desktop_deployment": {:git, "https://github.com/elixir-desktop/deployment.git", "c5c09864693121acb7e6de0f8f3253937ed2e07c", [ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c", depth: 1]}, + "desktop": {:git, "https://github.com/elixir-desktop/desktop.git", "0966857094b6ceaec6789fae65b74478bfc8be19", [ref: "0966857094b6ceaec6789fae65b74478bfc8be19"]}, + "desktop_deployment": {:git, "https://github.com/elixir-desktop/deployment.git", "c5c09864693121acb7e6de0f8f3253937ed2e07c", [ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c"]}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, "ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"}, "ecto_sql": {:hex, :ecto_sql, "3.13.4", "b6e9d07557ddba62508a9ce4a484989a5bb5e9a048ae0e695f6d93f095c25d60", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2b38cf0749ca4d1c5a8bcbff79bbe15446861ca12a61f9fba604486cb6b62a14"}, @@ -21,7 +21,7 @@ "ex_dbus": {:hex, :ex_dbus, "0.1.4", "053df83d45b27ba0b9b6ef55a47253922069a3ace12a2a7dd30d3aff58301e17", [:mix], [{:dbus, "~> 0.8.0", [hex: :dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "d8baeaf465eab57b70a47b70e29fdfef6eb09ba110fc37176eebe6ac7874d6d5"}, "ex_sni": {:hex, :ex_sni, "0.2.9", "81f9421035dd3edb6d69f1a4dd5f53c7071b41628130d32ba5ab7bb4bfdc2da0", [:mix], [{:debouncer, "~> 0.1", [hex: :debouncer, repo: "hexpm", optional: false]}, {:ex_dbus, "~> 0.1", [hex: :ex_dbus, repo: "hexpm", optional: false]}, {:saxy, "~> 1.4.0", [hex: :saxy, repo: "hexpm", optional: false]}], "hexpm", "921d67d913765ed20ea8354fd1798dabc957bf66990a6842d6aaa7cd5ee5bc06"}, "expo": {:hex, :expo, "1.1.1", "4202e1d2ca6e2b3b63e02f69cfe0a404f77702b041d02b58597c00992b601db5", [:mix], [], "hexpm", "5fb308b9cb359ae200b7e23d37c76978673aa1b06e2b3075d814ce12c5811640"}, - "exqlite": {:git, "https://github.com/elixir-desktop/exqlite.git", "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", [ref: "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", depth: 1]}, + "exqlite": {:git, "https://github.com/elixir-desktop/exqlite.git", "1caf1f42395fff8a68ac5a509f7294090a6f6f0d", [ref: "1caf1f42395fff8a68ac5a509f7294090a6f6f0d"]}, "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, "finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, From 456d6d4412bbf449aef2e13be1b3c8f81d146932 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 6 May 2026 23:48:34 +0000 Subject: [PATCH 28/38] fix(ci): split Windows toolchain and installer jobs for 360m limit The combined Windows job exceeded GitHub Actions' maximum job duration (~6h). Run wxWidgets + OTP build in `windows-toolchain` and Mix/release in `windows-installer` (needs toolchain) so each job gets its own timeout budget. Restore shared OTP cache in the second job; install NSIS there for makensis. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 52 +++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index d6b5500..90405b3 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -10,15 +10,14 @@ env: DOCKER_BUILDKIT: 0 jobs: - windows: + # wxWidgets + OTP compile alone can approach GitHub's 360m/job limit; splitting gives toolchain + Mix two budgets. + windows-toolchain: runs-on: windows-latest - # GitHub-hosted jobs allow up to 360 minutes; Windows mix/npm/installer can exceed 180 on slow runs. timeout-minutes: 360 defaults: run: shell: wsl-bash {0} env: - # Repo uses Git LFS for Android *.a etc.; smudge on NTFS/WSL often stalls after fetch. GIT_LFS_SKIP_SMUDGE: "1" name: Build Erlang/OTP (Windows) steps: @@ -28,12 +27,10 @@ jobs: with: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - + - uses: Vampire/setup-wsl@v2 if: steps.win32-cache.outputs.cache-hit != 'true' with: - # Ubuntu 22.04 for up-to-date userland; OTP's installer_win32 invokes makensis.exe with - # Windows-style flags, so we install NSIS for Windows and expose it via /usr/local/bin/makensis.exe. distribution: Ubuntu-22.04 - name: Install WSL dependencies @@ -59,7 +56,6 @@ jobs: run: | choco install openssl --version=1.1.1.2100 -y choco install nsis -y - rem Copy instead of move — Program Files paths are often locked on runners (move fails with Access denied). if exist "C:\\Program Files\\OpenSSL-Win64" ( xcopy "C:\\Program Files\\OpenSSL-Win64" "C:\\OpenSSL-Win64\\" /E /I /H /Y /Q ) else if exist "C:\\Program Files\\OpenSSL" ( @@ -78,16 +74,14 @@ jobs: exit 1 fi install -d /usr/local/bin - # otp_build runs makensis.exe /V2 ... — Windows NSIS accepts those flags; Linux makensis does not. printf '%s\n' '#!/bin/sh' "exec \"$NSIS\" \"\$@\"" > /usr/local/bin/makensis.exe chmod +x /usr/local/bin/makensis.exe - + - name: Download wxWidgets if: steps.win32-cache.outputs.cache-hit != 'true' run: | set -euo pipefail cd "${GITHUB_WORKSPACE}" - # Shallow clone + stall limits avoid hangs on slow/stuck HTTP after "Compressing objects". git config --global http.lowSpeedLimit 500 git config --global http.lowSpeedTime 120 git config --global http.postBuffer 524288000 @@ -95,8 +89,8 @@ jobs: cd wxWidgets git submodule update --init --depth 1 sed -i -r -e 's/wxUSE_POSTSCRIPT +0/wxUSE_POSTSCRIPT 1/' include/wx/msw/setup.h - sed -i -r -e 's/wxUSE_WEBVIEW_EDGE +0/wxUSE_WEBVIEW_EDGE 1/' include/wx/msw/setup.h - sed -i -r -e 's/WXWIN_COMPATIBILITY_3_0 +0/WXWIN_COMPATIBILITY_3_0 1/' include/wx/msw/setup.h + sed -i -r -e 's/wxUSE_WEBVIEW_EDGE +0/wxUSE_WEBVIEW_EDGE 1/' include/wx/msw/setup.h + sed -i -r -e 's/WXWIN_COMPATIBILITY_3_0 +0/WXWIN_COMPATIBILITY_3_0 1/' include/wx/msw/setup.h - name: Install WebView2 if: steps.win32-cache.outputs.cache-hit != 'true' @@ -132,7 +126,7 @@ jobs: export ERLC_USE_SERVER=true export ERTS_SKIP_DEPEND=true eval `./otp_build env_win32 x64` - ./otp_build all -a + ./otp_build all -a cp /mnt/c/opt/local64/pgm/wxWidgets-3.x.x/3rdparty/webview2/runtimes/win-x64/native/WebView2Loader.dll $ERL_TOP/release/win32/erts-*/bin/ ./otp_build installer_win32 export NAME=`ls release/win32/otp*.exe` @@ -144,7 +138,37 @@ jobs: with: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - + + windows-installer: + runs-on: windows-latest + needs: windows-toolchain + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows installer + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + - name: Run Erlang installer shell: cmd run: C:\\opt\\otp.exe /S From b0ae0c3f0e223fadea5d7521c10d5ddca3f2f488 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 7 May 2026 06:00:19 +0000 Subject: [PATCH 29/38] fix(ci): split Windows assemble vs installer to beat 360m job timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build Windows installer hit the max duration during `mix desktop.installer` (full release). Use DESKTOP_CI_RELEASE_PHASE=assemble so the first job runs `mix release` without NSIS, caches `_build/prod`, and a second job restores it and runs `mix desktop.installer` only — each job keeps its own timeout budget. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 147 +++++++++++++++++++++++++++++++-- mix.exs | 15 +++- 2 files changed, 149 insertions(+), 13 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 90405b3..f6f6e34 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -139,7 +139,8 @@ jobs: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - windows-installer: + # Assemble + NSIS each need up to 360m; split so `mix desktop.installer` gets its own budget. + windows-assemble: runs-on: windows-latest needs: windows-toolchain timeout-minutes: 360 @@ -148,7 +149,7 @@ jobs: shell: wsl-bash {0} env: GIT_LFS_SKIP_SMUDGE: "1" - name: Build Windows installer + name: Build Windows release (assemble) steps: - name: Restore Windows Cache uses: actions/cache/restore@v3 @@ -190,17 +191,16 @@ jobs: shell: msys2 {0} run: | echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc - + - name: Install Elixir shell: msys2 {0} run: | cd $HOME git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git cd elixir - make + make echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc - - # Do not run Git LFS smudge: rel/android *.a are huge and stall on Windows I/O after fetch. + - uses: actions/checkout@v4 with: lfs: false @@ -229,7 +229,6 @@ jobs: mkdir -p "$MIX_HOME" "$HEX_HOME" mix local.hex --force mix local.rebar --force - # Installer/release uses prod; skip dev/test-only deps to shrink Hex + git work. MIX_ENV=prod mix deps.get --only prod - name: Save Mix / Hex cache @@ -247,10 +246,141 @@ jobs: run: | cd assets && npm install + - name: Assemble prod release (no NSIS yet) + env: + MAKE: make + REBAR_TARGET_ARCH_WORDSIZE: 64 + REBAR_TARGET_ARCH: x86_64-w64-mingw32 + DESKTOP_CI_RELEASE_PHASE: assemble + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export MIX_ENV=prod + mix assets.deploy + mix release default_release --overwrite + + - name: Save Windows prod _build cache + uses: actions/cache/save@v4 + with: + path: _build/prod + key: windows-prod-assemble-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + windows-package: + runs-on: windows-latest + needs: windows-assemble + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows installer + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + + - name: Run Erlang installer + shell: cmd + run: C:\\opt\\otp.exe /S + + - name: "Install msys2" + uses: msys2/setup-msys2@v2 + with: + install: pacman-mirrors pkg-config base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-go upx mingw-w64-x86_64-dlfcn unzip git tar mingw-w64-x86_64-nodejs mingw-w64-x86_64-imagemagick mingw-w64-x86_64-osslsigncode autoconf automake libtool gettext-devel gettext + update: true + + - name: Locate Erlang + shell: msys2 {0} + run: | + ERTS=`find /c/Program\ Files/[Ee]rl* -type d -name "erts-*" -not -path "*lib*"` + echo $ERTS + echo export PATH=\"\$PATH:$ERTS/bin\" > $HOME/.bashrc + + - name: Locate NSIS + shell: msys2 {0} + run: | + echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc + + - name: Install Elixir + shell: msys2 {0} + run: | + cd $HOME + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git + cd elixir + make + echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc + + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + + - name: "Get dependencies" + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export HEX_HTTP_CONCURRENCY=8 + export GIT_TERMINAL_PROMPT=0 + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + mkdir -p "$MIX_HOME" "$HEX_HOME" + mix local.hex --force + mix local.rebar --force + MIX_ENV=prod mix deps.get --only prod + + - name: Restore Windows prod _build + id: prod-build + uses: actions/cache/restore@v4 + with: + path: _build/prod + key: windows-prod-assemble-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + - name: Verify prod build from assemble job + shell: msys2 {0} + run: | + set -euo pipefail + test -d "$GITHUB_WORKSPACE/_build/prod" || { echo "Missing _build/prod after cache restore" >&2; exit 1; } + test -d "$GITHUB_WORKSPACE/_build/prod/rel" || { echo "Missing release output — assemble job must complete first." >&2; exit 1; } + + - name: "npm install" + shell: msys2 {0} + run: | + cd assets && npm install + - name: "Build Release" env: MAKE: make - REBAR_TARGET_ARCH_WORDSIZE: 64 + REBAR_TARGET_ARCH_WORDSIZE: 64 REBAR_TARGET_ARCH: x86_64-w64-mingw32 WIN32_KEY_PASS: ${{ secrets.WIN32_KEY_PASS }} shell: msys2 {0} @@ -259,7 +389,6 @@ jobs: export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" export MIX_ENV=prod - mix assets.deploy mix desktop.installer - name: Archive Installer diff --git a/mix.exs b/mix.exs index 998254f..6928676 100644 --- a/mix.exs +++ b/mix.exs @@ -38,10 +38,17 @@ defmodule Todo.MixProject do [:assemble] _ -> - [ - :assemble, - &Desktop.Deployment.generate_installer/1 - ] + # CI can assemble in one Windows job and run NSIS packaging in the next (separate 360m budget). + case System.get_env("DESKTOP_CI_RELEASE_PHASE") do + "assemble" -> + [:assemble] + + _ -> + [ + :assemble, + &Desktop.Deployment.generate_installer/1 + ] + end end end From e34e5a9c51627953ebd67ff78105c1c844f412a1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 7 May 2026 12:10:26 +0000 Subject: [PATCH 30/38] fix(ci): split Windows compile/assets from release assemble Build Windows release (assemble) still hit the 360m cap during `mix release`. Run `mix compile` + `mix assets.deploy` in a new job, cache `_build/prod` and `priv/static`, then assemble-only release in the following job so compilation and tarball bundling each get their own timeout budget. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 140 ++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index f6f6e34..b9c3a6e 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -139,8 +139,8 @@ jobs: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - # Assemble + NSIS each need up to 360m; split so `mix desktop.installer` gets its own budget. - windows-assemble: + # Compile, release assemble, and NSIS can each need up to 360m on Windows runners. + windows-compile: runs-on: windows-latest needs: windows-toolchain timeout-minutes: 360 @@ -149,7 +149,7 @@ jobs: shell: wsl-bash {0} env: GIT_LFS_SKIP_SMUDGE: "1" - name: Build Windows release (assemble) + name: Build Windows compile & assets steps: - name: Restore Windows Cache uses: actions/cache/restore@v3 @@ -246,6 +246,139 @@ jobs: run: | cd assets && npm install + - name: Compile prod and digest assets + env: + MAKE: make + REBAR_TARGET_ARCH_WORDSIZE: 64 + REBAR_TARGET_ARCH: x86_64-w64-mingw32 + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export MIX_ENV=prod + mix compile + mix assets.deploy + + - name: Save Windows compile output cache + uses: actions/cache/save@v4 + with: + path: | + _build/prod + priv/static + key: windows-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + windows-assemble: + runs-on: windows-latest + needs: windows-compile + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows release (assemble) + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + + - name: Run Erlang installer + shell: cmd + run: C:\\opt\\otp.exe /S + + - name: "Install msys2" + uses: msys2/setup-msys2@v2 + with: + install: pacman-mirrors pkg-config base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-go upx mingw-w64-x86_64-dlfcn unzip git tar mingw-w64-x86_64-nodejs mingw-w64-x86_64-imagemagick mingw-w64-x86_64-osslsigncode autoconf automake libtool gettext-devel gettext + update: true + + - name: Locate Erlang + shell: msys2 {0} + run: | + ERTS=`find /c/Program\ Files/[Ee]rl* -type d -name "erts-*" -not -path "*lib*"` + echo $ERTS + echo export PATH=\"\$PATH:$ERTS/bin\" > $HOME/.bashrc + + - name: Locate NSIS + shell: msys2 {0} + run: | + echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc + + - name: Install Elixir + shell: msys2 {0} + run: | + cd $HOME + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git + cd elixir + make + echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc + + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + + - name: "Get dependencies" + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export HEX_HTTP_CONCURRENCY=8 + export GIT_TERMINAL_PROMPT=0 + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + mkdir -p "$MIX_HOME" "$HEX_HOME" + mix local.hex --force + mix local.rebar --force + MIX_ENV=prod mix deps.get --only prod + + - name: "npm install" + shell: msys2 {0} + run: | + cd assets && npm install + + - name: Restore Windows compile output + uses: actions/cache/restore@v4 + id: compile-cache + with: + path: | + _build/prod + priv/static + key: windows-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + - name: Verify compile output from prior job + shell: msys2 {0} + run: | + set -euo pipefail + test -d "$GITHUB_WORKSPACE/_build/prod/lib" || { echo "Missing compiled _build/prod — compile job must run first." >&2; exit 1; } + - name: Assemble prod release (no NSIS yet) env: MAKE: make @@ -258,7 +391,6 @@ jobs: export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" export MIX_ENV=prod - mix assets.deploy mix release default_release --overwrite - name: Save Windows prod _build cache From 4ea20d0c9270b18bcdd6269d16903f00ebc82528 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 7 May 2026 18:21:59 +0000 Subject: [PATCH 31/38] fix(ci): split Windows prod compile from assets.deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build Windows compile & assets hit the 360m limit during the combined step. Run `mix compile` alone in Build Windows compile (prod), cache `_build/prod`, then digest assets in a follow-up job that restores compile output and runs `mix assets.deploy` — separate timeouts per GitHub job cap. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 147 ++++++++++++++++++++++++++++++--- 1 file changed, 137 insertions(+), 10 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index b9c3a6e..baaf6e6 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -139,7 +139,7 @@ jobs: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - # Compile, release assemble, and NSIS can each need up to 360m on Windows runners. + # Split prod compile vs assets.deploy vs release — each can approach GitHub's 360m/job limit on Windows. windows-compile: runs-on: windows-latest needs: windows-toolchain @@ -149,7 +149,7 @@ jobs: shell: wsl-bash {0} env: GIT_LFS_SKIP_SMUDGE: "1" - name: Build Windows compile & assets + name: Build Windows compile (prod) steps: - name: Restore Windows Cache uses: actions/cache/restore@v3 @@ -246,7 +246,7 @@ jobs: run: | cd assets && npm install - - name: Compile prod and digest assets + - name: Compile prod (Mix only) env: MAKE: make REBAR_TARGET_ARCH_WORDSIZE: 64 @@ -258,19 +258,146 @@ jobs: export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" export MIX_ENV=prod mix compile + + - name: Save Windows Mix compile cache + uses: actions/cache/save@v4 + with: + path: _build/prod + key: windows-job-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + windows-assets: + runs-on: windows-latest + needs: windows-compile + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows assets + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + + - name: Run Erlang installer + shell: cmd + run: C:\\opt\\otp.exe /S + + - name: "Install msys2" + uses: msys2/setup-msys2@v2 + with: + install: pacman-mirrors pkg-config base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-go upx mingw-w64-x86_64-dlfcn unzip git tar mingw-w64-x86_64-nodejs mingw-w64-x86_64-imagemagick mingw-w64-x86_64-osslsigncode autoconf automake libtool gettext-devel gettext + update: true + + - name: Locate Erlang + shell: msys2 {0} + run: | + ERTS=`find /c/Program\ Files/[Ee]rl* -type d -name "erts-*" -not -path "*lib*"` + echo $ERTS + echo export PATH=\"\$PATH:$ERTS/bin\" > $HOME/.bashrc + + - name: Locate NSIS + shell: msys2 {0} + run: | + echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc + + - name: Install Elixir + shell: msys2 {0} + run: | + cd $HOME + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git + cd elixir + make + echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc + + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + + - name: "Get dependencies" + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export HEX_HTTP_CONCURRENCY=8 + export GIT_TERMINAL_PROMPT=0 + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + mkdir -p "$MIX_HOME" "$HEX_HOME" + mix local.hex --force + mix local.rebar --force + MIX_ENV=prod mix deps.get --only prod + + - name: "npm install" + shell: msys2 {0} + run: | + cd assets && npm install + + - name: Restore Windows Mix compile output + uses: actions/cache/restore@v4 + id: compile-job-cache + with: + path: _build/prod + key: windows-job-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + - name: Verify compile output + shell: msys2 {0} + run: | + set -euo pipefail + test -d "$GITHUB_WORKSPACE/_build/prod/lib" || { echo "Missing compiled _build/prod from compile job." >&2; exit 1; } + + - name: Digest Phoenix assets + env: + MAKE: make + REBAR_TARGET_ARCH_WORDSIZE: 64 + REBAR_TARGET_ARCH: x86_64-w64-mingw32 + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export MIX_ENV=prod mix assets.deploy - - name: Save Windows compile output cache + - name: Save Windows compile + assets cache uses: actions/cache/save@v4 with: path: | _build/prod priv/static - key: windows-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + key: windows-job-assets-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 windows-assemble: runs-on: windows-latest - needs: windows-compile + needs: windows-assets timeout-minutes: 360 defaults: run: @@ -364,20 +491,20 @@ jobs: run: | cd assets && npm install - - name: Restore Windows compile output + - name: Restore Windows compile + assets output uses: actions/cache/restore@v4 id: compile-cache with: path: | _build/prod priv/static - key: windows-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + key: windows-job-assets-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 - - name: Verify compile output from prior job + - name: Verify compile + assets output from prior job shell: msys2 {0} run: | set -euo pipefail - test -d "$GITHUB_WORKSPACE/_build/prod/lib" || { echo "Missing compiled _build/prod — compile job must run first." >&2; exit 1; } + test -d "$GITHUB_WORKSPACE/_build/prod/lib" || { echo "Missing compiled _build/prod — assets job must run first." >&2; exit 1; } - name: Assemble prod release (no NSIS yet) env: From 161f8000cca950bff1d89df52a930ebb0235a122 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 00:33:02 +0000 Subject: [PATCH 32/38] fix(ci): avoid MSYS2 npm segfault on Windows (npm install exit 139) GitHub-hosted npm run under msys2/bash crashed with SIGSEGV. Pin Node via actions/setup-node and run npm install with cmd.exe + working-directory. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 44 ++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index baaf6e6..88b38a1 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -241,10 +241,15 @@ jobs: .ci_hex_home key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + # MSYS2's bundled npm can segfault (exit 139); use the Actions Node toolchain + cmd.exe. + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: "npm install" - shell: msys2 {0} - run: | - cd assets && npm install + shell: cmd + working-directory: assets + run: npm install - name: Compile prod (Mix only) env: @@ -356,10 +361,15 @@ jobs: mix local.rebar --force MIX_ENV=prod mix deps.get --only prod + # MSYS2's bundled npm can segfault (exit 139); use the Actions Node toolchain + cmd.exe. + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: "npm install" - shell: msys2 {0} - run: | - cd assets && npm install + shell: cmd + working-directory: assets + run: npm install - name: Restore Windows Mix compile output uses: actions/cache/restore@v4 @@ -486,10 +496,15 @@ jobs: mix local.rebar --force MIX_ENV=prod mix deps.get --only prod + # MSYS2's bundled npm can segfault (exit 139); use the Actions Node toolchain + cmd.exe. + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: "npm install" - shell: msys2 {0} - run: | - cd assets && npm install + shell: cmd + working-directory: assets + run: npm install - name: Restore Windows compile + assets output uses: actions/cache/restore@v4 @@ -631,10 +646,15 @@ jobs: test -d "$GITHUB_WORKSPACE/_build/prod" || { echo "Missing _build/prod after cache restore" >&2; exit 1; } test -d "$GITHUB_WORKSPACE/_build/prod/rel" || { echo "Missing release output — assemble job must complete first." >&2; exit 1; } + # MSYS2's bundled npm can segfault (exit 139); use the Actions Node toolchain + cmd.exe. + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: "npm install" - shell: msys2 {0} - run: | - cd assets && npm install + shell: cmd + working-directory: assets + run: npm install - name: "Build Release" env: From 1721b2f2165628f2aead7de2454c73a1f3b0e27a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 06:44:11 +0000 Subject: [PATCH 33/38] fix(ci): split Windows mix deps.compile from mix compile Build Windows compile (prod) hit the 360m limit during `mix compile`. Run `mix deps.compile` in a preceding job, cache `_build/prod`, then restore and run `mix compile` for the app only so each step gets its own timeout budget. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 140 ++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 88b38a1..021de76 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -139,8 +139,8 @@ jobs: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - # Split prod compile vs assets.deploy vs release — each can approach GitHub's 360m/job limit on Windows. - windows-compile: + # deps.compile vs mix compile each can approach GitHub's 360m/job limit on Windows. + windows-deps-compile: runs-on: windows-latest needs: windows-toolchain timeout-minutes: 360 @@ -149,7 +149,7 @@ jobs: shell: wsl-bash {0} env: GIT_LFS_SKIP_SMUDGE: "1" - name: Build Windows compile (prod) + name: Build Windows deps compile (prod) steps: - name: Restore Windows Cache uses: actions/cache/restore@v3 @@ -241,6 +241,125 @@ jobs: .ci_hex_home key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + - uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: "npm install" + shell: cmd + working-directory: assets + run: npm install + + - name: Compile prod dependencies (Mix only) + env: + MAKE: make + REBAR_TARGET_ARCH_WORDSIZE: 64 + REBAR_TARGET_ARCH: x86_64-w64-mingw32 + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export MIX_ENV=prod + mix deps.compile + + - name: Save Windows deps compile cache + uses: actions/cache/save@v4 + with: + path: _build/prod + key: windows-mix-deps-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + windows-compile: + runs-on: windows-latest + needs: windows-deps-compile + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows compile (prod) + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + + - name: Run Erlang installer + shell: cmd + run: C:\\opt\\otp.exe /S + + - name: "Install msys2" + uses: msys2/setup-msys2@v2 + with: + install: pacman-mirrors pkg-config base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-go upx mingw-w64-x86_64-dlfcn unzip git tar mingw-w64-x86_64-nodejs mingw-w64-x86_64-imagemagick mingw-w64-x86_64-osslsigncode autoconf automake libtool gettext-devel gettext + update: true + + - name: Locate Erlang + shell: msys2 {0} + run: | + ERTS=`find /c/Program\ Files/[Ee]rl* -type d -name "erts-*" -not -path "*lib*"` + echo $ERTS + echo export PATH=\"\$PATH:$ERTS/bin\" > $HOME/.bashrc + + - name: Locate NSIS + shell: msys2 {0} + run: | + echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc + + - name: Install Elixir + shell: msys2 {0} + run: | + cd $HOME + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git + cd elixir + make + echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc + + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + + - name: "Get dependencies" + shell: msys2 {0} + run: | + set -euo pipefail + export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" + export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" + export HEX_HTTP_CONCURRENCY=8 + export GIT_TERMINAL_PROMPT=0 + git config --global http.lowSpeedLimit 500 + git config --global http.lowSpeedTime 120 + mkdir -p "$MIX_HOME" "$HEX_HOME" + mix local.hex --force + mix local.rebar --force + MIX_ENV=prod mix deps.get --only prod + # MSYS2's bundled npm can segfault (exit 139); use the Actions Node toolchain + cmd.exe. - uses: actions/setup-node@v4 with: @@ -251,7 +370,20 @@ jobs: working-directory: assets run: npm install - - name: Compile prod (Mix only) + - name: Restore Windows deps compile output + uses: actions/cache/restore@v4 + id: deps-compile-cache + with: + path: _build/prod + key: windows-mix-deps-compile-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }}-v1 + + - name: Verify deps compile output + shell: msys2 {0} + run: | + set -euo pipefail + test -d "$GITHUB_WORKSPACE/_build/prod/lib" || { echo "Missing deps-compiled _build/prod from deps job." >&2; exit 1; } + + - name: Compile prod application (Mix only) env: MAKE: make REBAR_TARGET_ARCH_WORDSIZE: 64 From 54a5622c107e520d5d0ab398ca8f74b7f7fe0abf Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 12:56:14 +0000 Subject: [PATCH 34/38] fix(ci): speed Windows deps.compile with Elixir 1.19 parallel partitions deps.compile alone exceeded the 360m job limit. Bump binaries workflow (and lint CI) to Elixir 1.19.5 (matches .tool-versions), build v1.19.5 from source on Windows, and set MIX_OS_DEPS_COMPILE_PARTITION_COUNT plus MAKEFLAGS -j4 so Mix spawns parallel OS processes for dependency/native builds. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 5 ++++- .github/workflows/ci.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 021de76..600b5f2 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -5,7 +5,7 @@ env: OTP_VERSION: OTP-26.2.5.9_diode WXWIDGETS_REPO: https://github.com/wxWidgets/wxWidgets.git WXWIDGETS_VERSION: v3.2.6 - ELIXIR_VERSION: 1.16.3 + ELIXIR_VERSION: 1.19.5 ELIXIR_VARIANT: -otp-26 DOCKER_BUILDKIT: 0 @@ -253,8 +253,11 @@ jobs: - name: Compile prod dependencies (Mix only) env: MAKE: make + MAKEFLAGS: -j4 REBAR_TARGET_ARCH_WORDSIZE: 64 REBAR_TARGET_ARCH: x86_64-w64-mingw32 + # Elixir 1.19+: parallel OS processes for deps (native/NIF builds); cuts wall time vs 1.16. + MIX_OS_DEPS_COMPILE_PARTITION_COUNT: "4" shell: msys2 {0} run: | set -euo pipefail diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90c8b0c..5e53513 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: - name: Setup elixir uses: erlef/setup-beam@v1 with: - elixir-version: "1.16.3" + elixir-version: "1.19.5" otp-version: "26.2.5.9" - uses: actions/checkout@v5 From 01da0c6c391e9ee67f12a52791766821b4a22e63 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 8 May 2026 19:06:23 +0000 Subject: [PATCH 35/38] =?UTF-8?q?fix(docker):=20pin=20asdf=20v0.18.1=20?= =?UTF-8?q?=E2=80=94=20master=20no=20longer=20ships=20asdf.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linux installer Docker build sourced ~/.asdf/asdf.sh after cloning default branch; asdf v0.19 removed that entrypoint. Shallow-clone tag v0.18.1 so existing RUN blocks keep working. Co-authored-by: Dominic Letz --- scripts/Dockerfile | 3 ++- scripts/Dockerfile.cef | 2 +- scripts/Dockerfile.raspbian | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/Dockerfile b/scripts/Dockerfile index 5ea974a..55f2bec 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -52,7 +52,8 @@ ARG ELIXIR_VARIANT ENV ELIXIR_VARIANT=${ELIXIR_VARIANT:-"-otp-26"} ENV ASDF_DIR=/root/.asdf -RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} && \ +# asdf v0.19+ dropped asdf.sh (Go rewrite); pin v0.18.x for Docker RUN sourcing. +RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} --branch v0.18.1 --depth 1 && \ . ${ASDF_DIR}/asdf.sh && \ asdf plugin add elixir && \ echo "elixir ${ELIXIR_VERSION}${ELIXIR_VARIANT}" >> .tool-versions && \ diff --git a/scripts/Dockerfile.cef b/scripts/Dockerfile.cef index 7276ed9..b8f2197 100644 --- a/scripts/Dockerfile.cef +++ b/scripts/Dockerfile.cef @@ -50,7 +50,7 @@ RUN cd ~/projects/wxWidgets && \ ENV OTP_VERSION=25.3.2.6 ENV ELIXIR_VERSION=1.13.4 ENV ASDF_DIR=/root/.asdf -RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} && \ +RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} --branch v0.18.1 --depth 1 && \ . ${ASDF_DIR}/asdf.sh && \ asdf plugin add erlang && \ asdf plugin add elixir && \ diff --git a/scripts/Dockerfile.raspbian b/scripts/Dockerfile.raspbian index 16e9223..af16a1e 100644 --- a/scripts/Dockerfile.raspbian +++ b/scripts/Dockerfile.raspbian @@ -36,7 +36,7 @@ RUN cd ~/projects/wxWidgets && \ ENV OTP_VERSION=25.3.2.6 ENV ELIXIR_VERSION=1.13.4 ENV ASDF_DIR=/root/.asdf -RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} && \ +RUN git clone https://github.com/asdf-vm/asdf.git ${ASDF_DIR} --branch v0.18.1 --depth 1 && \ . ${ASDF_DIR}/asdf.sh && \ asdf plugin add erlang && \ asdf plugin add elixir && \ From c8abeab0ffeede08aeed144d38382abb43fd90c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 9 May 2026 01:19:42 +0000 Subject: [PATCH 36/38] ci(windows): dedupe deps fetch after splitting Mix deps job Remove duplicate mix deps.get from windows-deps-compile now that windows-mix-deps owns fetching; skip cold-fetch when Mix/Hex cache hits to avoid redundant work. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 86 ++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 600b5f2..0f901c4 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -139,8 +139,8 @@ jobs: path: "c:\\opt\\otp.exe" key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 - # deps.compile vs mix compile each can approach GitHub's 360m/job limit on Windows. - windows-deps-compile: + # mix deps.get vs deps.compile each need up to 360m on Windows when caches are cold. + windows-mix-deps: runs-on: windows-latest needs: windows-toolchain timeout-minutes: 360 @@ -149,7 +149,7 @@ jobs: shell: wsl-bash {0} env: GIT_LFS_SKIP_SMUDGE: "1" - name: Build Windows deps compile (prod) + name: Build Windows Mix deps (fetch) steps: - name: Restore Windows Cache uses: actions/cache/restore@v3 @@ -217,12 +217,13 @@ jobs: key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} - name: "Get dependencies" + if: steps.mix-cache.outputs.cache-hit != 'true' shell: msys2 {0} run: | set -euo pipefail export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" - export HEX_HTTP_CONCURRENCY=8 + export HEX_HTTP_CONCURRENCY=16 export GIT_TERMINAL_PROMPT=0 git config --global http.lowSpeedLimit 500 git config --global http.lowSpeedTime 120 @@ -241,6 +242,83 @@ jobs: .ci_hex_home key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + + windows-deps-compile: + runs-on: windows-latest + needs: windows-mix-deps + timeout-minutes: 360 + defaults: + run: + shell: wsl-bash {0} + env: + GIT_LFS_SKIP_SMUDGE: "1" + name: Build Windows deps compile (prod) + steps: + - name: Restore Windows Cache + uses: actions/cache/restore@v3 + id: win32-cache + with: + path: "c:\\opt\\otp.exe" + key: win32-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }}-v4 + + - name: Verify OTP installer exists + shell: cmd + run: | + if not exist "C:\opt\otp.exe" ( + echo Missing C:\opt\otp.exe - toolchain job should have restored or built it. >&2 + exit /b 1 + ) + + - name: Install NSIS for installer step + shell: cmd + run: choco install nsis -y + + - name: Run Erlang installer + shell: cmd + run: C:\\opt\\otp.exe /S + + - name: "Install msys2" + uses: msys2/setup-msys2@v2 + with: + install: pacman-mirrors pkg-config base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-go upx mingw-w64-x86_64-dlfcn unzip git tar mingw-w64-x86_64-nodejs mingw-w64-x86_64-imagemagick mingw-w64-x86_64-osslsigncode autoconf automake libtool gettext-devel gettext + update: true + + - name: Locate Erlang + shell: msys2 {0} + run: | + ERTS=`find /c/Program\ Files/[Ee]rl* -type d -name "erts-*" -not -path "*lib*"` + echo $ERTS + echo export PATH=\"\$PATH:$ERTS/bin\" > $HOME/.bashrc + + - name: Locate NSIS + shell: msys2 {0} + run: | + echo export PATH=\"\$PATH:/c/Program\ Files\ \(x86\)/NSIS\" >> $HOME/.bashrc + + - name: Install Elixir + shell: msys2 {0} + run: | + cd $HOME + git clone --depth 1 --branch v${{ env.ELIXIR_VERSION }} --single-branch https://github.com/elixir-lang/elixir.git + cd elixir + make + echo export PATH=\"\$PATH:$HOME/elixir/bin\" >> $HOME/.bashrc + + - uses: actions/checkout@v4 + with: + lfs: false + fetch-depth: 1 + + - name: Restore Mix / Hex cache + id: mix-cache + uses: actions/cache/restore@v4 + with: + path: | + deps + .ci_mix_home + .ci_hex_home + key: windows-mix-${{ hashFiles('mix.lock') }}-elixir-${{ env.ELIXIR_VERSION }} + - uses: actions/setup-node@v4 with: node-version: '22' From 6e44ec71fcc34c068e5bf2507e855c0a13bab369 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 9 May 2026 07:28:43 +0000 Subject: [PATCH 37/38] ci(windows): compile deps with MSVC for OTP-matching NIFs exqlite failed when mingw gcc used Erlang OTP MSVC headers. Run ilammy/msvc-dev-cmd and mix deps.compile from cmd with Erlang + Elixir on PATH, matching exqlite Windows guidance. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 0f901c4..8c5bb94 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -328,20 +328,27 @@ jobs: working-directory: assets run: npm install + # Elixir is installed under MSYS $HOME; MSVC deps.compile needs a cmd-accessible path (exqlite uses MSVC/cl). + - name: Export Elixir bin path for MSVC deps compile + shell: msys2 {0} + run: echo "ELIXIR_BIN_WIN=$(cygpath -aw "$HOME/elixir/bin")" >> "$GITHUB_ENV" + + # Erlang/OTP Windows installer is MSVC-built; NIFs must not use MSYS2 gcc against OTP headers (see exqlite guides/windows.md). + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + - name: Compile prod dependencies (Mix only) env: - MAKE: make - MAKEFLAGS: -j4 - REBAR_TARGET_ARCH_WORDSIZE: 64 - REBAR_TARGET_ARCH: x86_64-w64-mingw32 - # Elixir 1.19+: parallel OS processes for deps (native/NIF builds); cuts wall time vs 1.16. + MIX_ENV: prod + # Elixir 1.19+: parallel deps compile processes (native/NIF); MSVC tools coexist with MSYS make/nmake. MIX_OS_DEPS_COMPILE_PARTITION_COUNT: "4" - shell: msys2 {0} + shell: cmd run: | - set -euo pipefail - export MIX_HOME="$GITHUB_WORKSPACE/.ci_mix_home" - export HEX_HOME="$GITHUB_WORKSPACE/.ci_hex_home" - export MIX_ENV=prod + set "MIX_HOME=%GITHUB_WORKSPACE%\.ci_mix_home" + set "HEX_HOME=%GITHUB_WORKSPACE%\.ci_hex_home" + set "PATH=C:\Program Files\Erlang OTP\bin;%ELIXIR_BIN_WIN%;%PATH%" + cd /d "%GITHUB_WORKSPACE%" mix deps.compile - name: Save Windows deps compile cache From 9a66aa71eec9a8826ba8f1fa7ff93684d1491252 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 9 May 2026 13:40:04 +0000 Subject: [PATCH 38/38] ci(windows): only set WIN32_KEY_PASS when secret is non-empty An empty secret still set WIN32_KEY_PASS to '', so desktop_deployment tried osslsigncode with a blank -pass and broke argument parsing. Export the variable from a conditional step so missing secrets skip signing. Co-authored-by: Dominic Letz --- .github/workflows/binaries.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 8c5bb94..7111896 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -876,12 +876,18 @@ jobs: working-directory: assets run: npm install + # If WIN32_KEY_PASS is set to "" (missing secret), desktop_deployment still + # attempts signing and osslsigncode misparses -pass, breaking -n "The ...". + # Only export the variable when the secret is non-empty so CI skips signing. + - name: Export WIN32_KEY_PASS for code signing (optional) + if: ${{ secrets.WIN32_KEY_PASS != '' }} + run: echo "WIN32_KEY_PASS=${{ secrets.WIN32_KEY_PASS }}" >> "$GITHUB_ENV" + - name: "Build Release" env: MAKE: make REBAR_TARGET_ARCH_WORDSIZE: 64 REBAR_TARGET_ARCH: x86_64-w64-mingw32 - WIN32_KEY_PASS: ${{ secrets.WIN32_KEY_PASS }} shell: msys2 {0} run: | set -euo pipefail