Skip to content

Deploy y mantenimiento

Mindset & Code edited this page Aug 18, 2026 · 5 revisions

Deploy y mantenimiento

🇬🇧 English first · 🇪🇸 Español más abajo.

The two commands

"deploy:marca":    "npm run build && firebase deploy --only hosting:marca",
"deploy:personal": "npm run build:personal && node scripts/desplegar-personal.mjs"

Everything else in package.json is dev, build, build:personal, lint and preview. There is no CI, no GitHub Action and no deploy hook: publishing is one command run by hand.

The brand build — npm run deploy:marca

npm install
npm run dev            # http://localhost:5173
npm run deploy:marca   # build + publish

firebase.json and .firebaserc between them fix the destination:

Field Value
Firebase project sales-weather-etl
Hosting target marca
Site proyectos-mindset-code
Public directory dist
Rewrite **/index.html

The rewrite is what makes a single-page app work on static hosting: any path returns index.html and React Router resolves it in the browser. The consequence is that every URL returns 200, including ones that do not exist — a typo in a link does not show up as a 404 in the logs.

--only hosting:marca is not optional dressing. The Firebase project hosts more than one site, and a bare firebase deploy would touch what the target excludes.

The personal build — npm run deploy:personal

scripts/desplegar-personal.mjs exists because the destination site's identifier cannot live in this repository: it is public and belongs to the organisation, and whoever reads it must not be able to reach the signed portfolio from here. So the script:

  1. Reads SITIO_PERSONAL from .env.personal — gitignored, present on one machine — and exits with a clear message if it is missing.
  2. Writes firebase.personal.json on the fly, pointing at that site and at dist-personal. That file is gitignored too.
  3. Copies personal/retrato.jpg into dist-personal/, warning rather than failing if it is not there.
  4. Deploys with --config firebase.personal.json.
  5. Deletes the temporary config in a finally, so it disappears whether or not the deploy succeeded.

Point 5 is the one that matters: a failed deploy that left the config behind would leave the site identifier sitting in the working tree, one git add . away from being published.

The portrait is copied after the build for the same class of reason. It lives in personal/ rather than public/ because Vite copies public/ wholesale into the output, so a portrait there would ship with the brand build — the one that must not carry a face.

Updating a dashboard's data

The JSON files are committed, so this is a normal commit:

# 1. Regenerate in the project that owns the data
#    (the three .mjs generators live in project-sales-weather-etl)
node generate_churn_json.mjs

# 2. Copy the files across
cp web/public/data/churn/*.json ../project-portfolio/public/data/churn/

# 3. Commit and publish
cd ../project-portfolio
git add public/data && git commit -m "Actualiza los datos de churn"
npm run deploy:marca

Nothing automates step 2. See Arquitectura del sitio for which generator produces which folder.

The embedded simulator is copied the same way, so diff before you copy. public/demos/revenue/ is a copy of project-revenue-management-web. The two are identical today, but they were not for a few hours: a fix to the 7-day chart landed here first and reached the original later. cp -r in the wrong direction would have thrown it away, silently and with a clean git status on both sides.

Local development

npm run dev

Vite serves public/ directly, so changing a JSON needs a browser refresh and no rebuild. npm run preview serves the built dist/ if you need to check the production bundle, and npm run lint runs ESLint 9 with the React Hooks and React Refresh plugins.

What .gitignore keeps out, and why

Entry Reason
.env.personal, personal/, firebase.personal.json everything that identifies or signs the personal variant
dist/, dist-marca/, dist-personal/, .firebase/ build output — the source is the source
CLAUDE.md, .claude/, agents/, skills/, .engram/ local tooling, not part of the site

Note that plain .env is not on the list: under Vite only VITE_-prefixed variables reach the bundle, and that file holds the shared build configuration, while anything that identifies a person goes in .env.personal.


🇪🇸 Español

Los dos comandos

"deploy:marca":    "npm run build && firebase deploy --only hosting:marca",
"deploy:personal": "npm run build:personal && node scripts/desplegar-personal.mjs"

Lo demás en package.json es dev, build, build:personal, lint y preview. No hay CI, ni GitHub Action, ni hook de despliegue: publicar es un comando lanzado a mano.

La compilación de marca — npm run deploy:marca

npm install
npm run dev            # http://localhost:5173
npm run deploy:marca   # compila y publica

Entre firebase.json y .firebaserc queda fijado el destino:

Campo Valor
Proyecto de Firebase sales-weather-etl
Destino de hosting marca
Sitio proyectos-mindset-code
Directorio público dist
Reescritura **/index.html

La reescritura es lo que hace que una aplicación de una sola página funcione sobre hosting estático: cualquier ruta devuelve index.html y React Router la resuelve en el navegador. La consecuencia es que todas las URL devuelven 200, también las que no existen — una errata en un enlace no aparece como un 404 en los registros.

--only hosting:marca no es un adorno. El proyecto de Firebase aloja más de un sitio, y un firebase deploy a secas tocaría lo que el destino deja fuera.

La compilación personal — npm run deploy:personal

scripts/desplegar-personal.mjs existe porque el identificador del sitio de destino no puede vivir en este repositorio: es público y pertenece a la organización, y quien lo lea no debe poder llegar desde aquí al portafolio firmado. Así que el script:

  1. Lee SITIO_PERSONAL de .env.personal —en .gitignore, presente en una sola máquina— y sale con un mensaje claro si falta.
  2. Escribe firebase.personal.json al vuelo, apuntando a ese sitio y a dist-personal. Ese fichero también está en .gitignore.
  3. Copia personal/retrato.jpg a dist-personal/, avisando en vez de fallar si no está.
  4. Despliega con --config firebase.personal.json.
  5. Borra la configuración temporal en un finally, de modo que desaparece haya salido bien o mal el despliegue.

El punto 5 es el que importa: un despliegue fallido que dejara la configuración atrás dejaría el identificador del sitio en el árbol de trabajo, a un git add . de publicarse.

El retrato se copia después de compilar por la misma clase de razón. Vive en personal/ y no en public/ porque Vite copia public/ entera a la salida, así que un retrato ahí se publicaría también en la compilación de marca — justo la que no debe llevar cara.

Actualizar los datos de un dashboard

Los JSON están commiteados, así que esto es un commit normal:

# 1. Regenerar en el proyecto dueño de los datos
#    (los tres generadores .mjs viven en project-sales-weather-etl)
node generate_churn_json.mjs

# 2. Copiar los ficheros
cp web/public/data/churn/*.json ../project-portfolio/public/data/churn/

# 3. Commit y publicación
cd ../project-portfolio
git add public/data && git commit -m "Actualiza los datos de churn"
npm run deploy:marca

El paso 2 no lo automatiza nada. En Arquitectura del sitio está qué generador produce cada carpeta.

El simulador embebido se copia igual, así que haz un diff antes de copiar. public/demos/revenue/ es una copia de project-revenue-management-web. Hoy son idénticos, pero no lo fueron durante unas horas: un arreglo del gráfico de 7 días entró aquí primero y llegó al original después. Un cp -r en el sentido equivocado lo habría tirado a la basura, en silencio y dejando los dos repositorios limpios en git status.

Desarrollo local

npm run dev

Vite sirve public/ directamente, así que cambiar un JSON solo pide recargar el navegador, sin recompilar. npm run preview sirve el dist/ ya compilado si hace falta comprobar el bundle de producción, y npm run lint pasa ESLint 9 con los plugins de React Hooks y React Refresh.

Qué deja fuera .gitignore, y por qué

Entrada Razón
.env.personal, personal/, firebase.personal.json todo lo que identifica o firma la variante personal
dist/, dist-marca/, dist-personal/, .firebase/ salida de compilación — la fuente es la fuente
CLAUDE.md, .claude/, agents/, skills/, .engram/ herramientas locales, que no son parte del sitio

Conviene notar que el .env a secas no está en la lista: con Vite solo llegan al bundle las variables con prefijo VITE_, y ese fichero lleva la configuración de compilación compartida, mientras que todo lo que identifica a una persona va en .env.personal.