diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 9f41624..d6b5500 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -12,10 +12,14 @@ env: jobs: windows: runs-on: windows-latest - timeout-minutes: 90 + # 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: - name: Restore Windows Cache @@ -23,31 +27,73 @@ 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 }}-v4 - uses: Vampire/setup-wsl@v2 if: steps.win32-cache.outputs.cache-hit != 'true' with: - distribution: 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 + run: | + 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 + - 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 - 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") + 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" ( + 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' + 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' 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 @@ -77,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 @@ -96,7 +143,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 }}-v4 - name: Run Erlang installer shell: cmd @@ -124,20 +171,52 @@ 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: 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 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' + 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} @@ -152,6 +231,10 @@ 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" + export MIX_ENV=prod mix assets.deploy mix desktop.installer @@ -163,49 +246,78 @@ jobs: _build/prod/*.exe macos: - runs-on: macos-13 + 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: macos-wxwidgets-${{ env.WXWIDGETS_VERSION }}-otp-${{ env.OTP_VERSION }} + 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' run: | - brew install binutils coreutils wget automake autoconf libtool + 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 - ./configure --prefix=/usr/local/wxWidgets --enable-webview --enable-compat30 --disable-shared + 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)" + 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" 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 + # 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 - 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 @@ -216,10 +328,11 @@ jobs: cd assets && npm install - name: "Build Release" - env: - MACOS_PEM: ${{ secrets.MACOS_PEM }} run: | - . $HOME/.asdf/asdf.sh + 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/" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6aa6535 --- /dev/null +++ b/AGENTS.md @@ -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. 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()`. diff --git a/mix.exs b/mix.exs index 1512534..5ba584c 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"] @@ -90,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}, @@ -100,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"}, 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