Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 125 additions & 13 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,41 @@ name: CI / CD – TaskFlow
on:
push:
branches: [ main, develop ]
paths:
- 'Backend/**'
- 'Frontend/**'
- '.github/workflows/**'
pull_request:
branches: [ main, develop ]
paths:
- 'Backend/**'
- 'Frontend/**'
- '.github/workflows/**'

jobs:
changes:
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
backend:
- 'Backend/**'
- 'Backend/Dockerfile'
frontend:
- 'Frontend/**'
- 'Frontend/Dockerfile'

# 1. Checkout + Setup .NET
setup:
setup-backend:
runs-on: ubuntu-latest
needs: changes
if: ${{ needs.changes.outputs.backend == 'true' }}
outputs:
cache-key: ${{ steps.cache.outputs.cache-key }}
steps:
Expand All @@ -32,8 +60,8 @@ jobs:
restore-keys: ${{ runner.os }}-nuget-

# 2. Restore dependencies + Build
build:
needs: setup
build-backend:
needs: setup-backend
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand All @@ -51,8 +79,8 @@ jobs:
run: dotnet build ./Backend/FlowTasks.sln --configuration Release --no-restore

# 3. Unit Tests + Coverage
test:
needs: build
test-backend:
needs: build-backend
runs-on: ubuntu-latest
services:
postgres:
Expand Down Expand Up @@ -97,8 +125,8 @@ jobs:
path: "**/TestResults/*.trx"

# 4. Code Quality
quality:
needs: test
quality-backend:
needs: test-backend
runs-on: ubuntu-latest
steps:
- name: Checkout code
Expand All @@ -122,9 +150,8 @@ jobs:
-Dsonar.qualitygate.wait=true

# 5. Build & Push Docker Image
docker:
needs: quality
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
docker-backend:
needs: build-backend
runs-on: ubuntu-latest
permissions:
packages: write
Expand All @@ -148,8 +175,93 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: ./Backend/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository_owner }}/taskflow:latest
ghcr.io/${{ github.repository_owner }}/taskflow:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/taskflow:${{ github.ref_name == 'main' && 'stable' || 'dev' }}

# ======================== FRONTEND JOBS ========================

setup-frontend:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: Frontend/package-lock.json

- name: Install dependencies
run: cd Frontend && npm ci --legacy-peer-deps

- name: Lint Frontend
run: cd Frontend && npm install && npx ng lint

# Décommente si tu ajoutes des tests Angular
# - name: Run tests
# run: cd Frontend && npm run test -- --watch=false --browsers=ChromeHeadless

build-frontend:
needs: setup-frontend
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: Frontend/package-lock.json

- name: Install & Build
run: |
cd Frontend
npm ci --legacy-peer-deps
npm run build -- --configuration production

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: Frontend/dist/

docker-frontend:
needs: build-frontend
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push frontend image
uses: docker/build-push-action@v6
with:
context: ./Frontend
file: ./Frontend/Dockerfile
push: true
tags: |
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:latest
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:${{ github.sha }}
ghcr.io/$$ {{ github.repository_owner }}/ $${{ github.event.repository.name }}:${{ github.ref_name == 'main' && 'stable' || 'dev' }}
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:latest
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:${{ github.sha }}
ghcr.io/${{ github.repository_owner }}/taskflow-frontend:${{ github.ref_name == 'main' && 'stable' || 'dev' }}
2 changes: 1 addition & 1 deletion Backend/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# FlowTasks - Backend API

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

## 🚀 Stack Technique

Expand Down
28 changes: 28 additions & 0 deletions Backend/dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Étape de build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app

COPY Backend/FlowTasks.sln ./
COPY Backend/src/FlowTasks.API/FlowTasks.API.csproj ./src/FlowTasks.API/
COPY Backend/src/FlowTasks.Application/FlowTasks.Application.csproj ./src/FlowTasks.Application/
COPY Backend/src/FlowTasks.Domain/FlowTasks.Domain.csproj ./src/FlowTasks.Domain/
COPY Backend/src/FlowTasks.Tests/FlowTasks.Tests.csproj ./src/FlowTasks.Tests/
COPY Backend/src/FlowTasks.Infrastructure/FlowTasks.Infrastructure.csproj ./src/FlowTasks.Infrastructure/

RUN dotnet restore

COPY Backend/src/FlowTasks.API/. ./src/FlowTasks.API/
COPY Backend/src/FlowTasks.Application/. ./src/FlowTasks.Application/
COPY Backend/src/FlowTasks.Domain/. ./src/FlowTasks.Domain/
COPY Backend/src/FlowTasks.Infrastructure/. ./src/FlowTasks.Infrastructure/
COPY Backend/src/FlowTasks.Tests/. ./src/FlowTasks.Tests/

# Build & Publish l'API
WORKDIR /app/src/FlowTasks.API
RUN dotnet publish -c Release -o /app/publish --no-restore

# Étape runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "FlowTasks.API.dll"]
1 change: 0 additions & 1 deletion Backend/src/FlowTasks.API/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,3 @@ public async Task<ActionResult> ChangePassword([FromBody] ChangePasswordRequest
}
}
}

1 change: 1 addition & 0 deletions Backend/src/FlowTasks.API/FlowTasks.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>edb59238-8d13-4f8f-be87-065b28b31de9</UserSecretsId>
</PropertyGroup>

<ItemGroup>
Expand Down
8 changes: 5 additions & 3 deletions Backend/src/FlowTasks.API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@
});
});

var dbConnectin = builder.Configuration["DbConnection"];
// Database configuration
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
options.UseNpgsql(dbConnectin));

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

// JWT Authentication
var jwtKey = builder.Configuration["Jwt:Key"] ?? throw new InvalidOperationException("JWT Key not configured");
var jwtKey = builder.Configuration["JwtKey"] ?? throw new InvalidOperationException("JWT Key not configured");
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "FlowTasks";
var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "FlowTasks";

Expand All @@ -115,12 +116,13 @@

builder.Services.AddAuthorization();

var UrlFrontend = builder.Configuration["UrlFrontend"];
// CORS configuration
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAngular", policy =>
{
policy.WithOrigins("http://localhost:4200")
policy.WithOrigins(UrlFrontend)

Check warning on line 125 in Backend/src/FlowTasks.API/Program.cs

View workflow job for this annotation

GitHub Actions / build-backend

Possible null reference argument for parameter 'origins' in 'CorsPolicyBuilder CorsPolicyBuilder.WithOrigins(params string[] origins)'.

Check warning on line 125 in Backend/src/FlowTasks.API/Program.cs

View workflow job for this annotation

GitHub Actions / test-backend

Possible null reference argument for parameter 'origins' in 'CorsPolicyBuilder CorsPolicyBuilder.WithOrigins(params string[] origins)'.
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
Expand Down
19 changes: 19 additions & 0 deletions Backend/src/FlowTasks.API/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": ""
},
"Jwt": {
"Key": "",
"Issuer": "FlowTasks",
"Audience": "FlowTasks"
}
}

14 changes: 14 additions & 0 deletions Frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM node:20-alpine AS build

WORKDIR /src
COPY package*.json ./
RUN npm ci --legacy-peer-deps
COPY . .
RUN npm run build -- --configuration production
FROM nginx:alpine

COPY --from=build /dist/taskflow /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
14 changes: 13 additions & 1 deletion Frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,23 @@
],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"cli": {
"analytics": "40863e82-8a07-4bb9-8a8c-a5f94f50a9f9"
"analytics": "40863e82-8a07-4bb9-8a8c-a5f94f50a9f9",
"schematicCollections": [
"angular-eslint"
]
}
}
44 changes: 44 additions & 0 deletions Frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @ts-check
const eslint = require("@eslint/js");
const { defineConfig } = require("eslint/config");
const tseslint = require("typescript-eslint");
const angular = require("angular-eslint");

module.exports = defineConfig([
{
files: ["**/*.ts"],
extends: [
eslint.configs.recommended,
tseslint.configs.recommended,
tseslint.configs.stylistic,
angular.configs.tsRecommended,
],
processor: angular.processInlineTemplates,
rules: {
"@angular-eslint/directive-selector": [
"error",
{
type: "attribute",
prefix: "app",
style: "camelCase",
},
],
"@angular-eslint/component-selector": [
"error",
{
type: "element",
prefix: "app",
style: "kebab-case",
},
],
},
},
{
files: ["**/*.html"],
extends: [
angular.configs.templateRecommended,
angular.configs.templateAccessibility,
],
rules: {},
}
]);
17 changes: 17 additions & 0 deletions Frontend/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
server {
listen 80;
server_name localhost;

root /usr/share/nginx/html;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}

# Optionnel : cache des assets
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot) {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Loading
Loading