1 Commits

Author SHA1 Message Date
Millian Lamiaux
c938c81df0 ci(admin-web): Docker build & push vers registry Gitea + reusable tests
Some checks failed
CI / Detect Changes (pull_request) Successful in 3s
CI / YouTube Worker (pull_request) Failing after 6s
CI / Admin Web CI (pull_request) Failing after 7s
CI / Deploy (pull_request) Has been skipped
PR → Build → WiFi/USB Deploy → LGTM / Build & Deploy to iPhone (WiFi/USB) (pull_request) Failing after 2m54s
PR → Build → WiFi/USB Deploy → LGTM / Wait for LGTM comment (pull_request) Has been skipped
- Nouveau Dockerfile multi-stage Next.js standalone (node:22-bookworm-slim)
- Configuration semantic-release (.releaserc.json, version.json, devDeps)
- docker-compose.portainer.yml pour déploiement Portainer
- Workflow docker-admin-web.yml : test → semantic-release → build push → deploy
- Extraction admin-web-tests.yml (reusable workflow) appelé par ci.yml et docker-admin-web.yml → 0 duplication
- next.config.ts : ajout output: 'standalone'
- .dockerignore : exclusions node_modules, .next, etc.
2026-07-12 07:18:01 +02:00
10 changed files with 278 additions and 30 deletions

41
.github/workflows/admin-web-tests.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Admin Web Tests
on:
workflow_call:
inputs:
node-version:
type: string
default: '22'
jobs:
test:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: admin-web
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
cache-dependency-path: admin-web/package-lock.json
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
- name: Run unit tests
run: npx vitest run
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test

View File

@@ -38,34 +38,9 @@ jobs:
name: Admin Web CI
needs: changes
if: needs.changes.outputs.admin-web == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: admin-web
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: admin-web/package-lock.json
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
- name: Run unit tests
run: npx vitest run
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test
uses: ./.github/workflows/admin-web-tests.yml
with:
node-version: '22'
# ── YouTube Worker: Node.js microservice ──
youtube-worker-check:

154
.github/workflows/docker-admin-web.yml vendored Normal file
View File

@@ -0,0 +1,154 @@
name: Docker Admin Web
on:
push:
branches: [main]
paths:
- 'admin-web/**'
- '.github/workflows/docker-admin-web.yml'
workflow_dispatch:
concurrency:
group: docker-admin-web-${{ github.ref }}
cancel-in-progress: false
jobs:
admin-web-test:
uses: ./.github/workflows/admin-web-tests.yml
semantic-release:
name: Semantic Release
needs: admin-web-test
runs-on: ubuntu-latest
timeout-minutes: 5
defaults:
run:
working-directory: admin-web
outputs:
released: ${{ steps.check-release.outputs.released }}
version: ${{ steps.check-release.outputs.version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
cache-dependency-path: admin-web/package-lock.json
- name: Install dependencies
run: npm ci
- name: Record version before release
id: before
run: echo "version=$(node -p "require('./version.json').version")" >> $GITHUB_OUTPUT
- name: Run semantic-release
env:
GITEA_TOKEN: ${{ secrets.CI_GITEA_TOKEN }}
GITEA_URL: https://gitea.1000co.fr
run: npx semantic-release
- name: Check if released
id: check-release
run: |
VERSION=$(node -p "require('./version.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
if [ "${{ steps.before.outputs.version }}" != "$VERSION" ]; then
echo "released=true" >> $GITHUB_OUTPUT
else
echo "released=false" >> $GITHUB_OUTPUT
fi
build-and-push:
name: Build & Push Docker Image
needs: semantic-release
if: needs.semantic-release.outputs.released == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install Docker CLI
run: apt-get update -qq && apt-get install -y -qq docker.io
- name: Login to Gitea registry
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | \
docker login gitea.1000co.fr \
-u "${{ secrets.DOCKER_USERNAME }}" \
--password-stdin
- name: Build and push
run: |
U="${{ secrets.DOCKER_USERNAME }}"
V="${{ needs.semantic-release.outputs.version }}"
docker build \
--file ./admin-web/Dockerfile \
--tag "gitea.1000co.fr/$U/tabatago-admin-web:$V" \
--tag "gitea.1000co.fr/$U/tabatago-admin-web:latest" \
./admin-web
docker push "gitea.1000co.fr/$U/tabatago-admin-web:$V"
docker push "gitea.1000co.fr/$U/tabatago-admin-web:latest"
deploy:
name: Deploy to Portainer
needs: [semantic-release, build-and-push]
if: needs.semantic-release.outputs.released == 'true' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- name: Install deps
run: apt-get update -qq && apt-get install -y -qq gettext-base jq curl
- name: Deploy stack to Portainer
env:
PORTAINER_TOKEN: ${{ secrets.PORTAINER_ACCESS_TOKEN }}
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
VERSION: ${{ needs.semantic-release.outputs.version }}
run: |
set -e
# Substitute env vars in compose file
export DOCKER_USERNAME VERSION
COMPOSE=$(envsubst '$DOCKER_USERNAME $VERSION' < admin-web/docker-compose.portainer.yml)
# Authenticate and discover Docker endpoint (first endpoint = local Docker)
ENDPOINT_ID=$(curl -sSf -H "X-API-Key: $PORTAINER_TOKEN" \
"https://portainer.1000co.fr/api/endpoints" | jq -r '.[0].Id')
echo "Endpoint ID: $ENDPOINT_ID"
# Check if "tabatago-admin-web" stack already exists
STACK_ID=$(curl -sSf -H "X-API-Key: $PORTAINER_TOKEN" \
"https://portainer.1000co.fr/api/stacks" | jq -r '.[] | select(.Name == "tabatago-admin-web") | .Id')
if [ -n "$STACK_ID" ] && [ "$STACK_ID" != "null" ]; then
echo "Stack tabatago-admin-web exists (ID: $STACK_ID), updating compose content..."
HTTP_RESPONSE=$(curl -s -w '\n%{http_code}' \
-X PUT \
-H "X-API-Key: $PORTAINER_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg content "$COMPOSE" '{stackFileContent: $content, prune: false, pullImage: true}')" \
"https://portainer.1000co.fr/api/stacks/$STACK_ID?endpointId=$ENDPOINT_ID" || true)
HTTP_BODY=$(echo "$HTTP_RESPONSE" | sed '$d')
HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
if [ -z "$HTTP_CODE" ] || [ "$HTTP_CODE" -ge 400 ]; then
echo "Portainer API error (HTTP ${HTTP_CODE:-connection failed}):"
echo "$HTTP_BODY"
exit 1
fi
echo "Stack updated. Portainer will redeploy with new images."
else
echo "⚠️ Stack tabatago-admin-web not found in Portainer."
echo "Create it manually in Portainer first: Stacks → Add stack → paste compose content from admin-web/docker-compose.portainer.yml"
exit 1
fi

12
admin-web/.dockerignore Normal file
View File

@@ -0,0 +1,12 @@
node_modules
.next
.git
.gitignore
README.md
.npm
.pnp
.DS_Store
*.md
coverage
playwright-report
test-results

14
admin-web/.releaserc.json Normal file
View File

@@ -0,0 +1,14 @@
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/exec", {
"prepareCmd": "echo '{\"version\": \"${nextRelease.version}\"}' > version.json"
}],
["@semantic-release/git", {
"assets": ["version.json"],
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
}]
]
}

29
admin-web/Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:22-bookworm-slim AS production
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/public ./public
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
RUN groupadd -g 1001 appgroup && useradd -u 1001 -g appgroup -s /bin/sh -M nodeuser
USER nodeuser
EXPOSE 3000
ENTRYPOINT ["node", "server.js"]

View File

@@ -0,0 +1,16 @@
services:
admin-web:
image: gitea.1000co.fr/${DOCKER_USERNAME}/tabatago-admin-web:${VERSION:-latest}
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}
- NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY}
networks:
- supabase_supabase-network
networks:
supabase_supabase-network:
external: true

View File

@@ -1,7 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: 'standalone',
};
export default nextConfig;

View File

@@ -13,7 +13,8 @@
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:headed": "playwright test --headed",
"test:all": "npm run test && npm run test:e2e"
"test:all": "npm run test && npm run test:e2e",
"semantic-release": "semantic-release"
},
"dependencies": {
"@google/genai": "^1.47.0",
@@ -30,6 +31,10 @@
},
"devDependencies": {
"@playwright/test": "^1.58.2",
"@semantic-release/commit-analyzer": "^13.0.0",
"@semantic-release/exec": "^6.0.0",
"@semantic-release/git": "^10.0.0",
"@semantic-release/release-notes-generator": "^14.0.0",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
@@ -42,6 +47,7 @@
"eslint": "^9",
"eslint-config-next": "16.1.6",
"jsdom": "^28.1.0",
"semantic-release": "^24.0.0",
"shadcn": "^3.8.5",
"tailwindcss": "^4",
"tw-animate-css": "^1.4.0",

1
admin-web/version.json Normal file
View File

@@ -0,0 +1 @@
{"version": "0.1.0"}