Skip to content

Commit bfff3b6

Browse files
authored
Merge pull request #8 from benamarfaiez/feature-ci-cd
Introduce CI/CD workflow, frontend Dockerization, and Docker Compose
2 parents 2072b1e + 09ebb7d commit bfff3b6

31 files changed

Lines changed: 5546 additions & 2716 deletions

.github/workflows/ci-cd.yml

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,41 @@ name: CI / CD – TaskFlow
33
on:
44
push:
55
branches: [ main, develop ]
6+
paths:
7+
- 'Backend/**'
8+
- 'Frontend/**'
9+
- '.github/workflows/**'
610
pull_request:
711
branches: [ main, develop ]
12+
paths:
13+
- 'Backend/**'
14+
- 'Frontend/**'
15+
- '.github/workflows/**'
816

917
jobs:
18+
changes:
19+
runs-on: ubuntu-latest
20+
outputs:
21+
backend: ${{ steps.filter.outputs.backend }}
22+
frontend: ${{ steps.filter.outputs.frontend }}
23+
steps:
24+
- uses: actions/checkout@v4
25+
- uses: dorny/paths-filter@v3
26+
id: filter
27+
with:
28+
filters: |
29+
backend:
30+
- 'Backend/**'
31+
- 'Backend/Dockerfile'
32+
frontend:
33+
- 'Frontend/**'
34+
- 'Frontend/Dockerfile'
35+
1036
# 1. Checkout + Setup .NET
11-
setup:
37+
setup-backend:
1238
runs-on: ubuntu-latest
39+
needs: changes
40+
if: ${{ needs.changes.outputs.backend == 'true' }}
1341
outputs:
1442
cache-key: ${{ steps.cache.outputs.cache-key }}
1543
steps:
@@ -32,8 +60,8 @@ jobs:
3260
restore-keys: ${{ runner.os }}-nuget-
3361

3462
# 2. Restore dependencies + Build
35-
build:
36-
needs: setup
63+
build-backend:
64+
needs: setup-backend
3765
runs-on: ubuntu-latest
3866
steps:
3967
- name: Checkout code
@@ -51,8 +79,8 @@ jobs:
5179
run: dotnet build ./Backend/FlowTasks.sln --configuration Release --no-restore
5280

5381
# 3. Unit Tests + Coverage
54-
test:
55-
needs: build
82+
test-backend:
83+
needs: build-backend
5684
runs-on: ubuntu-latest
5785
services:
5886
postgres:
@@ -97,8 +125,8 @@ jobs:
97125
path: "**/TestResults/*.trx"
98126

99127
# 4. Code Quality
100-
quality:
101-
needs: test
128+
quality-backend:
129+
needs: test-backend
102130
runs-on: ubuntu-latest
103131
steps:
104132
- name: Checkout code
@@ -122,9 +150,8 @@ jobs:
122150
-Dsonar.qualitygate.wait=true
123151
124152
# 5. Build & Push Docker Image
125-
docker:
126-
needs: quality
127-
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
153+
docker-backend:
154+
needs: build-backend
128155
runs-on: ubuntu-latest
129156
permissions:
130157
packages: write
@@ -148,8 +175,93 @@ jobs:
148175
uses: docker/build-push-action@v6
149176
with:
150177
context: .
178+
file: ./Backend/Dockerfile
179+
push: true
180+
tags: |
181+
ghcr.io/${{ github.repository_owner }}/taskflow:latest
182+
ghcr.io/${{ github.repository_owner }}/taskflow:${{ github.sha }}
183+
ghcr.io/${{ github.repository_owner }}/taskflow:${{ github.ref_name == 'main' && 'stable' || 'dev' }}
184+
185+
# ======================== FRONTEND JOBS ========================
186+
187+
setup-frontend:
188+
needs: changes
189+
if: ${{ needs.changes.outputs.frontend == 'true' }}
190+
runs-on: ubuntu-latest
191+
steps:
192+
- name: Checkout code
193+
uses: actions/checkout@v4
194+
195+
- name: Setup Node.js
196+
uses: actions/setup-node@v4
197+
with:
198+
node-version: 20
199+
cache: 'npm'
200+
cache-dependency-path: Frontend/package-lock.json
201+
202+
- name: Install dependencies
203+
run: cd Frontend && npm ci --legacy-peer-deps
204+
205+
- name: Lint Frontend
206+
run: cd Frontend && npm install && npx ng lint
207+
208+
# Décommente si tu ajoutes des tests Angular
209+
# - name: Run tests
210+
# run: cd Frontend && npm run test -- --watch=false --browsers=ChromeHeadless
211+
212+
build-frontend:
213+
needs: setup-frontend
214+
runs-on: ubuntu-latest
215+
steps:
216+
- name: Checkout code
217+
uses: actions/checkout@v4
218+
219+
- name: Setup Node.js
220+
uses: actions/setup-node@v4
221+
with:
222+
node-version: 20
223+
cache: 'npm'
224+
cache-dependency-path: Frontend/package-lock.json
225+
226+
- name: Install & Build
227+
run: |
228+
cd Frontend
229+
npm ci --legacy-peer-deps
230+
npm run build -- --configuration production
231+
232+
- name: Upload build artifact
233+
uses: actions/upload-artifact@v4
234+
with:
235+
name: frontend-dist
236+
path: Frontend/dist/
237+
238+
docker-frontend:
239+
needs: build-frontend
240+
runs-on: ubuntu-latest
241+
permissions:
242+
packages: write
243+
contents: read
244+
steps:
245+
- name: Checkout code
246+
uses: actions/checkout@v4
247+
248+
- name: Set up Docker Buildx
249+
uses: docker/setup-buildx-action@v3
250+
251+
- name: Login to GitHub Container Registry
252+
uses: docker/login-action@v3
253+
with:
254+
registry: ghcr.io
255+
username: ${{ github.actor }}
256+
password: ${{ secrets.GITHUB_TOKEN }}
257+
258+
- name: Build and push frontend image
259+
uses: docker/build-push-action@v6
260+
with:
261+
context: ./Frontend
262+
file: ./Frontend/Dockerfile
151263
push: true
152264
tags: |
153-
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:latest
154-
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:${{ github.sha }}
155-
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:${{ github.ref_name == 'main' && 'stable' || 'dev' }}
265+
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:latest
266+
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:${{ github.sha }}
267+
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:${{ github.ref_name == 'main' && 'stable' || 'dev' }}

Backend/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# FlowTasks - Backend API
22

3-
Backend complet pour une application de gestion de projets et de tâches similaire à Jira, développé avec .NET 8.
3+
Backend complet pour une application de gestion de projets et de tâches, développé avec .NET 8.
44

55
## 🚀 Stack Technique
66

Backend/dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Étape de build
2+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
3+
WORKDIR /app
4+
5+
COPY Backend/FlowTasks.sln ./
6+
COPY Backend/src/FlowTasks.API/FlowTasks.API.csproj ./src/FlowTasks.API/
7+
COPY Backend/src/FlowTasks.Application/FlowTasks.Application.csproj ./src/FlowTasks.Application/
8+
COPY Backend/src/FlowTasks.Domain/FlowTasks.Domain.csproj ./src/FlowTasks.Domain/
9+
COPY Backend/src/FlowTasks.Tests/FlowTasks.Tests.csproj ./src/FlowTasks.Tests/
10+
COPY Backend/src/FlowTasks.Infrastructure/FlowTasks.Infrastructure.csproj ./src/FlowTasks.Infrastructure/
11+
12+
RUN dotnet restore
13+
14+
COPY Backend/src/FlowTasks.API/. ./src/FlowTasks.API/
15+
COPY Backend/src/FlowTasks.Application/. ./src/FlowTasks.Application/
16+
COPY Backend/src/FlowTasks.Domain/. ./src/FlowTasks.Domain/
17+
COPY Backend/src/FlowTasks.Infrastructure/. ./src/FlowTasks.Infrastructure/
18+
COPY Backend/src/FlowTasks.Tests/. ./src/FlowTasks.Tests/
19+
20+
# Build & Publish l'API
21+
WORKDIR /app/src/FlowTasks.API
22+
RUN dotnet publish -c Release -o /app/publish --no-restore
23+
24+
# Étape runtime
25+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
26+
WORKDIR /app
27+
COPY --from=build /app/publish .
28+
ENTRYPOINT ["dotnet", "FlowTasks.API.dll"]

Backend/src/FlowTasks.API/Controllers/AuthController.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,3 @@ public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest
102102
}
103103
}
104104
}
105-

Backend/src/FlowTasks.API/FlowTasks.API.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
7+
<UserSecretsId>edb59238-8d13-4f8f-be87-065b28b31de9</UserSecretsId>
78
</PropertyGroup>
89

910
<ItemGroup>

Backend/src/FlowTasks.API/Program.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@
6161
});
6262
});
6363

64+
var dbConnectin = builder.Configuration["DbConnection"];
6465
// Database configuration
6566
builder.Services.AddDbContext<ApplicationDbContext>(options =>
66-
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
67+
options.UseNpgsql(dbConnectin));
6768

6869
// Identity configuration
6970
builder.Services.AddIdentity<User, IdentityRole>(options =>
@@ -90,7 +91,7 @@
9091
.AddInMemoryStorage();
9192

9293
// JWT Authentication
93-
var jwtKey = builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key not configured");
94+
var jwtKey = builder.Configuration["JwtKey"] ?? throw new InvalidOperationException("JWT Key not configured");
9495
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "FlowTasks";
9596
var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "FlowTasks";
9697

@@ -115,12 +116,13 @@
115116

116117
builder.Services.AddAuthorization();
117118

119+
var UrlFrontend = builder.Configuration["UrlFrontend"];
118120
// CORS configuration
119121
builder.Services.AddCors(options =>
120122
{
121123
options.AddPolicy("AllowAngular", policy =>
122124
{
123-
policy.WithOrigins("http://localhost:4200")
125+
policy.WithOrigins(UrlFrontend)
124126
.AllowAnyHeader()
125127
.AllowAnyMethod()
126128
.AllowCredentials();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning",
6+
"Microsoft.EntityFrameworkCore": "Information"
7+
}
8+
},
9+
"AllowedHosts": "*",
10+
"ConnectionStrings": {
11+
"DefaultConnection": ""
12+
},
13+
"Jwt": {
14+
"Key": "",
15+
"Issuer": "FlowTasks",
16+
"Audience": "FlowTasks"
17+
}
18+
}
19+

Frontend/Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM node:20-alpine AS build
2+
3+
WORKDIR /src
4+
COPY package*.json ./
5+
RUN npm ci --legacy-peer-deps
6+
COPY . .
7+
RUN npm run build -- --configuration production
8+
FROM nginx:alpine
9+
10+
COPY --from=build /dist/taskflow /usr/share/nginx/html
11+
12+
EXPOSE 80
13+
14+
CMD ["nginx", "-g", "daemon off;"]

Frontend/angular.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,23 @@
9797
],
9898
"scripts": []
9999
}
100+
},
101+
"lint": {
102+
"builder": "@angular-eslint/builder:lint",
103+
"options": {
104+
"lintFilePatterns": [
105+
"src/**/*.ts",
106+
"src/**/*.html"
107+
]
108+
}
100109
}
101110
}
102111
}
103112
},
104113
"cli": {
105-
"analytics": "40863e82-8a07-4bb9-8a8c-a5f94f50a9f9"
114+
"analytics": "40863e82-8a07-4bb9-8a8c-a5f94f50a9f9",
115+
"schematicCollections": [
116+
"angular-eslint"
117+
]
106118
}
107119
}

Frontend/eslint.config.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// @ts-check
2+
const eslint = require("@eslint/js");
3+
const { defineConfig } = require("eslint/config");
4+
const tseslint = require("typescript-eslint");
5+
const angular = require("angular-eslint");
6+
7+
module.exports = defineConfig([
8+
{
9+
files: ["**/*.ts"],
10+
extends: [
11+
eslint.configs.recommended,
12+
tseslint.configs.recommended,
13+
tseslint.configs.stylistic,
14+
angular.configs.tsRecommended,
15+
],
16+
processor: angular.processInlineTemplates,
17+
rules: {
18+
"@angular-eslint/directive-selector": [
19+
"error",
20+
{
21+
type: "attribute",
22+
prefix: "app",
23+
style: "camelCase",
24+
},
25+
],
26+
"@angular-eslint/component-selector": [
27+
"error",
28+
{
29+
type: "element",
30+
prefix: "app",
31+
style: "kebab-case",
32+
},
33+
],
34+
},
35+
},
36+
{
37+
files: ["**/*.html"],
38+
extends: [
39+
angular.configs.templateRecommended,
40+
angular.configs.templateAccessibility,
41+
],
42+
rules: {},
43+
}
44+
]);

0 commit comments

Comments
 (0)