ElixirDesktopで作るスマホアプリ Part 1 PJ作成+CI設定

# はじめに
この記事はElixirアドベントカレンダー2024のシリーズ２、5日目の記事です

https://qiita.com/advent-calendar/2024/elixir

本記事では、実際にPaaS等にデプロイして以下の機能をもたせるWebダッシュボードと
- DBサーバー
- DBに対して頻繁に更新を行うような高負荷な処理のAPI切り出し
- 秘匿情報のデリバリ
- パスワードリセット
- ダッシュボード

スマホアプリのプロジェクトを作成してCIを構築する方法を紹介します。


# ダッシュボードWebアプリを作成

最初にダッシュボードWebアプリを作成します

## プロジェクトの作成

以下のコマンドでプロジェクトを作成した後、スマホアプリと同じモジュール名にしたいのでフォルダ名を変更します

```
mix phx.new trarecord
mv trarecord trarecord_server
```

以下のコマンドでDBを構築しWebサーバーを起動します

```
cd trarecord_server
mix ecto.create
iex -S mix phx.server
```

http://localhost:4000 にアクセスしてトップページが表示されれば成功です


![スクリーンショット 2024-11-26 10.26.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/5ce1e689-1dfa-7d65-4f0a-ba51280f5fc1.png)


## first commit and push

最初のコミットを行い、githubのリポジトリにpushします


![スクリーンショット 2024-11-24 0.28.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/e2df70c9-db91-e9a3-d59f-0f9cfd501b19.png)


```
git init
git add .
git commit -m 'init'
```

リポジトリのページに行くと、以下のコマンドでpushしましょうとあるので実行します

```
git remote add origin git@github.com:thehaigo/trarecord_server.git
git branch -M main
git push -u origin main
```

これでGithubにプロジェクトがpushされました

## CIの設定

次にCI(Continuous Integration)を以下を参考にして設定していきます

https://fly.io/phoenix-files/github-actions-for-elixir-ci/

https://qiita.com/mnishiguchi/items/8e709ef617a3c3deedaa


- `.github/workflows`フォルダを作成
- `.github/workflows/ci.yml` YAMLファイルを作成（ファイル名は任意）

内容を以下のようにします、


```ci.yml
name: CI

on:
  pull_request:
    paths-ignore:
      - ".gitignore"
      - "README.md"
  push:
    branches:
      - "main"
    paths-ignore:
      - ".gitignore"
      - "README.md"

jobs:
  test: 
    name: Build & Test
    runs-on: ubuntu-latest
    services:
      db:
        image: postgres:latest
        ports: ['5432:5432']
        env:
          POSTGRES_PASSWORD: postgres
        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Seup Erlang & Elixir
        uses: erlef/setup-beam@v1
        with:
          otp-version: '26.2.5'
          elixir-version: '1.17.2'

      - name: Cache deps and _build
        uses: actions/cache@v3
        with:
          path: |
            deps
            _build
          key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
          restore-keys: |
            ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}      

      - name: Install Dependencies      
        run: mix deps.get

      - name: Compiles without warnings
        run: mix compile --warnings-as-errors

      - name: Run Test
        run: mix test
```

上記の設定をmainにpushするとGettext周りで警告がでてCIがコケるので直していきます

![スクリーンショット 2024-11-25 0.55.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/d1b0c019-bf8e-fea8-f3c6-77521817ea3d.png)


8月には対応済みのようですが、まだリリースはされていない様子(最終リリースは６月）

https://github.com/phoenixframework/phoenix/pull/5902/files

```diff_elixir:lib/trarecord_web/gettext.ex:L23
- use Gettext, otp_app: :trarecord
+ use Gettext.Backend, otp_app: :trarecord
```

```diff_elixir:lib/trarecord_web.ex:L39
  def controller do
    quote do
      use Phoenix.Controller,
        formats: [:html, :json],
        layouts: [html: TrarecordWeb.Layouts]

      import Plug.Conn
-     import TrarecordWeb.Gettext
+     use Gettext, backend: TrarecordWeb.Gettext

      unquote(verified_routes())
    end
  end
```

```diff_elixir:lib/trarecord_web.ex:L83
  defp html_helpers do
    quote do
      # HTML escaping functionality
      import Phoenix.HTML
      # Core UI components and translation
      import TrarecordWeb.CoreComponents
-     import TrarecordWeb.Gettext
+     use Gettext, backend: TrarecordWeb.Gettext

      # Shortcut for generating JS commands
      alias Phoenix.LiveView.JS

      # Routes generation with the ~p sigil
      unquote(verified_routes())
    end
  end
```

```diff_elixir:lib/trarecord_web/components/core_components.ex:L17
  use Phoenix.Component
+ use Gettext, backend: TrarecordWeb.Gettext

  alias Phoenix.LiveView.JS
- import TrarecordWeb.Gettext
```

別のブランチを作ってPull Request（以下PR）を作成します

```
git checkout -b fix/gettext_warning
git add . 
git commit -m 'fix gettext warning'
git push origin fix/gettext_warning
```

PRを作成し、Github Actionsが実行されCIも無事通ったみたいなのでマージします

![スクリーンショット 2024-11-25 1.17.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/b91cd3b6-2cf6-3ad8-3d82-10d59ac79477.png)

これでCIが構築されたので、PRの作成mainへのpush時にテストが実行されるようになりました

あとはお好みで以下を追加しても良いかもしれません
- mix format -> コードフォーマッタ
- mix credo -> 静的解析

# スマホアプリ

次にスマホアプリのプロジェクトを作成していきます


## プロジェクトを作成

DBはすでにWebダッシュボードの方で作成しているので、最初のコミットのみを行います

```
mix phx.new trearecord
cd trarecord
git init 
git add .
git commit -m 'init'
```

githubにtrarecordリポジトリを作成し、pushを行いましょう

```
git remote add origin git@github.com:thehaigo/trarecord.git
git branch -M main
git push -u origin main
```


## スマホアプリ化

このままだとWebアプリでしかないのでスマホアプリ化を行っていきます

使用するライブラリに、`desktop_setup`を追加します

https://github.com/thehaigo/desktop_setup

```diff_elixir:mix.exs:33
  defp deps do
    [
      ...
-     {:bandit, "~> 1.5"}
+     {:bandit, "~> 1.5"},
      {:desktop_setup, github: "thehaigo/desktop_setup", only: :dev}
    ]
  end
```

追加したら以下のコマンドをおこなってデスクトップアプリ設定、iOS,Androidのプロジェクトの作成を行います

```
mix deps.get
mix desktop.install
mix desktop.setup.ios
mix desktop.setup.android
```

portがWebダッシュボードと被ってしまうので起動するポートの変更を行います

```diff_elixir:config/dev.exs:L19
config :trarecord, TrarecordWeb.Endpoint,
  # Binding to loopback ipv4 address prevents access from other machines.
  # Change to `ip: {0, 0, 0, 0}` to allow access from other machines.
- http: [ip: {127, 0, 0, 1}, port: 4000],
+ http: [ip: {127, 0, 0, 1}, port: 4001],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "PzZwrFHJWosit7pIVv5lPOPSSxExgVAgbQQYKHhwkaQX0Ek3HF5fF01//0Xyx3WN",
  watchers: [
    esbuild: {Esbuild, :install_and_run, [:trarecord, ~w(--sourcemap=inline --watch)]},
    tailwind: {Tailwind, :install_and_run, [:trarecord, ~w(--watch)]}
  ]

```

以下のコマンドで起動を確認できたら成功です

```
iex -S mix
```

![スクリーンショット 2024-11-26 10.55.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/99993e2d-df32-eace-a5cd-4156d933d9a0.png)

Xcode,AndroidStudioからのアプリ起動はそれぞれかMacは２日目、WSL,Linuxの方は3日目の記事を参考にしてください

https://qiita.com/the_haigo/items/5788b34c4b806020c0b9

https://qiita.com/the_haigo/items/1470f4231b95e5c1e680


起動に成功したので、ここで一旦コミットしておきます

```
git checkout -b feature/setup_desktop_app
git add .
git commit -m 'setup desktop'
```

## CIの設定

次にCIの設定を行っていきます

CIの設定はWebダッシュボードと同じ処理を行ってください（ci.ymlの作成,Gettext周りの修正）

```
git add .
git commit -m 'add ci'
git push origin feature/setup_desktop_app
```

pushを行いPRを作成するとDesktopアプリとして起動するライブラリが起動失敗しているので、テスト時のみ通常のWebアプリとして実行するように変更します


![スクリーンショット 2024-11-26 11.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/4fc3bf55-9b82-a690-b34c-b4dddb1cfda3.png)


desktopアプリの場合は`lib/trarecord.ex`を参照し、テスト時は`lib/trarecord/application.ex`を参照するようにします

```diff_elixir:mix.exs:L19
  def application do
-    [
-      mod: {Trarecord.Application, []},
-      extra_applications: [:logger, :runtime_tools]
-    ]
+    application(Mix.env()) 
  end

+ def application(:test) do
+   [
+     mod: {Trarecord.Application, []},
+     extra_applications: [:logger, :runtime_tools]
+   ]
+ end

+ def application(_) do
+   [
+     mod: {Trarecord, []},
+     extra_applications: [:logger, :runtime_tools]
+   ]
+ end
```

セッション管理をインメモリDBのETSを使用するようにしているので、etsを起動する処理を追加します

```diff_elixir:lib/trarecord/application.ex:L9
  def start(_type, _args) do
    children = [
      TrarecordWeb.Telemetry,
      Trarecord.Repo,
      {DNSCluster, query: Application.get_env(:trarecord, :dns_cluster_query) || :ignore},
      {Phoenix.PubSub, name: Trarecord.PubSub},
      # Start the Finch HTTP client for sending emails
      {Finch, name: Trarecord.Finch},
      # Start a worker by calling: Trarecord.Worker.start_link(arg)
      # {Trarecord.Worker, arg},
      # Start to serve requests, typically the last entry
      TrarecordWeb.Endpoint
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
+    :session = :ets.new(:session, [:named_table, :public, read_concurrency: true])
    opts = [strategy: :one_for_one, name: Trarecord.Supervisor]
    Supervisor.start_link(children, opts)
  end
```

```
git add .
git commit -m 'test run as web app'
git push origin feature/setup_desktop_app
```

CIが問題なくpassされるようになったのでマージをして完了になります

# 最後に
プロジェクトを作成して、CIの設定ができました
次はスマホアプリに認証機能を実装してきます

Webダッシュボードはスマホアプリの実装が粗方終わってから実装を行っていきます

本記事は以上になりますありがとうございました

# 参考サイト

https://qiita.com/mnishiguchi/items/8e709ef617a3c3deedaa
https://qiita.com/the_haigo/items/5788b34c4b806020c0b9
https://qiita.com/the_haigo/items/1470f4231b95e5c1e680
https://fly.io/phoenix-files/github-actions-for-elixir-ci/
https://github.com/erlef/setup-beam