Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
21e3b66
Add AGENTS.md with cloud-specific development instructions
cursoragent May 4, 2026
e1b64f4
docs: fix SQLite spelling in TodoApp moduledoc
cursoragent May 4, 2026
218c22e
AGENTS.md: clarify assets.deploy vs dev watchers, lint env, Git LFS
cursoragent May 4, 2026
9e337d4
Clarify AGENTS.md: DB path, dev run row, Android note
cursoragent May 4, 2026
b637d2e
Fix CI: skip desktop installer for mobile releases; install NSIS for …
cursoragent May 4, 2026
4abd6f1
AGENTS.md: note CI uses different Elixir/OTP than .tool-versions
cursoragent May 4, 2026
6407399
Android run_mix: mkdir assets dir before writing app.zip
cursoragent May 4, 2026
4eb7f1a
docs(AGENTS): clarify prod asset build and npm install step
cursoragent May 4, 2026
8575b39
CI (Windows): use WSL Ubuntu 22.04 for NSIS 3; extend job timeout
cursoragent May 4, 2026
a928006
CI (Windows): use Chocolatey NSIS + WSL wrapper for makensis.exe
cursoragent May 4, 2026
2fb7a1b
CI (Windows): retry apt-get in WSL when mirrors are unreachable
cursoragent May 4, 2026
af21421
CI (Windows): xcopy OpenSSL to C:\OpenSSL-Win64 (avoid move access de…
cursoragent May 4, 2026
d868ac0
CI (Windows): raise binary job timeout to 300 minutes
cursoragent May 4, 2026
b7279b8
CI (macOS): use macos-15 runner instead of deprecated macos-13
cursoragent May 5, 2026
8814182
CI (macOS): use Homebrew libpng for wxWidgets (fix fp.h on macOS 15)
cursoragent May 5, 2026
a1fbcb7
CI (Windows): avoid Git/LFS checkout stalls; shallow clones; 20m job cap
cursoragent May 5, 2026
3c86b88
CI (macOS): point wx configure at Homebrew libpng (CPPFLAGS/LDFLAGS)
cursoragent May 5, 2026
9347681
CI (macOS): Homebrew libtiff + resilient shallow wxWidgets clone
cursoragent May 5, 2026
4b77da7
CI (macOS): install asdf via Homebrew (v0.19 has no asdf.sh)
cursoragent May 5, 2026
458475e
CI (macOS): fix KERL CXX flags for asdf-erlang on Apple Clang
cursoragent May 5, 2026
02ac245
CI (macOS): do not pass MACOS_PEM into Build Release on CI
cursoragent May 5, 2026
0246702
CI (Windows): raise job timeout to 90 minutes
cursoragent May 5, 2026
2735ec1
CI (Windows): extend job timeout to 180 minutes
cursoragent May 5, 2026
42ffd11
CI (Windows): use maximum job timeout (360 minutes)
cursoragent May 5, 2026
90e8ee9
CI (Windows): cache deps + Hex/Mix homes; faster Hex downloads
cursoragent May 6, 2026
5d60996
fix(ci): speed Windows mix deps with shallow git clones and prod-only…
cursoragent May 6, 2026
59d86d8
fix(deps): drop git :depth with :ref for Mix 1.17 (Android & Linux CI)
cursoragent May 6, 2026
456d6d4
fix(ci): split Windows toolchain and installer jobs for 360m limit
cursoragent May 6, 2026
b0ae0c3
fix(ci): split Windows assemble vs installer to beat 360m job timeout
cursoragent May 7, 2026
e34e5a9
fix(ci): split Windows compile/assets from release assemble
cursoragent May 7, 2026
4ea20d0
fix(ci): split Windows prod compile from assets.deploy
cursoragent May 7, 2026
161f800
fix(ci): avoid MSYS2 npm segfault on Windows (npm install exit 139)
cursoragent May 8, 2026
1721b2f
fix(ci): split Windows mix deps.compile from mix compile
cursoragent May 8, 2026
54a5622
fix(ci): speed Windows deps.compile with Elixir 1.19 parallel partitions
cursoragent May 8, 2026
01da0c6
fix(docker): pin asdf v0.18.1 — master no longer ships asdf.sh
cursoragent May 8, 2026
c8abeab
ci(windows): dedupe deps fetch after splitting Mix deps job
cursoragent May 9, 2026
6e44ec7
ci(windows): compile deps with MSVC for OTP-matching NIFs
cursoragent May 9, 2026
9a66aa7
ci(windows): only set WIN32_KEY_PASS when secret is non-empty
cursoragent May 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
873 changes: 822 additions & 51 deletions .github/workflows/binaries.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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 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

```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_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_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

- **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. 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".
- **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.
2 changes: 1 addition & 1 deletion lib/todo_app.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.

Expand Down
42 changes: 34 additions & 8 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
]
]
]
Expand All @@ -32,6 +28,30 @@ 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]

_ ->
# 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

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
Expand Down Expand Up @@ -90,8 +110,11 @@ defmodule Todo.MixProject do
deps_list = [
# {:desktop, path: "../desktop"},
# {:desktop, "~> 1.5"},
{:desktop, github: "elixir-desktop/desktop"},
{:desktop_deployment, github: "elixir-desktop/deployment"},
# 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"},
{:desktop_deployment,
github: "elixir-desktop/deployment", ref: "c5c09864693121acb7e6de0f8f3253937ed2e07c"},
{:igniter, "~> 0.6"},
# {:desktop_deployment, path: "../deployment", runtime: false},

Expand All @@ -100,7 +123,10 @@ 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},
{:phoenix_html, "~> 4.1"},
{:phoenix_live_dashboard, "~> 0.8.3"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
Expand Down
6 changes: 3 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"]},
"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"},
Expand All @@ -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"]},
"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"},
Expand Down
1 change: 1 addition & 0 deletions rel/android/app/run_mix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion scripts/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
2 changes: 1 addition & 1 deletion scripts/Dockerfile.cef
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
2 changes: 1 addition & 1 deletion scripts/Dockerfile.raspbian
Original file line number Diff line number Diff line change
Expand Up @@ -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 && \
Expand Down
Loading