Skip to content
Closed
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
141 changes: 141 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: "DEV - CI/CD"

on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- develop
- main
push:
branches:
- main # pour que PROD puisse se déclencher automatiquement si besoin

jobs:
# 1️⃣ Build & tests
build-and-test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Generate Prisma Client
run: pnpm prisma generate

- name: Skip tests (no test script defined)
run: echo "⚠️ Aucun test défini, étape ignorée."

- name: Build app
run: pnpm build

# 2️⃣ Build & push Docker image
docker-build-push:
runs-on: ubuntu-latest
needs: build-and-test
permissions:
contents: read
packages: write

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=sha,format=long

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
NEXT_PUBLIC_ENV=${{ github.base_ref }}

# 3️⃣ Deploy DEV (auto sur PR)
deploy-dev:
runs-on: self-hosted
needs: docker-build-push

steps:
- name: Clean workspace
run: rm -rf *

- name: Checkout code
uses: actions/checkout@v4

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

- name: Pull new image + restart DEV stack
run: |
echo "🔧 Deploying on DEV..."
cd /home/baptiste/Dev/WPT/dev
docker-compose pull
docker rm -f wpt-dev_website || true
docker-compose up -d --remove-orphans
echo "🚀 Deployed in DEV!"

# 4️⃣ Deploy PROD (skip par défaut, relançable sur main)
deploy-prod:
runs-on: self-hosted
needs: docker-build-push
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
steps:
- name: Clean workspace
run: rm -rf *

- name: Checkout code
uses: actions/checkout@v4

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

- name: Pull new image + restart PROD stack
run: |
echo "🔧 Deploying on PROD..."
cd /home/baptiste/Dev/WPT/prod
docker-compose pull
docker rm -f wpt_website || true
docker-compose up -d --remove-orphans
echo "🚀 Deployed in PROD!"
Comment on lines +117 to +141

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 2 months ago

To fix the problem, add a permissions block to the deploy-prod job, restricting the GITHUB_TOKEN to the least required privileges (likely contents: read). This ensures that the deploy-prod job cannot unexpectedly write to source code or other resources via the token. Place the following block directly under the runs-on and above needs (or immediately after needs, if preferred for consistency):

permissions:
  contents: read

This matches the minimal usage for jobs that only need to fetch code or authenticate for Docker pulls. Review other self-hosted jobs (deploy-dev), and add a similar block if appropriate, but CodeQL only explicitly flagged deploy-prod, so let's just do that per instructions. No imports or other definitions are necessary—just a YAML block.


Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -115,6 +115,8 @@
   # 4️⃣ Deploy PROD (skip par défaut, relançable sur main)
   deploy-prod:
     runs-on: self-hosted
+    permissions:
+      contents: read
     needs: docker-build-push
     if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
     steps:
EOF
@@ -115,6 +115,8 @@
# 4️⃣ Deploy PROD (skip par défaut, relançable sur main)
deploy-prod:
runs-on: self-hosted
permissions:
contents: read
needs: docker-build-push
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
steps:
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
80 changes: 65 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,74 @@
# Étape 1 : build
FROM node:22-alpine AS builder
FROM node:22-alpine AS base

# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat openssl
WORKDIR /app
RUN npm install -g pnpm

# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate

# Install dependencies based on pnpm
COPY package.json pnpm-lock.yaml ./
COPY .env .env
RUN pnpm install
RUN pnpm install --frozen-lockfile

FROM base AS dev
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN apk add --no-cache openssl
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Generates prisma files
RUN pnpm prisma generate

# Enables Hot Reloading Check https://github.com/vercel/next.js/issues/36774 for more information
ENV CHOKIDAR_USEPOLLING=true
ENV WATCHPACK_POLLING=true

# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
RUN apk add --no-cache openssl
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED 1

# Generates prisma files for production build
RUN pnpm prisma generate
RUN pnpm build

# Étape 2 : image finale propre
FROM node:22-alpine
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
RUN npm install -g pnpm
COPY --from=builder /app/package.json /app/pnpm-lock.yaml ./
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.ts ./
COPY --from=builder /app/tsconfig.json ./

RUN pnpm install --prod
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN apk add --no-cache openssl
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Install pnpm in runner
RUN corepack enable && corepack prepare pnpm@latest --activate

# Copier les fichiers nécessaires
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./
COPY --from=builder --chown=nextjs:nodejs /app/pnpm-lock.yaml ./
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/next.config.ts ./

USER nextjs

EXPOSE 3000
CMD ["pnpm", "start"]
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

# Prisma generate + db push + start
CMD sh -c "pnpm prisma generate && pnpm prisma db push --accept-data-loss && pnpm start"
22 changes: 10 additions & 12 deletions app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@ import type React from 'react'

import { useState, useEffect, useRef } from 'react'
import { DocumentationSection } from '@/components/pages/(auth)/landing/DocumentationSection'
import { TestimonialsSection } from '@/components/pages/(auth)/landing/TestimonialSection'
import { PricingSection } from '@/components/pages/(auth)/landing/PrincingSection'
import { FAQSection } from '@/components/pages/(auth)/landing/FaqSection'
import { CTASection } from '@/components/pages/(auth)/landing/CtaSection'
import { FooterSection } from '@/components/pages/(auth)/landing/FooterSection'
import NumbersThatSpeak from '@/components/pages/(auth)/landing/NumbersThatSpeak'
import EffortlessIntegration from '@/components/pages/(auth)/landing/EffortlessIntegration'
import YourWorkInSync from '@/components/pages/(auth)/landing/YourWorkInSync'
import SmartSimpleBrilliant from '@/components/pages/(auth)/landing/SmartSimpleBrilliant'
import { SITENAME } from '@/constants/globals'
import { LANDING_PAGE_LINKS, ROUTES } from '@/constants/routes'
import { Link } from '@/lib/i18n/navigation'
import { useTranslations } from 'next-intl'
import Image from 'next/image'

function Badge({ icon, text }: { icon: React.ReactNode; text: string }) {
return (
Expand Down Expand Up @@ -143,15 +139,18 @@ export default function LandingPage() {
<div className="w-full max-w-[497px] lg:w-[497px] flex flex-col justify-center items-center gap-6 sm:gap-8 md:gap-10 lg:gap-12 relative z-10 mt-6 sm:mt-8 md:mt-10 lg:mt-12">
<div className="backdrop-blur-[8.25px] flex justify-start items-center gap-4">
<div className="h-10 sm:h-11 md:h-12 px-6 sm:px-8 md:px-10 lg:px-12 py-2 sm:py-[6px] relative bg-primary hover:bg-primary/90 transition-colors shadow-[0px_0px_0px_2.5px_rgba(255,255,255,0.1)_inset] overflow-hidden rounded-full flex justify-center items-center cursor-pointer">
<Link href={ROUTES.SIGN_IN} className="flex flex-col justify-center text-primary-foreground text-sm sm:text-base md:text-[15px] font-semibold leading-5 font-sans">
<Link
href={ROUTES.SIGN_IN}
className="flex flex-col justify-center text-primary-foreground text-sm sm:text-base md:text-[15px] font-semibold leading-5 font-sans"
>
{t('Buttons.CTA')}
</Link>
</div>
</div>
</div>

<div className="absolute top-[232px] sm:top-[248px] md:top-[264px] lg:top-[320px] left-1/2 transform -translate-x-1/2 z-0 pointer-events-none">
<img
<Image
src="/mask-group-pattern.svg"
alt=""
className="w-[936px] sm:w-[1404px] md:w-[2106px] lg:w-[2808px] h-auto opacity-30 sm:opacity-40 md:opacity-50 mix-blend-multiply"
Expand Down Expand Up @@ -351,7 +350,6 @@ export default function LandingPage() {
<div className="self-stretch px-4 sm:px-6 md:px-24 py-8 sm:py-12 md:py-16 border-b border-border flex justify-center items-center gap-6">
<div className="w-full max-w-[586px] px-4 sm:px-6 py-4 sm:py-5 overflow-hidden rounded-lg flex flex-col justify-start items-center gap-3 sm:gap-4">
<Badge

icon={
<svg
width="12"
Expand All @@ -368,14 +366,14 @@ export default function LandingPage() {
/>
</svg>
}
text={t("Trusted.Badge")}
text={t('Trusted.Badge')}
/>

<div className="w-full max-w-[472.55px] text-center flex justify-center flex-col text-foreground text-xl sm:text-2xl md:text-3xl lg:text-5xl font-bold leading-tight md:leading-[60px] font-sans tracking-tight">
{t("Trusted.Title")}
{t('Trusted.Title')}
</div>
<div className="self-stretch text-center text-muted-foreground text-sm sm:text-base font-normal leading-6 sm:leading-7 font-sans">
{t("Trusted.Description")}
{t('Trusted.Description')}
</div>
</div>
</div>
Expand Down Expand Up @@ -421,7 +419,7 @@ export default function LandingPage() {
`}
>
<div className="w-6 h-6 xs:w-7 xs:h-7 sm:w-8 sm:h-8 md:w-9 md:h-9 lg:w-10 lg:h-10 relative shadow-[0px_-4px_8px_rgba(255,255,255,0.64)_inset] overflow-hidden rounded-full">
<img
<Image
src="/horizon-icon.svg"
alt="Horizon"
className="w-full h-full object-contain"
Expand Down
16 changes: 8 additions & 8 deletions app/api/[uuid]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,50 +51,50 @@ async function handleRequest(req: NextRequest, uuid: string) {

export async function GET(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function POST(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function PUT(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function PATCH(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function DELETE(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function OPTIONS(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
export async function HEAD(
req: NextRequest,
{ params }: { params: { uuid: string } },
{ params }: { params: Promise<{ uuid: string }> },
) {
const { uuid } = await params
return handleRequest(req, uuid)
}
}
Loading
Loading