From 46dbd721e47f06929ecff0bd4e14c654112380ab Mon Sep 17 00:00:00 2001 From: Galindo97 Date: Wed, 20 May 2026 15:43:58 -0500 Subject: [PATCH 1/2] refactor(jenkins): mover E2E antes del deploy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E2E corre contra la versión actualmente desplegada (A76_URL). Si los tests fallan, el deploy no ocurre — nada roto llega al servidor. Orden final: Tests (paralelo) → Security Scan → Generate Version → Docker Build → E2E → Deploy Dev → Smoke Test Co-Authored-By: Claude Sonnet 4.6 --- Jenkinsfile | 122 ++++++++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 5ceca915..10f0ad82 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,5 @@ // Pipeline CI/CD — Aduanasoft Anexo76 -// Ejecuta: tests → security scan → docker build/push → deploy dev → E2E → smoke test +// Ejecuta: tests → security scan → docker build/push → E2E → deploy dev → smoke test // // Credenciales requeridas en Jenkins: // - harbor-credentials : username/password para dev.aduanasoft.com @@ -292,6 +292,66 @@ pipeline { } } + // ── E2E (Playwright) — contra la versión actualmente desplegada ───────── + // Corre ANTES del deploy con la imagen ya en Harbor pero aún no desplegada. + // Playwright apunta a A76_URL (versión viva anterior) — dominio de confianza + // para Workspace. Si E2E falla, el deploy no ocurre. + stage('E2E (Playwright)') { + when { branch 'development' } + steps { + withCredentials([ + string(credentialsId: 'a76-public-url-dev', variable: 'A76_URL'), + usernamePassword( + credentialsId: 'a76-e2e-credentials', + usernameVariable: 'E2E_USER', + passwordVariable: 'E2E_PASS' + ) + ]) { + sh ''' + set -euo pipefail + C="a76-test-e2e-${BUILD_NUMBER}" + cleanup() { docker rm -f "$C" >/dev/null 2>&1 || true; } + trap cleanup EXIT + + docker rm -f "$C" >/dev/null 2>&1 || true + docker run -d --name "$C" "$PLAYWRIGHT_IMAGE" sleep infinity + docker exec "$C" mkdir -p /workspace + docker cp "$WORKSPACE/." "$C:/workspace" + + docker exec \ + -e CI=true \ + -e "JENKINS_URL=${JENKINS_URL}" \ + -e "PLAYWRIGHT_TEST_BASE_URL=${A76_URL}" \ + -e "E2E_TEST_USER=${E2E_USER}" \ + -e "E2E_TEST_PASSWORD=${E2E_PASS}" \ + -w /workspace/frontend \ + "$C" bash -lc ' + set -euxo pipefail + npm install -g pnpm@9 --quiet + pnpm install --frozen-lockfile + pnpm run i18n:compile + + E2E_EXIT=0 + PLAYWRIGHT_JUNIT_OUTPUT_NAME=playwright-results.xml \ + pnpm exec playwright test --reporter=junit,html || E2E_EXIT=$? + + exit "$E2E_EXIT" + ' + docker cp "$C:/workspace/frontend/playwright-results.xml" \ + "$WORKSPACE/frontend/playwright-results.xml" 2>/dev/null || true + docker cp "$C:/workspace/frontend/playwright-report" \ + "$WORKSPACE/frontend/playwright-report" 2>/dev/null || true + ''' + } + } + post { + always { + junit allowEmptyResults: true, testResults: 'frontend/playwright-results.xml' + archiveArtifacts artifacts: 'frontend/playwright-report/**', allowEmptyArchive: true + } + } + } + // ── Deploy Dev ──────────────────────────────────────────────────────── stage('Deploy — Dev') { when { branch 'development' } @@ -342,66 +402,6 @@ pipeline { } } - // ── E2E (Playwright) — contra la URL desplegada ─────────────────────── - // Corre después del deploy para que Workspace confíe en el dominio de retorno. - // No levanta dev server — Playwright apunta directamente a A76_URL. - stage('E2E (Playwright)') { - when { branch 'development' } - steps { - withCredentials([ - string(credentialsId: 'a76-public-url-dev', variable: 'A76_URL'), - usernamePassword( - credentialsId: 'a76-e2e-credentials', - usernameVariable: 'E2E_USER', - passwordVariable: 'E2E_PASS' - ) - ]) { - sh ''' - set -euo pipefail - C="a76-test-e2e-${BUILD_NUMBER}" - cleanup() { docker rm -f "$C" >/dev/null 2>&1 || true; } - trap cleanup EXIT - - docker rm -f "$C" >/dev/null 2>&1 || true - docker run -d --name "$C" "$PLAYWRIGHT_IMAGE" sleep infinity - docker exec "$C" mkdir -p /workspace - docker cp "$WORKSPACE/." "$C:/workspace" - - docker exec \ - -e CI=true \ - -e "JENKINS_URL=${JENKINS_URL}" \ - -e "PLAYWRIGHT_TEST_BASE_URL=${A76_URL}" \ - -e "E2E_TEST_USER=${E2E_USER}" \ - -e "E2E_TEST_PASSWORD=${E2E_PASS}" \ - -w /workspace/frontend \ - "$C" bash -lc ' - set -euxo pipefail - npm install -g pnpm@9 --quiet - pnpm install --frozen-lockfile - pnpm run i18n:compile - - # Ejecutar E2E contra la URL desplegada - E2E_EXIT=0 - PLAYWRIGHT_JUNIT_OUTPUT_NAME=playwright-results.xml \ - pnpm exec playwright test --reporter=junit,html || E2E_EXIT=$? - - exit "$E2E_EXIT" - ' - docker cp "$C:/workspace/frontend/playwright-results.xml" \ - "$WORKSPACE/frontend/playwright-results.xml" 2>/dev/null || true - docker cp "$C:/workspace/frontend/playwright-report" \ - "$WORKSPACE/frontend/playwright-report" 2>/dev/null || true - ''' - } - } - post { - always { - junit allowEmptyResults: true, testResults: 'frontend/playwright-results.xml' - archiveArtifacts artifacts: 'frontend/playwright-report/**', allowEmptyArchive: true - } - } - } - // ── Smoke Test ──────────────────────────────────────────────────────── stage('Smoke Test') { when { branch 'development' } From 557f904af070d755bfccfa98436a6a8763581740 Mon Sep 17 00:00:00 2001 From: Galindo97 Date: Wed, 20 May 2026 15:49:28 -0500 Subject: [PATCH 2/2] fix(auth): cambiar client_id de Keycloak de anexo76-frontend a hub-frontend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anexo76 no es independiente del Hub/Workspace — comparten el mismo cliente de Keycloak (hub-frontend). anexo76-frontend no existe en KC. - Dockerfile.prod: agrega ARG/ENV VITE_KEYCLOAK_CLIENT_ID=hub-frontend - workspace-auth.ts: actualiza fallback de 'anexo76-frontend' a 'hub-frontend' - .env.example: actualiza a hub-frontend - Jenkinsfile: pasa --build-arg VITE_KEYCLOAK_CLIENT_ID=hub-frontend al build Co-Authored-By: Claude Sonnet 4.6 --- Jenkinsfile | 1 + frontend/.env.example | 4 ++-- frontend/Dockerfile.prod | 3 +++ frontend/src/lib/server/workspace-auth.ts | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 10f0ad82..edde2aba 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -273,6 +273,7 @@ pipeline { docker build --progress=plain \ --build-arg VITE_API_URL=\${A76_URL}/api/ \ --build-arg VITE_KEYCLOAK_URL=\${A76_URL}/kcauth/ \ + --build-arg VITE_KEYCLOAK_CLIENT_ID=hub-frontend \ --build-arg INTERNAL_API_URL=http://backend:3467/api/ \ -t ${IMAGE_FRONTEND}:${env.APP_VERSION} \ -t ${IMAGE_FRONTEND}:latest \ diff --git a/frontend/.env.example b/frontend/.env.example index d4cf3ba4..fbc32cfa 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -12,10 +12,10 @@ INTERNAL_HUB_URL=http://localhost:8001 # Keycloak VITE_KEYCLOAK_URL=http://localhost:8085/kcauth VITE_KEYCLOAK_REALM=master -VITE_KEYCLOAK_CLIENT_ID=anexo76-frontend +VITE_KEYCLOAK_CLIENT_ID=hub-frontend KEYCLOAK_URL=http://localhost:8085/kcauth KEYCLOAK_REALM=master -KEYCLOAK_CLIENT_ID=anexo76-frontend +KEYCLOAK_CLIENT_ID=hub-frontend # KEYCLOAK_CLIENT_SECRET= # solo si el cliente KC no es público # SvelteKit — necesario para cookies secure y URLs SSR correctas diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod index 8c3cc1bd..83b5b005 100644 --- a/frontend/Dockerfile.prod +++ b/frontend/Dockerfile.prod @@ -22,6 +22,9 @@ ENV VITE_API_URL=${VITE_API_URL} ARG VITE_KEYCLOAK_URL ENV VITE_KEYCLOAK_URL=${VITE_KEYCLOAK_URL} +ARG VITE_KEYCLOAK_CLIENT_ID=hub-frontend +ENV VITE_KEYCLOAK_CLIENT_ID=${VITE_KEYCLOAK_CLIENT_ID} + ARG INTERNAL_API_URL ENV INTERNAL_API_URL=${INTERNAL_API_URL} diff --git a/frontend/src/lib/server/workspace-auth.ts b/frontend/src/lib/server/workspace-auth.ts index e9902830..d6415f03 100644 --- a/frontend/src/lib/server/workspace-auth.ts +++ b/frontend/src/lib/server/workspace-auth.ts @@ -93,7 +93,7 @@ export function getKeycloakRealm(): string { } export function getKeycloakClientId(): string { - return (env.KEYCLOAK_CLIENT_ID || env.VITE_KEYCLOAK_CLIENT_ID || 'anexo76-frontend').trim(); + return (env.KEYCLOAK_CLIENT_ID || env.VITE_KEYCLOAK_CLIENT_ID || 'hub-frontend').trim(); } export function getCleanReturnPath(url: URL): string {