Intégration continue pour un site Phoenix LiveView (sans Postgres)
Dans le cadre de la construction et du maintien d'un site LiveView fiable, un pipeline CI solide est essentiel. Ci-dessous, je vais vous guider à travers la configuration CI que nous utilisons pour ce site, qui est construit avec Phoenix (Elixir), et déployé en utilisant Kamal (un autre article sera écrit à ce sujet !) et Docker. Au moment où nous écrivons ces lignes, l'application ne repose pas sur une base de données comme Postgres, mais nous avons laissé le support commenté pour une expansion future.
Nous utilisons GitHub Actions pour CI, ce qui nous donne des environnements propres et isolés et une bonne intégration avec Docker Hub et les versions versionnées.
1. Déclencheurs
on:
push:
branches: [main]
tags:
- "*"
pull_request:
branches: [main]
La pipeline CI s'exécute automatiquement :
-
Lors de tout push vers le
mainbranche - Quand un nouveau tag est créé (utilisé pour la création de la Docker Image)
-
Sur les pull requests vers
main
2. Qu'est-ce que Mise ?
Mise est un gestionnaire de versions moderne qui prend en charge plusieurs langages et outils — similaire à asdf, mais plus rapide et plus facile à configurer. En utilisant un seul fichier de configuration (`mise.toml`), vous pouvez définir les versions exactes d'outils comme Elixir, Erlang, Ruby, et même des environnements JavaScript comme Bun.
En développement local comme en CI, cela garantit que tout le monde — et chaque tâche — utilise les mêmes versions. Plus de surprises entre votre machine et GitHub Actions !
2.1 Version d'outils définit
[tools]
bun = "1.2.11"
elixir = "1.18.3"
erlang = "27.3.4"
ruby = "3.4.2"
Cette configuration garantit des environnements d'exécution cohérents :
-
bunfor asset management -
elixiranderlangfor the Phoenix backend -
rubyfor tools like Kamal used in deployment
2.2 Tâches réutilisables
Mise vous permet également de définir des tâches CLI, vous pouvez donc standardiser les flux de travail courants comme la configuration, les nettoyages et les assistants de déploiement. Ces tâches sont excellentes pour l'intégration et l'automatisation.
[tasks.reset]
description = "Reset and recompile project"
run = "rm -rf deps _build .elixir_ls && mix deps.get && mix && mix test"
[tasks."clean:images"]
description = "Delete images folders"
run = "rm -rf priv/waffle"
[tasks."setup:kamal"]
description = "Install kamal to manage deployment"
run = "gem install kamal"
[tasks."assets:install"]
description = "Install JS assets"
run = "bun install --cwd assets"
2.3 'Mise' dans la CI
Dans la CI, nous utilisons l'officiel `jdx/mise-action` pour installer et mettre en cache ces outils rapidement. Cela accélère les builds, évite les versions incompatibles et maintient des environnements CI miroirs des environnements de développement locaux.
En mettant en cache les artefacts de mixage et de construction par version d'outil et par environnement, nous gagnons également du temps sur les exécutions répétées de CI :
key: ${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-${{ hashFiles('**/mix.lock') }}
3. Apreçu des jobs
Le workflow définit quatre jobs. Examinons-les !
3.1 Outils
Ce job initialise l'environnement CI en installant tous les outils requis en utilisant
mise. Il assure la cohérence entre le développement et la CI en alignant les versions des outils définies dans
mise.toml.
tools:
name: TOOLS
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up tools
uses: jdx/mise-action@v2
3.2 Lint
Le job LINT applique les règles de qualité de code et de sécurité. Il vérifie le formatage, les dépendances inutilisées, les vulnérabilités de sécurité et les problèmes stylistiques.
Des outils comme
credo, sobelow, hex.audit, et
mix deps.unlock --check-unused
sont utilisés pour garantir que le code reste propre et sécurisé.
lint:
name: LINT
needs: [tools]
runs-on: ubuntu-24.04
env:
MIX_ENV: dev
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up tools
uses: jdx/mise-action@v2
- name: Cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-
${{ env.MIX_ENV }}-mise-
- name: Install dependencies
run: mix deps.get
- name: Compile without warnings
run: mix compile --warnings-as-errors
- name: Check code format
run: mix format --dry-run --check-formatted
- name: Check unused deps
run: mix deps.unlock --check-unused
- name: Hex audit
run: mix hex.audit
- name: Deps audit
run: mix deps.audit
- name: Run credo
run: mix credo
- name: Run sobelow
run: mix sobelow --config
3.3 Test
Ce job exécute la suite de tests avec
mix test. Il garantit que l'application se comporte comme prévu.
PostgreSQL est commenté pour l'instant car le projet ne nécessite pas actuellement de base de données, mais la configuration est prête pour une utilisation future.
test:
name: TEST
needs: [tools]
runs-on: ubuntu-24.04
env:
MIX_ENV: test
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up tools
uses: jdx/mise-action@v2
- name: Cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-
${{ env.MIX_ENV }}-mise-
- name: Install dependencies
run: mix deps.get
- name: Run tests
run: mix test --max-failures=1 --color
3.4 Build
Le job BUILD compile l'application, construit les assets et génère une release. Il utilise les paramètres de production pour s'assurer que la sortie compilée correspond à ce qui sera déployé. Si un tag Git est poussé, il construit également et pousse une image Docker vers Docker Hub en utilisant les secrets configurés.
build:
name: BUILD
needs: [lint, test]
runs-on: ubuntu-24.04
env:
MIX_ENV: prod
PHX_SERVER: true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up tools
uses: jdx/mise-action@v2
with:
cache_key_prefix: mise-${{ runner.os }}
- name: Cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.MIX_ENV }}-mise-${{ hashFiles('**/mise.toml') }}-mix-
${{ runner.os }}-${{ env.MIX_ENV }}-mise-
- name: Install web dependencies
run: mise run assets:install
- name: Install backend dependencies
run: mix deps.get --only prod
- name: Build
run: MIX_ENV=${{ env.MIX_ENV }} mix do assets.setup, assets.deploy, compile --warnings-as-errors, release --overwrite
- name: Docker meta
if: ${{ contains(github.ref, 'refs/tags/') }}
uses: docker/metadata-action@v5
id: meta
with:
images: ${{ env.DOCKER_IMAGE }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Set up docker buildx
if: ${{ contains(github.ref, 'refs/tags/') }}
id: buildx
uses: docker/setup-buildx-action@v3
- name: Login to docker hub
if: ${{ contains(github.ref, 'refs/tags/') }}
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Release
if: ${{ contains(github.ref, 'refs/tags/') }}
uses: docker/build-push-action@v6
with:
context: .
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
builder: ${{ steps.buildx.outputs.name }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
4. Publication d'une image Docker
Sur chaque commit tagué, le flux de travail:
- Construit la version Phoenix avec des actifs statiques
- Generates Docker metadata and tags
- Se connecte à Docker Hub en utilisant des secrets
- Construit et pousse l'image en utilisant build-push-action
5. Conclusion
This setup gives us confidence in code quality and makes production releases seamless Si vous construisez un site Phoenix LiveView sans base de données, c'est une bonne base. Lorsque vous êtes prêt à activer Postgres, les fondations sont déjà là.