Compare commits

...
Author SHA1 Message Date
devops 2670727cfa fix(ci,ecommerce): escanear el manifiesto renderizado y endurecer readOnlyRootFilesystem del frontend
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (pull_request) Successful in 23s
Build and Push Frontend / Construir y Subir Imagen (pull_request) Skipped
- Trivy IaC escaneaba solo el patch aislado de Kustomize
  (workloads/ecommerce/frontend/patches/deployment.yaml), no lo que
  realmente corre en el clúster. Eso generaba falsos positivos en todo
  lo heredado del base remoto de platform-templates (runAsNonRoot,
  seccompProfile) porque el patch nunca los repite. Se instala kubectl,
  se renderiza el kustomization completo y se escanea ese output.
- readOnlyRootFilesystem pasa de false a true en el contenedor del
  frontend: revisando next.config.ts (optimización de imágenes) y
  app/api/revalidate/route.ts (revalidateTag sin cache handler custom),
  la imagen sí escribe a ./.next/cache en runtime. Se agregan volúmenes
  emptyDir para .next/cache y /tmp.
2026-08-24 20:35:08 -05:00
devops 3147cafaff chore: release v1.0.133 [skip ci] 2026-08-25 01:23:38 +00:00
devops cb5a08e7cc Merge pull request 'fix(ci): timeout de Trivy insuficiente en el scan de imagen del frontend' (#30) from fix/frontend-trivy-scan-timeout into main
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 20s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 17m24s
2026-08-25 00:56:15 +00:00
devops c0f7c8ef90 fix(ci): timeout de Trivy insuficiente en el scan de imagen del frontend
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (pull_request) Successful in 32s
Build and Push Frontend / Construir y Subir Imagen (pull_request) Skipped
El default de Trivy (5m) dejó de alcanzar para escanear la imagen del
frontend, igual que ya le había pasado a commerce-backend el
2026-08-16 ("context deadline exceeded" ~4m45s, justo tras terminar
el docker build). Se aplica el mismo fix ya validado en
build-medusa.yaml: --timeout 15m0s en ambos scans de imagen (CRITICAL
y HIGH), y se sube timeout-minutes del job de 20 a 30 para que quepan.
2026-08-24 19:54:00 -05:00
devops 78432cd479 Merge pull request 'fix(ecommerce): eliminar legacyProductMeta, datos reales cubren los 17 productos' (#29) from fix/etapa2-subfase1-remove-legacy-product-meta into main
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 50s
Build and Push Frontend / Construir y Subir Imagen (push) Failing after 27m28s
2026-08-24 23:52:45 +00:00
devops 9a8e5d2b04 fix(ecommerce): eliminar legacyProductMeta, datos reales cubren los 17 productos
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (pull_request) Successful in 1m6s
Build and Push Frontend / Construir y Subir Imagen (pull_request) Skipped
Los 17 productos legacy ya tienen category, collection, brand, ageRange,
imagenes y featured/isNew reales en Medusa (migrados en fix #2/#2b de
Etapa 2 sub-fase 1), por lo que el fallback hardcodeado en mapProduct()
ya no aporta nada y solo duplica datos que pueden desincronizarse.
2026-08-24 18:50:55 -05:00
devops 1bcb6911db chore: release v1.0.129 [skip ci] 2026-08-24 05:43:38 +00:00
devops 04a0abb155 Merge pull request 'fix(ecommerce): sintaxis correcta de +categories en fields de /store/products' (#28) from fix/etapa2-subfase1-categorias-fields-syntax into main
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 23s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 15m52s
2026-08-24 05:27:22 +00:00
3 changed files with 65 additions and 215 deletions

No files matched your search

+37 -2
View File
@@ -122,11 +122,12 @@ jobs:
needs: gitleaks
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 30
env:
APP_DIR: workloads/ecommerce
MANIFEST_FILE: workloads/ecommerce/frontend/patches/deployment.yaml
KUSTOMIZE_DIR: workloads/ecommerce/frontend
IMAGE_NAME: gitea.cruzcloud.net/devops/ecommerce-frontend
steps:
@@ -276,6 +277,32 @@ jobs:
chmod +x /tmp/trivy
/tmp/trivy version
- name: Instalar kubectl
shell: bash
run: |
set -euo pipefail
KUBECTL_VERSION="v1.31.2"
curl -sSfL \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \
-o /tmp/kubectl
chmod +x /tmp/kubectl
/tmp/kubectl version --client
# ${MANIFEST_FILE} es solo el patch de Kustomize del Deployment, no el
# manifiesto que de verdad corre en el clúster -- escanearlo directo
# generaba falsos positivos en todo lo que hereda del base remoto de
# platform-templates (runAsNonRoot, seccompProfile), porque ese patch
# nunca repite lo que ya trae el base (visto en vivo 2026-08-24:
# comparando el patch aislado contra el output real de
# "kubectl kustomize", 2 de 5 hallazgos desaparecían). Renderizamos el
# kustomization completo (${KUSTOMIZE_DIR}, que sí trae el base vía
# git remoto) y escaneamos eso.
- name: Renderizar manifiesto Kustomize
shell: bash
run: |
set -euo pipefail
/tmp/kubectl kustomize "${KUSTOMIZE_DIR}" > /tmp/frontend-rendered.yaml
# Escanea el manifiesto Kubernetes real del frontend (no la imagen):
# contenedores como root, falta de resource limits, falta de
# readiness/liveness probes, etc. Informativo por ahora — no bloquea
@@ -287,7 +314,7 @@ jobs:
/tmp/trivy config \
--severity CRITICAL,HIGH,MEDIUM \
--exit-code 0 \
"${MANIFEST_FILE}"
/tmp/frontend-rendered.yaml
# Instalado directo con pip (no como container aparte): el runner de
# Gitea ejecuta cada job ya dentro de un contenedor propio que habla
@@ -367,6 +394,12 @@ jobs:
# CRITICAL bloquea el pipeline: no se sube una imagen con una CVE
# crítica conocida y con fix disponible.
# --timeout 15m0s: el default de Trivy (5m) dejó de alcanzar también
# para el frontend (visto en vivo: "context deadline exceeded" a los
# ~4m45s, en un run justo después de terminar el docker build, con
# el host bajo la misma inestabilidad de nodo vista ese día). Mismo
# fix ya aplicado en build-medusa.yaml el 2026-08-16 -- el supuesto
# de que el frontend era más liviano y no lo necesitaba ya no vale.
- name: Escanear imagen (Trivy) — CRITICAL bloquea
shell: bash
run: |
@@ -375,6 +408,7 @@ jobs:
--severity CRITICAL \
--exit-code 1 \
--ignore-unfixed \
--timeout 15m0s \
"${IMAGE_NAME}:${{ steps.vars.outputs.VERSION }}"
# HIGH solo informa por ahora — no bloquea mientras aprendemos a leer
@@ -387,6 +421,7 @@ jobs:
--severity HIGH \
--exit-code 0 \
--ignore-unfixed \
--timeout 15m0s \
"${IMAGE_NAME}:${{ steps.vars.outputs.VERSION }}"
# SBOM de la imagen que ya pasó el gate de CRITICAL — describe
@@ -25,7 +25,7 @@ spec:
runAsUser: 1001
containers:
- name: app
image: gitea.cruzcloud.net/devops/ecommerce-frontend:v1.0.122
image: gitea.cruzcloud.net/devops/ecommerce-frontend:v1.0.133
env:
- name: HEADLESS_PROVIDER
value: medusa
@@ -64,9 +64,20 @@ spec:
# binario de node para esto, pero eso solo funciona si el
# bounding set del contenedor lo permite -- drop: ALL del base
# lo bloquea igual, root o no root. Mismo motivo que docs-portal.
#
# readOnlyRootFilesystem: true -- revisado en código (2026-08-24,
# aún no probado en vivo con este cambio) que esta imagen sí
# escribe a disco en runtime, no solo en build: el servidor
# standalone de Next.js cachea a ./.next/cache tanto el
# optimizador de imágenes (next.config.ts trae remotePatterns,
# sin "unoptimized: true") como el revalidateTag("medusa-products")
# de app/api/revalidate/route.ts (cache handler por default, sin
# uno custom tipo Redis). De ahí los dos volumeMounts de abajo:
# sin ellos, con root filesystem de solo lectura, ambas rutas
# fallarían al intentar escribir.
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
@@ -79,3 +90,13 @@ spec:
limits:
cpu: "1"
memory: 768Mi
volumeMounts:
- name: next-cache
mountPath: /app/.next/cache
- name: tmp
mountPath: /tmp
volumes:
- name: next-cache
emptyDir: {}
- name: tmp
emptyDir: {}
@@ -66,190 +66,6 @@ type MedusaProductsResponse = {
offset?: number;
};
type LegacyMeta = {
category: string;
collection: string;
brand: string;
ageRange: string;
featured: boolean;
isNew: boolean;
fallbackImage: string;
fallbackThumbnail: string;
};
const legacyProductMeta: Record<string, LegacyMeta> = {
"toy-story-5-rex": {
"category": "Figuras",
"collection": "Toy Story",
"brand": "Disney Pixar",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-001-toy-story-5-rex.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-001-toy-story-5-rex.webp"
},
"toy-story-5-buzz-lightyear": {
"category": "Figuras",
"collection": "Toy Story",
"brand": "Disney Pixar",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-002-toy-story-5-buzz-lightyear.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-002-toy-story-5-buzz-lightyear.webp"
},
"toy-story-5-jessie": {
"category": "Figuras",
"collection": "Toy Story",
"brand": "Disney Pixar",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-003-toy-story-5-jessie.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-003-toy-story-5-jessie.webp"
},
"toy-story-5-woody": {
"category": "Figuras",
"collection": "Toy Story",
"brand": "Disney Pixar",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-004-toy-story-5-woody.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-004-toy-story-5-woody.webp"
},
"intensamente-furia": {
"category": "Peluches",
"collection": "Intensamente",
"brand": "Disney",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-005-intensamente-furia.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-005-intensamente-furia.webp"
},
"intensamente-tristeza": {
"category": "Peluches",
"collection": "Intensamente",
"brand": "Disney",
"ageRange": "3+",
"featured": true,
"isNew": true,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-006-intensamente-tristeza.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-006-intensamente-tristeza.webp"
},
"minnie-y-mickey-mouse": {
"category": "Peluches",
"collection": "Mickey & Minnie",
"brand": "Disney",
"ageRange": "3+",
"featured": true,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-007-minnie-y-mickey-mouse.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-007-minnie-y-mickey-mouse.webp"
},
"angel-lilo-y-stitch": {
"category": "Peluches",
"collection": "Lilo & Stitch",
"brand": "Disney",
"ageRange": "3+",
"featured": true,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-008-angel-lilo-y-stitch.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-008-angel-lilo-y-stitch.webp"
},
"mochilas-disney-coleccion-marvel": {
"category": "Mochilas",
"collection": "Marvel",
"brand": "Marvel",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-009-mochilas-disney-coleccion-marvel.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-009-mochilas-disney-coleccion-marvel.webp"
},
"mochila-ligera-y-resistente-al-agua-de-disney-frozen-mochila-escolar-y-de-viaje-con-multiples-compa": {
"category": "Mochilas",
"collection": "Frozen",
"brand": "Disney",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-010-mochila-ligera-y-resistente-al-agua-de-d.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-010-mochila-ligera-y-resistente-al-agua-de-d.webp"
},
"mochila-escolar-de-spider-man-de-disney-moda-estudiantil-libro-de-gran-capacidad-mochila-ligera": {
"category": "Mochilas",
"collection": "Marvel",
"brand": "Marvel",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-011-mochila-escolar-de-spider-man-de-disney-.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-011-mochila-escolar-de-spider-man-de-disney-.webp"
},
"disney-mochila-de-poliester-ultraligera-y-transpirable-de-gran-tamano-y-multiples-colores-dimension": {
"category": "Mochilas",
"collection": "Disney",
"brand": "Disney",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-012-disney-mochila-de-poliester-ultraligera-.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-012-disney-mochila-de-poliester-ultraligera-.webp"
},
"mochila-pequena-bob-esponja": {
"category": "Mochilas",
"collection": "Bob Esponja",
"brand": "Disney",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-013-mochila-pequena-bob-esponja.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-013-mochila-pequena-bob-esponja.webp"
},
"mochila-casual-de-gran-capacidad-con-estampado-de-bob-esponja-unisex-bolsa-escolar-de-dibujos-anim": {
"category": "Mochilas",
"collection": "Bob Esponja",
"brand": "Disney",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-014-mochila-casual-de-gran-capacidad-con-est.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-014-mochila-casual-de-gran-capacidad-con-est.webp"
},
"mochila-escolar-estilo-britanico-de-disney-con-proteccion-de-la-columna-vertebral-nueva-mochila-de": {
"category": "Mochilas",
"collection": "Disney",
"brand": "Disney",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-015-mochila-escolar-estilo-britanico-de-disn.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-015-mochila-escolar-estilo-britanico-de-disn.webp"
},
"disney-mochila-iron-man-de-poliester-ultraligera-y-transpirable-de-gran-tamano-dimensiones-46-cm-x": {
"category": "Mochilas",
"collection": "Marvel",
"brand": "Marvel",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-016-disney-mochila-iron-man-de-poliester-ult.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-016-disney-mochila-iron-man-de-poliester-ult.webp"
},
"mochila-de-disney-marvel-capitan-america-de-gran-capacidad-ligera-y-resistente-a-salpicaduras-par": {
"category": "Mochilas",
"collection": "Marvel",
"brand": "Marvel",
"ageRange": "6+",
"featured": false,
"isNew": false,
"fallbackImage": "https://shop.cruzcloud.net/images/products/ari-017-mochila-de-disney-marvel-capitan-america.webp",
"fallbackThumbnail": "https://shop.cruzcloud.net/images/thumbnails/ari-017-mochila-de-disney-marvel-capitan-america.webp"
}
};
function backendUrl(): string {
const value = process.env.MEDUSA_BACKEND_URL;
if (!value) {
@@ -338,7 +154,6 @@ function mapVariant(variant: MedusaVariant): ProductVariant {
function mapProduct(product: MedusaProduct): Product {
const slug = product.handle || product.id;
const legacy = legacyProductMeta[slug];
const variant = product.variants?.[0];
const calculated = variant?.calculated_price;
@@ -346,24 +161,16 @@ function mapProduct(product: MedusaProduct): Product {
const category =
product.categories?.[0]?.name ||
stringMetadata(product.metadata, "category") ||
legacy?.category ||
"Variedades";
const collection =
product.collection?.title ||
stringMetadata(product.metadata, "collection") ||
legacy?.collection ||
"";
const brand =
stringMetadata(product.metadata, "brand") ||
legacy?.brand ||
"ARI Shopping";
const brand = stringMetadata(product.metadata, "brand") || "ARI Shopping";
const ageRange =
stringMetadata(product.metadata, "ageRange") ||
legacy?.ageRange ||
"Todas";
const ageRange = stringMetadata(product.metadata, "ageRange") || "Todas";
const images = [
...(product.images || [])
@@ -371,15 +178,8 @@ function mapProduct(product: MedusaProduct): Product {
.filter(Boolean),
];
if (images.length === 0 && legacy?.fallbackImage) {
images.push(legacy.fallbackImage);
}
const thumbnail =
product.thumbnail ||
images[0] ||
legacy?.fallbackThumbnail ||
"/images/brand/logo-ari.png";
product.thumbnail || images[0] || "/images/brand/logo-ari.png";
const price =
typeof calculated?.calculated_amount === "number"
@@ -426,14 +226,8 @@ function mapProduct(product: MedusaProduct): Product {
currency: normalizeCurrency(calculated?.currency_code),
priceLabel: price === null ? "Consultar disponibilidad" : "",
available,
featured:
booleanMetadata(product.metadata, "featured") ||
legacy?.featured ||
false,
isNew:
booleanMetadata(product.metadata, "isNew") ||
legacy?.isNew ||
false,
featured: booleanMetadata(product.metadata, "featured"),
isNew: booleanMetadata(product.metadata, "isNew"),
image: images[0] || thumbnail,
thumbnail,
images: images.length > 0 ? images : [thumbnail],