feat: externalize e2e testings [skip-release] (#312)

* feat: externalize e2e tests.

* refactor: improve e2e workflow.

* chore: upgrade workflow action version.

* fix: createEnv by adding emptyStringAsUndefined (#317)

* chore: release 1.18.2

---------

Co-authored-by: killian-larcher <killian.larcher@optimusai.fr>
Co-authored-by: Charles GTE <charlesgte31@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Killian Larcher
2026-06-10 18:08:58 +02:00
committed by GitHub
co-authored by killian-larcher Charles GTE github-actions[bot] <github-actions[bot]@users.noreply.github.com>
parent ebdef2d302
commit 32da8b5fd0
5 changed files with 72 additions and 100 deletions
+66 -97
View File
@@ -4,114 +4,83 @@ on:
pull_request: pull_request:
branches: [main] branches: [main]
permissions:
contents: read
packages: write
jobs: jobs:
build-and-test: build-image:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name == github.repository if: github.event.pull_request.head.repo.full_name == github.repository
outputs:
image: ${{ steps.image.outputs.ref }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Setup pnpm - name: Compute image reference
uses: pnpm/action-setup@v4 id: image
run: echo "ref=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/portabase:${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
- name: Setup Node.js - name: Set up Docker Buildx
uses: actions/setup-node@v4 uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with: with:
node-version: 22 registry: ghcr.io
cache: 'pnpm' username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies - name: Build and push Docker image
run: pnpm install --frozen-lockfile
- name: Build Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./docker/dockerfile/Dockerfile file: ./docker/dockerfile/Dockerfile
push: false push: true
load: true tags: ${{ steps.image.outputs.ref }}
tags: portabase/portabase:test target: prod
- name: Run app container run-e2e:
run: | if: github.event.pull_request.head.repo.full_name == github.repository
docker run -d --name myapp \ needs: build-image
-p 8887:80 \ uses: portabase/e2e-tests/.github/workflows/run-e2e.yml@main
-e NODE_ENV=production \ with:
-e PROJECT_URL=http://localhost:8887 \ server_image: ${{ needs.build-image.outputs.image }}
-e PROJECT_SECRET=80af5e4c875dfded3a6ba511c6ed8b0160cad723f848ee0851403ed6141f6ba1 \ secrets:
portabase/portabase:test E2E_NOTIFICATION_SMTP_HOST: ${{ secrets.E2E_NOTIFICATION_SMTP_HOST }}
E2E_NOTIFICATION_SMTP_PORT: ${{ secrets.E2E_NOTIFICATION_SMTP_PORT }}
- name: Wait for app port to be listening E2E_NOTIFICATION_SMTP_USER: ${{ secrets.E2E_NOTIFICATION_SMTP_USER }}
run: | E2E_NOTIFICATION_SMTP_PASSWORD: ${{ secrets.E2E_NOTIFICATION_SMTP_PASSWORD }}
for i in {1..40}; do E2E_NOTIFICATION_SMTP_FROM: ${{ secrets.E2E_NOTIFICATION_SMTP_FROM }}
E2E_NOTIFICATION_SMTP_TO: ${{ secrets.E2E_NOTIFICATION_SMTP_TO }}
if curl -fsS http://localhost:8887/ >/dev/null; then E2E_NOTIFICATION_SLACK_WEBHOOK: ${{ secrets.E2E_NOTIFICATION_SLACK_WEBHOOK }}
echo "App is responding on port 8887" E2E_NOTIFICATION_DISCORD_WEBHOOK: ${{ secrets.E2E_NOTIFICATION_DISCORD_WEBHOOK }}
exit 0 E2E_NOTIFICATION_TELEGRAM_BOT_TOKEN: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_BOT_TOKEN }}
fi E2E_NOTIFICATION_TELEGRAM_CHAT_ID: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_CHAT_ID }}
echo "Waiting for app readiness... ($i/40)" E2E_NOTIFICATION_TELEGRAM_TOPIC_ID: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_TOPIC_ID }}
sleep 2 E2E_NOTIFICATION_GOTIFY_SERVER_URL: ${{ secrets.E2E_NOTIFICATION_GOTIFY_SERVER_URL }}
done E2E_NOTIFICATION_GOTIFY_APP_TOKEN: ${{ secrets.E2E_NOTIFICATION_GOTIFY_APP_TOKEN }}
echo "Timeout: app never became ready" E2E_NOTIFICATION_NTFY_TOPIC: ${{ secrets.E2E_NOTIFICATION_NTFY_TOPIC }}
docker logs myapp E2E_NOTIFICATION_NTFY_SERVER_URL: ${{ secrets.E2E_NOTIFICATION_NTFY_SERVER_URL }}
exit 1 E2E_NOTIFICATION_NTFY_TOKEN: ${{ secrets.E2E_NOTIFICATION_NTFY_TOKEN }}
E2E_NOTIFICATION_NTFY_USERNAME: ${{ secrets.E2E_NOTIFICATION_NTFY_USERNAME }}
- name: Install Playwright browsers E2E_NOTIFICATION_NTFY_PASSWORD: ${{ secrets.E2E_NOTIFICATION_NTFY_PASSWORD }}
run: pnpm exec playwright install --with-deps chromium E2E_NOTIFICATION_WEBHOOK_URL: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_URL }}
E2E_NOTIFICATION_WEBHOOK_SECRET_HEADER: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_SECRET_HEADER }}
- name: Run Playwright tests E2E_NOTIFICATION_WEBHOOK_SECRET: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_SECRET }}
run: pnpm exec playwright test E2E_STORAGE_AWS_S3_ENDPOINT_URL: ${{ secrets.E2E_STORAGE_AWS_S3_ENDPOINT_URL }}
env: E2E_STORAGE_AWS_S3_REGION: ${{ secrets.E2E_STORAGE_AWS_S3_REGION }}
PROJECT_URL: http://localhost:8887 E2E_STORAGE_AWS_S3_ACCESS_KEY: ${{ secrets.E2E_STORAGE_AWS_S3_ACCESS_KEY }}
# E2E Notification E2E_STORAGE_AWS_S3_SECRET_KEY: ${{ secrets.E2E_STORAGE_AWS_S3_SECRET_KEY }}
E2E_NOTIFICATION_SMTP_HOST: ${{ secrets.E2E_NOTIFICATION_SMTP_HOST }} E2E_STORAGE_AWS_S3_BUCKET_NAME: ${{ secrets.E2E_STORAGE_AWS_S3_BUCKET_NAME }}
E2E_NOTIFICATION_SMTP_PORT: ${{ secrets.E2E_NOTIFICATION_SMTP_PORT }} E2E_STORAGE_AWS_S3_PORT: ${{ secrets.E2E_STORAGE_AWS_S3_PORT }}
E2E_NOTIFICATION_SMTP_USER: ${{ secrets.E2E_NOTIFICATION_SMTP_USER }} E2E_STORAGE_R2_ENDPOINT_URL: ${{ secrets.E2E_STORAGE_R2_ENDPOINT_URL }}
E2E_NOTIFICATION_SMTP_PASSWORD: ${{ secrets.E2E_NOTIFICATION_SMTP_PASSWORD }} E2E_STORAGE_R2_REGION: ${{ secrets.E2E_STORAGE_R2_REGION }}
E2E_NOTIFICATION_SMTP_FROM: ${{ secrets.E2E_NOTIFICATION_SMTP_FROM }} E2E_STORAGE_R2_ACCESS_KEY: ${{ secrets.E2E_STORAGE_R2_ACCESS_KEY }}
E2E_NOTIFICATION_SMTP_TO: ${{ secrets.E2E_NOTIFICATION_SMTP_TO }} E2E_STORAGE_R2_SECRET_KEY: ${{ secrets.E2E_STORAGE_R2_SECRET_KEY }}
E2E_NOTIFICATION_SLACK_WEBHOOK: ${{ secrets.E2E_NOTIFICATION_SLACK_WEBHOOK }} E2E_STORAGE_R2_BUCKET_NAME: ${{ secrets.E2E_STORAGE_R2_BUCKET_NAME }}
E2E_NOTIFICATION_DISCORD_WEBHOOK: ${{ secrets.E2E_NOTIFICATION_DISCORD_WEBHOOK }} E2E_STORAGE_R2_PORT: ${{ secrets.E2E_STORAGE_R2_PORT }}
E2E_NOTIFICATION_TELEGRAM_BOT_TOKEN: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_BOT_TOKEN }} E2E_STORAGE_GOOGLE_DRIVE_CLIENT_ID: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_CLIENT_ID }}
E2E_NOTIFICATION_TELEGRAM_CHAT_ID: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_CHAT_ID }} E2E_STORAGE_GOOGLE_DRIVE_CLIENT_SECRET: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_CLIENT_SECRET }}
E2E_NOTIFICATION_TELEGRAM_TOPIC_ID: ${{ secrets.E2E_NOTIFICATION_TELEGRAM_TOPIC_ID }} E2E_STORAGE_GOOGLE_DRIVE_FOLDER_ID: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_FOLDER_ID }}
E2E_NOTIFICATION_GOTIFY_SERVER_URL: ${{ secrets.E2E_NOTIFICATION_GOTIFY_SERVER_URL }}
E2E_NOTIFICATION_GOTIFY_APP_TOKEN: ${{ secrets.E2E_NOTIFICATION_GOTIFY_APP_TOKEN }}
E2E_NOTIFICATION_NTFY_TOPIC: ${{ secrets.E2E_NOTIFICATION_NTFY_TOPIC }}
E2E_NOTIFICATION_NTFY_SERVER_URL: ${{ secrets.E2E_NOTIFICATION_NTFY_SERVER_URL }}
E2E_NOTIFICATION_NTFY_TOKEN: ${{ secrets.E2E_NOTIFICATION_NTFY_TOKEN }}
E2E_NOTIFICATION_NTFY_USERNAME: ${{ secrets.E2E_NOTIFICATION_NTFY_USERNAME }}
E2E_NOTIFICATION_NTFY_PASSWORD: ${{ secrets.E2E_NOTIFICATION_NTFY_PASSWORD }}
E2E_NOTIFICATION_WEBHOOK_URL: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_URL }}
E2E_NOTIFICATION_WEBHOOK_SECRET_HEADER: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_SECRET_HEADER }}
E2E_NOTIFICATION_WEBHOOK_SECRET: ${{ secrets.E2E_NOTIFICATION_WEBHOOK_SECRET }}
# E2E Storage
E2E_STORAGE_AWS_S3_ENDPOINT_URL: ${{ secrets.E2E_STORAGE_AWS_S3_ENDPOINT_URL }}
E2E_STORAGE_AWS_S3_REGION: ${{ secrets.E2E_STORAGE_AWS_S3_REGION }}
E2E_STORAGE_AWS_S3_ACCESS_KEY: ${{ secrets.E2E_STORAGE_AWS_S3_ACCESS_KEY }}
E2E_STORAGE_AWS_S3_SECRET_KEY: ${{ secrets.E2E_STORAGE_AWS_S3_SECRET_KEY }}
E2E_STORAGE_AWS_S3_BUCKET_NAME: ${{ secrets.E2E_STORAGE_AWS_S3_BUCKET_NAME }}
E2E_STORAGE_AWS_S3_PORT: ${{ secrets.E2E_STORAGE_AWS_S3_PORT }}
E2E_STORAGE_R2_ENDPOINT_URL: ${{ secrets.E2E_STORAGE_R2_ENDPOINT_URL }}
E2E_STORAGE_R2_REGION: ${{ secrets.E2E_STORAGE_R2_REGION }}
E2E_STORAGE_R2_ACCESS_KEY: ${{ secrets.E2E_STORAGE_R2_ACCESS_KEY }}
E2E_STORAGE_R2_SECRET_KEY: ${{ secrets.E2E_STORAGE_R2_SECRET_KEY }}
E2E_STORAGE_R2_BUCKET_NAME: ${{ secrets.E2E_STORAGE_R2_BUCKET_NAME }}
E2E_STORAGE_R2_PORT: ${{ secrets.E2E_STORAGE_R2_PORT }}
E2E_STORAGE_GOOGLE_DRIVE_CLIENT_ID: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_CLIENT_ID }}
E2E_STORAGE_GOOGLE_DRIVE_CLIENT_SECRET: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_CLIENT_SECRET }}
E2E_STORAGE_GOOGLE_DRIVE_FOLDER_ID: ${{ secrets.E2E_STORAGE_GOOGLE_DRIVE_FOLDER_ID }}
- name: Upload report if failed
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7
- name: Cleanup
if: always()
run: docker stop myapp && docker rm myapp || true
+1 -1
View File
@@ -33,5 +33,5 @@ keywords:
- web-ui - web-ui
- agent - agent
license: Apache-2.0 license: Apache-2.0
version: 1.18.1 version: 1.18.2
date-released: '2026-03-02' date-released: '2026-03-02'
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "portabase", "name": "portabase",
"version": "1.18.1", "version": "1.18.2",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack -p 8887", "dev": "next dev --turbopack -p 8887",
+2
View File
@@ -6,6 +6,7 @@ import packageJson from "../package.json" with {type: "json"};
const {version} = packageJson; const {version} = packageJson;
export const env = createEnv({ export const env = createEnv({
emptyStringAsUndefined: true,
server: { server: {
NEXT_PUBLIC_PROJECT_VERSION: z.string().optional(), NEXT_PUBLIC_PROJECT_VERSION: z.string().optional(),
@@ -199,3 +200,4 @@ export const env = createEnv({
}, },
}); });
+2 -1
View File
@@ -33,7 +33,8 @@
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts",
".next/dev/types/**/*.ts" ".next/dev/types/**/*.ts",
".next/dev/dev/types/**/*.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules"