All articles
CI/CD en profondeur : déployer une app React multi-environnement sur AWS S3 + CloudFront avec GitHub Actions

CI/CD en profondeur : déployer une app React multi-environnement sur AWS S3 + CloudFront avec GitHub Actions

Apprenez à construire et déployer une app React + Vite multi-environnement avec le Domain-Driven Design, GitHub Actions pour le CI/CD, et AWS S3 + CloudFront pour un hébergement statique rapide et économique. Configurations par environnement, workflows par branche et mise en place sécurisée du déploiement AWS inclus.

Résumer avec l'IA :

AM
Antonin Marxer
· 10 min read10

Tu lances un MVP et tu veux déployer ton app React avec un minimum d'infrastructure, tout en limitant les coûts ?
Dans ce tuto, tu vas apprendre à déployer une app React multi-environnement sur AWS S3.

Maintenir des standards de code

« Si tu veux aller vite, si tu veux finir rapidement, si tu veux que ton code soit facile à écrire, rends-le facile à lire »
Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

Pour qu'une app reste maintenable, elle doit être facile à lire. C'est ce qui rend sa suite de développement facile.

  1. Configure ton IDE pour le lint et le formatage automatique

Pour garder un style cohérent pendant le développement, on installe les extensions suivantes :

Dans les paramètres de VS Code (CTRL + ,) :

  • Mets « Prettier - Code formatter » comme formateur par défaut (Editor: Default Formatter).
  • Active « Editor: Format on Save ».
  1. Utilise le Domain-Driven Design (DDD) + une structure propre

Plutôt que de tout balancer dans un dossier components/ et de prier pour que ce soit clair, on organise le code par fonctionnalité métier, pas par type technique trop générique.

Chaque fonctionnalité métier a son propre dossier, avec ses composants, ses hooks, etc., formant ce qu'on appelle un bounded context.

C'est quoi, un bounded context en DDD ?
Imagine un modèle utilisateur. Dans la feature Auth, c'est un couple email/mot de passe. Dans la feature Admin, ce sont des rôles/permissions. Ces objets métier portent le même nom, mais leurs responsabilités diffèrent. D'où l'isolation dans des bounded contexts !

Un bounded context définit les limites de ta feature.

Structure du projet

  • src/features : un dossier par capacité métier (ex. : auth, theme, dashboard)
  • src/core : des composants réutilisables, indépendants de l'app (ex. : Button, Layout, useDebounce)
  • src/shared : de la logique/des composants réutilisables à l'échelle du projet, mais spécifiques à celui-ci
  • src/pages : chaque page (ex. : home, 404, etc.)
  • src/assets : les images et icônes globales
  1. Les bonnes pratiques du DDD
  • Regrouper les fichiers par feature
    Chaque feature gère ses propres composants et états, y compris ses slices Zustand sous features/X/store/X.slice.ts si tu utilises Zustand.
  • Exposer une API publique minimale via un barrel export
    Chaque feature a un fichier index.ts qui exporte tout ce qu'elle expose.
  • Garder core/ générique
    Ne pas laisser filtrer la logique des features dans core/
  • Shared = des utilitaires spécifiques au projet
  1. Et les anti-patterns du DDD ?
  • Ne pas mettre la logique métier dans core/
  • Ne pas utiliser de stores globaux dans src/stores/ pour la gestion d'état : ça casse la logique DDD.
  • Ne pas sur-abstraire
    Plus l'architecture est plate, mieux c'est. Keep It Simple, Stupid (KISS)
  1. Cas particuliers : le principe Open/Closed OCP de SOLID

Bonus : le principe Open/Closed pour la réutilisabilité
Dans features/theme/components, crée un ThemeToggleButton générique, puis un ConnectedThemeToggle qui se branche sur Zustand ou Redux. Boom : portable et extensible.

ThemeToggleButton est un composant fermé et réutilisable qui accepte des callbacks.

Hébergement : AWS S3 + CloudFront vs Netlify/Render/Cloudflare

Une app React, ce ne sont que des fichiers statiques (HTML/CSS/JS). Pourquoi ne pas l'héberger sur S3 pour quelques centimes par mois ?

Le contenu dynamique affiché sur ton site est récupéré côté client au chargement de la page. En clair, tu peux builder ton site et héberger les fichiers sur une solution d'hébergement statique, par exemple un bucket S3 quasi gratuit (HTTP).

S3 + CloudFront (hébergement statique + CDN + SSL)

  • Achète un nom de domaine chez un registrar comme porkbun, Namecheap, etc. Je te conseille de comparer les prix des domaines sur https://tld-list.com/
  • Délègue le DNS à AWS Route 53 ou à Cloudflare. Les records DNS indiquent comment traiter les requêtes et associent les adresses IP au domaine. Tu peux utiliser une hosted zone AWS Route 53 pour 0,50 €/mois, ou Cloudflare, qui fournit la gestion DNS et un pare-feu applicatif web gratuitement. Il y aura en revanche plus d'étapes de configuration DNS pour servir les distributions CloudFront. Le fournisseur DNS de ton choix te demandera d'enregistrer ses nameservers dans les paramètres DNS de ton registrar.
  • Configure SSL via AWS Certificate Manager
  • Sers le contenu via CloudFront (CDN + HTTPS + cache)
  • Upload le build statique vers un bucket S3

Les alternatives (Cloudflare Pages / Netlify / Render)

Elles offrent :

  • Un CI/CD reposant sur Git
  • Des domaines personnalisés faciles à brancher
  • Des fonctions serverless (comme Cloudflare Workers)
  • De bons plans gratuits pour les projets perso

Laquelle choisir ? Ça dépend de tes besoins et de ce que propose l'écosystème de chaque plateforme :

  • Cloudflare Pages est sans doute la meilleure plateforme gratuite pour les sites statiques, avec ses Workers, des fonctions serverless toujours exécutées à la edge, ce qui les rend incroyablement rapides.
  • Firebase , développé par Google, est parfait si tu veux des capacités backend comme la base de données et l'auth.

    À noter : le free tier de Firebase Authentication (Spark Plan) a de grosses limitations sur l'auth par téléphone (10k vérifications/mois aux US, ou ~100 à 300/mois dans les autres pays) et sur la MFA, tout simplement indisponible. Pour l'authentification, il n'y a même pas à hésiter : commence avec Supabase Cloud pour une mise en place rapide et un free tier généreux (50k Monthly Active Users (MAU) + 8 $/mois pour 100k MAU). Ensuite, si besoin, oublie l'abonnement et auto-héberge Supabase, qui est open-source. Depuis mars 2025, Supabase a lancé sa UI Library basée sur Tailwind CSS et ShadCN, le seul élément qui manquait à cette solution complète.

  • Render est une valeur sûre si tu as besoin de services statiques et backend au même endroit.

Déploiement multi-environnement

Quand ton app web commence à recevoir du trafic, tu as besoin d'une pré-version pour vérifier que tes changements ne cassent pas la production. Un projet sérieux a besoin de :

  • dev : instable, pour les tests en local
  • qa : pour les tests de l'équipe en interne
  • stg : les tests utilisateurs / UAT
  • prod : la production exposée aux clients

Ta configuration varie généralement d'un environnement à l'autre :

  • Des URLs d'API différentes : quand tu dépends d'APIs, staging et dev utilisent la version sandbox des APIs.
  • Des feature toggles différents : les feature flags permettent de désactiver une fonctionnalité pour diverses raisons :
    1) une fonctionnalité encore en développement actif a été fusionnée dans la codebase principale par souci d'agilité.
    2) une fonctionnalité publiée ne répond pas aux attentes des clients et a été désactivée le temps d'être retravaillée.

On va injecter la configuration au moment du build. En clair, on construit l'app une fois par environnement.

Les variables Vite

Vite expose les variables d'environnement sous forme de chaînes dans l'objet import.meta.env. Ces variables doivent être préfixées par VITE_ dans les fichiers .env. On les utilise comme ceci :

js
1
2
console.log(import.meta.env.VITE_API_ENDPOINT);
// resolves to "http://localhost:8080/"

Les constantes intégrées

Par défaut, quand on lance vite, NODE_ENV vaut development et .env.development est utilisé.
Quand on lance vite build, NODE_ENV vaut production et .env.production est utilisé.

Vite fournit les constantes suivantes :

  • import.meta.env.PROD : true si NODE_ENV vaut production
  • import.meta.env.DEV : true si NODE_ENV vaut development (toujours l'inverse de import.meta.env.PROD)
  • import.meta.env.SSR : indique si l'app tourne côté serveur.

Autre concept de Vite : le mode, qui prend par défaut les mêmes valeurs que NODE_ENV. Tu peux le surcharger en passant --mode, puis le lire via import.meta.env.MODE.
Si tu veux builder ton app en mode staging, crée un fichier .env.staging et lance vite build --mode staging.

Les configurations par environnement

Pour notre setup, définissons les fichiers .env.[mode] suivants :

  • .env.development : VITE_API_URI="http://localhost:8080/"
  • .env.qa : VITE_API_URI="https://api-qa.example.com/"
  • .env.stg : VITE_API_URI="https://api-staging.example.com/"
  • .env.production : VITE_API_URI="https://api.example.com/"
    Ajoute aussi la ligne VITE_APP_VERSION=$npm_package_version à chacun des fichiers .env. La version de l'app définie dans package.json devient ainsi accessible partout dans l'app via import.meta.env.VITE_APP_VERSION.

Ajoute les définitions de types pour tes variables d'environnement personnalisées dans src/vite-environment.d.ts, pour enrichir ImportMetaEnv :

src/vite-environment.d.ts
ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <reference types="vite/client" />
/// <reference types="vite/types/importMeta.d.ts" />
/// <reference types="vite-plugin-svgr/client" />
/// <reference types="vite/client" />
interface ViteTypeOptions {
// By adding this line, you can make the type of ImportMetaEnv strict
// to disallow unknown keys.
strictImportMetaEnv: unknown
}
// eslint-disable-next-line unicorn/prevent-abbreviations
interface ImportMetaEnv {
readonly VITE_APP_VERSION: string
readonly VITE_API_URI?: string
// more env variables...
}
interface ImportMeta {
readonly env: ImportMetaEnvironment
}

Dans package.json, définis une commande de build par environnement :

json
1
2
3
4
5
6
7
8
9
"scripts": {
"start": "vite",
"lint": "eslint .",
"test": "vitest run",
"build": "vite build",
"build:prod": "vite build",
"build:stg": "vite build --mode stg",
"build:qa": "vite build --mode qa",
}

CI — GitHub Actions : workflows automatisés et manuels

Déploiement automatique par nom de branche

Branche source Environnement
main production
staging staging
qa qa
release/* qa
feature/* qa
bugfix/* qa

Pour écouter les événements GitHub, crée un workflow (.github/workflows/deploy.yml) :

yaml
1
2
3
4
5
6
7
8
9
on:
push:
branches:
- 'main'
- 'staging'
- 'qa'
- 'release/*'
- 'feature/*'
- 'bugfix/*'

L'événement push se déclenche sur un push direct sur la branche, et quand une PR est fusionnée dedans.

Calcule le mode cible en fonction du contexte github.* du workflow GitHub :

yml
1
2
env:
MODE: (github.ref_name == 'main' && 'prod' || github.ref_name == 'staging' && 'stg' || 'qa') }}

La variable d'environnement ${{ env.MODE }} prend les valeurs 'prod' | 'staging' | 'qa'.

Dans l'étape de build, référence cette variable d'environnement :

yml
1
2
- name: Build project
run: npm run build:${{ env.MODE }}

Enfin, on upload l'artefact de l'app React buildée, pour le réutiliser dans le job de déploiement :

yml
1
2
3
4
5
6
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: web-dist
path: ./dist
retention-days: 1

Déploiements manuels avec workflow_dispatch

Déclenche les déploiements manuellement et choisis l'environnement depuis l'UI GitHub.

yml
1
2
3
4
5
6
7
8
9
on:
workflow_dispatch:
inputs:
environment:
type: choice
options:
- prod
- stg
- qa

En général, plusieurs features sont développées en même temps. Tout le monde ne peut pas pousser sa feature en cours de dev sur la branche staging. Pour laisser les devs déployer facilement sur cet environnement, on va utiliser workflow_dispatch.

D'abord, il faut écouter l'événement workflow_dispatch :

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
on:
workflow_dispatch:
inputs:
environment:
description: 'Select the environment to deploy to'
required: true
default: 'qa'
type: choice
options:
- qa
- staging
- production
push:
branches:
- 'main'
- 'staging'
- 'qa'
- 'release/*'
- 'feature/*'
- 'bugfix/*'

Modifie l'environnement de déploiement cible spécifiquement quand l'événement source est workflow_dispatch :

yml
1
2
env:
ENVIRONMENT: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment || (github.ref_name == 'main' && 'production' || github.ref_name == 'staging' && 'staging' || 'qa') }}

Dans l'onglet Actions de ton repo, tu peux choisir l'environnement cible sous Run workflow.

CD — Déploiement sur AWS (S3 + CloudFront)

On sait maintenant builder l'app avec la configuration de l'environnement cible, de façon automatisée comme manuelle via le workflow dispatch. De quoi garder de la flexibilité pour l'équipe de dev.

Chaque environnement est hébergé dans un bucket S3 séparé, pour assurer l'isolation.
Dans ce tuto, on part du principe que les buckets S3 existent déjà. La pipeline se contente d'uploader le nouveau livrable de l'app. Les buckets ne doivent être accessibles que via CloudFront pour profiter du cache, tu auras donc une distribution CloudFront par environnement.

Comme le site est purement statique, un dev peut aussi déployer vers l'environnement cible depuis sa machine grâce aux scripts suivants dans package.json :

json
1
2
3
4
5
6
7
8
9
10
11
12
{
...
"scripts" {
"build:prod": "vite build --mode prod",
"build:stg": "vite build --mode stg",
"build:dev": "vite build --mode dev",
"build": "vite build --mode prod",
"sync:prod": "aws s3 sync dist s3://prod-launchplate-react-primary && aws cloudfront create-invalidation --distribution-id XXXX1 --paths '/*'",
"sync:stg": "aws s3 sync dist s3://stg-launchplate-react-primary && aws cloudfront create-invalidation --distribution-id XXXX2 --paths '/*'",
"sync:dev": "aws s3 sync dist s3://dev-launchplate-react-primary && aws cloudfront create-invalidation --distribution-id XXXX3 --paths '/*'"
}
}

Remplace ces valeurs par tes noms de buckets et ton ID de distribution CloudFront.

On veut que le workflow GitHub fasse ces synchronisations S3 à notre place. Pour que le workflow puisse communiquer avec AWS, on peut utiliser l'authentification AWS OIDC, qui n'échange aucun secret.

1. Créer le provider IAM OIDC AWS

bash
1
2
3
4
5
# Create the OIDC provider in AWS
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list "a031c46782e6e6c662c2c87c76da9aa62ccabd8e"

Tu obtiens cette sortie :

json
1
2
3
{
"OpenIDConnectProviderArn": "arn:aws:iam::AWS_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
}

2. Créer un rôle IAM que GitHub Actions pourra assumer

Crée un rôle IAM nommé ReactLaunchplateGitHubActionsOIDCRole avec la trust policy suivante :

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<GITHUB_ORG>/<REPO_NAME>:*"
}
}
}
]
}

Remplace <AWS_ACCOUNT_ID> par ton ID de compte AWS, récupéré à l'étape précédente.
Remplace <GITHUB_ORG> et <REPO_NAME> par ton organisation et le nom de ton repo GitHub.

Tu peux aussi créer le rôle avec la CLI AWS. Crée un fichier oidc-provider-role.json contenant le JSON précédent, puis lance :

bin/sh
1
2
cd docs
aws iam create-role --role-name ReactLaunchplateGitHubActionsOIDCRole --assume-role-policy-document file://oidc-provider-role.json

En sortie, on obtient :

json
1
2
3
4
5
6
7
8
{
"Role": {
"Path": "/",
"RoleName": "ReactLaunchplateGitHubActionsOIDCRole",
"Arn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/ReactLaunchplateGitHubActionsOIDCRole",
...
}
}

3. Attacher des politiques au rôle

Il faut attacher les politiques nécessaires à ce rôle (ex. : S3FullAccess, CloudFrontFullAccess, Route53FullAccess, etc.)

D'abord, on crée la politique.
Crée un fichier nommé provider-policy.json avec le contenu suivant :

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:UpdateItem",
"cloudfront:*",
"acm:DescribeCertificate",
"acm:ListCertificates",
"acm:ListTagsForCertificate",
"acm:GetCertificate"
],
"Resource": [
"arn:aws:s3:::<PROJECT_NAME>-tfstate",
"arn:aws:s3:::<PROJECT_NAME>-tfstate/*",
"arn:aws:dynamodb:eu-west-3:YOUR_AWS_ACCOUNT_ID:table/<PROJECT_NAME>-tfstate-locks",
"arn:aws:s3:::*-<PROJECT_NAME>-primary",
"arn:aws:s3:::*-<PROJECT_NAME>-primary/*",
"arn:aws:s3:::*-<PROJECT_NAME>-failover",
"arn:aws:s3:::*-<PROJECT_NAME>-failover/*",
"arn:aws:s3:::*-<PROJECT_NAME>-log",
"arn:aws:s3:::*-<PROJECT_NAME>-log/*",
"arn:aws:cloudfront::<AWS_ACCOUNT_ID>:distribution/*",
"arn:aws:acm:us-east-1:<AWS_ACCOUNT_ID>:certificate/*",
"arn:aws:cloudfront::***:origin-access-identity/*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"acm:ListCertificates",
"cloudfront:ListDistributions"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": ["route53:GetHostedZone", "route53:ListResourceRecordSets"],
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Effect": "Allow",
"Action": ["route53:ChangeResourceRecordSets"],
"Resource": "arn:aws:route53:::hostedzone/*"
},
{
"Effect": "Allow",
"Action": ["route53:ListHostedZones"],
"Resource": "*"
}
]
}

Modifie oidc-provider-policy.json en remplaçant <PROJECT_NAME> par le nom de ton projet (ex. : launchplate-react) et <AWS_ACCOUNT_ID> par ton ID de compte AWS. Puis lance :

bin/sh
1
aws iam create-policy --policy-name ReactLaunchplateGitHubActionsOIDCRolePolicy --policy-document file://oidc-provider-policy.json

Tu obtiens cette sortie :

json
1
2
3
4
5
6
7
{
"Policy": {
"PolicyName": "ReactLaunchplateGitHubActionsOIDCRolePolicy",
"Arn": "arn:aws:iam::<AWS_ACCOUNT_ID>:policy/ReactLaunchplateGitHubActionsOIDCRolePolicy",
...
}
}

Une fois la politique créée, on peut l'attacher au rôle :

bin/sh
1
aws iam attach-role-policy --role-name ReactLaunchplateGitHubActionsOIDCRole --policy-arn arn:aws:iam::<AWS_ACCOUNT_ID>:policy/ReactLaunchplateGitHubActionsOIDCRolePolicy

Mettre à jour l'app avec aws sync

Le workflow GitHub s'authentifie auprès d'AWS via l'action aws-actions/configure-aws-credentials@v2, à laquelle on passe l'ARN du rôle à assumer et la région AWS ciblée.

Dans les secrets de ton repo, mets AWS_ROLE_ARN à arn:aws:iam::<AWS_ACCOUNT_ID>:role/ReactLaunchplateGitHubActionsOIDCRole.

Ajoute ce job au workflow :

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
env:
aws_region: eu-west-3
jobs:
...
deploy:
name: Web Sync to S3
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.aws_region }}
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: web-dist
path: ./dist
- name: Sync to S3 and invalidate CloudFront cache
run: npm run sync:${{ env.MODE }}

Ton site est maintenant synchronisé automatiquement sur S3, et le cache est invalidé pour rendre les changements visibles immédiatement.

Protéger les branches

Pour que le processus de review soit respecté, tu devrais interdire de pousser sur la branche main, via Settings > Rules > New branch ruleset.

  • Nom du ruleset : Branch protection rules
  • Cibles : Add target > Include default branch
  • Active les règles suivantes :
    • Restrict deletions
    • Require a pull request before merging.
      Je déconseille d'activer les approbations obligatoires : ça ralentit tout le processus de PR. J'aime la façon dont GitLab rend les approbations obligatoires tout en autorisant l'auto-approbation ; le fait de contourner les approbations devient alors conscient et fait prendre du recul : est-ce que ma PR demande des améliorations, ou puis-je l'approuver tout de suite ?

Tu peux ajouter un paramètre de bypass pour laisser certains rôles contourner le ruleset :
Bypass

Tu peux aussi autoriser les force push en passant par Classic Branch protection rules > Branches > Add rule.

  • Mets le pattern de nom de branche à *
  • Coche « Require a pull request before merging »
  • Coche « Require approvals » à 1 approbation.
  • Sous Rules applied to everyone including administrators, coche Allow force pushes, puis sélectionne Specify who can force push.

Le workflow GitHub final

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
name: AWS Deployment
on:
workflow_dispatch:
inputs:
environment:
description: 'Select the environment to deploy to'
required: true
default: 'qa'
type: choice
options:
- qa
- stg
- prod
push:
branches:
- 'main'
- 'staging'
- 'qa'
- 'release/*'
- 'feature/*'
- 'bugfix/*'
env:
MODE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment || (github.ref_name == 'main' && 'prod' || github.ref_name == 'staging' && 'stg' || 'qa') }}
jobs:
build:
name: Lint, Test & Build
runs-on: ubuntu-latest
outputs:
app_version: ${{ steps.get_version.outputs.app_version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20' # specify a version if needed
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
with:
version: latest
run_install: true
- name: Run Vitest tests
run: pnpm test
env:
CI: true
- name: Get App version
id: get_version
run: |
APP_VERSION=$(node -p "require('./package.json').version")
echo "app_version=$APP_VERSION" >> $GITHUB_OUTPUT
echo "App Version: $APP_VERSION"
- name: Build project
run: npm run build:${{ env.MODE }}
- name: Upload production-ready build files
id: deployment
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
name: Web Sync to S3
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.aws_region }}
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: web-dist
path: ./dist
- name: Sync to S3 and invalidate CloudFront cache
run: npm run sync:${{ env.MODE }}

Notifier l'équipe au déploiement

Je trouve ça très utile d'être notifié sur Slack à la fin du workflow :

  • Je sais immédiatement quand l'action GitHub est terminée et je peux aller vérifier les résultats.
  • Ça confirme aussi aux autres membres de l'équipe qu'une nouvelle version a été déployée.

Pour recevoir des notifications Slack, crée une Slack App avec un incoming webhook. Pour ça, suis le tuto de Slack.

Une fois ça fait, tu peux ajouter le job suivant à ton workflow GitHub :

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
slack:
name: Slack
needs: [build, deploy]
runs-on: ubuntu-latest
if: always()
steps:
- uses: technote-space/workflow-conclusion-action@v3
- uses: 8398a7/action-slack@v3
with:
status: custom
fields: workflow,job,commit,repo,ref,author,took
custom_payload: |
{
attachments: [{
color: '${{ env.WORKFLOW_CONCLUSION }}' === 'success' ? 'good' : '${{ env.WORKFLOW_CONCLUSION }}' === 'failure' ? 'danger' : 'warning',
text: `${process.env.AS_WORKFLOW} — *${{ env.WORKFLOW_CONCLUSION }}*
${process.env.AS_AUTHOR} deployed ${process.env.AS_REPO}@${process.env.AS_REF} (${process.env.AS_COMMIT}) in ${process.env.AS_TOOK}
*Environment:* ${{ needs.build.outputs.app_version }}`
}]
}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Il n'y a qu'à renseigner l'URL du webhook Slack dans les secrets de ton repo GitHub, car secrets.GITHUB_TOKEN est défini automatiquement.

Pour finir

Tu as maintenant :

  • Un template de projet React moderne
  • Les bonnes pratiques d'architecture DDD
  • Un CI/CD pour des déploiements multi-env
  • Un hébergement de site statique S3 + CloudFront
  • La tranquillité d'esprit et de quoi te vanter
  • Envie de sauter le boilerplate ? Clone juste le repo : launchplate-react-terraform ;)

Comments (0)

Log in to leave a comment.

No comments yet. Be the first!

0

Launcher