# Ofusca tu build en CI

Dónde encaja AfterPack en tu pipeline: solo en producción, compila una vez y promueve el mismo artefacto por todas las etapas, con una receta de CI y las reglas para semillas, artefactos, source maps y una nube caída.

Source: https://www.afterpack.dev/es/docs/builds

AfterPack es un paso posterior al build: tu bundler produce JavaScript y luego AfterPack lo ofusca. Recomendamos ofuscar una sola vez, en el build de producción, y promover ese artefacto sin cambios por todas las etapas. Una línea más abajo no es una recomendación: un source map desplegado devuelve tu código fuente original, así que mantén los maps fuera del deploy.

## Dev no se ofusca

La ofuscación se ejecuta sobre la salida compilada, así que no tiene nada que hacer contra un servidor de dev. Vite dev, `next dev` y HMR sirven código sin compilar o compilado solo en parte.

- **Desarrollo.** Tu bundler funciona como siempre. AfterPack está inactivo. Los stack traces apuntan a tu código fuente real, el HMR es instantáneo y la depuración es normal.
- **Build de producción.** AfterPack se ejecuta después de que el bundle se cierra. Un [plugin de framework](https://www.afterpack.dev/es/docs/frameworks) lo ejecuta automáticamente. En un stack de bundler puro, lo invocas tú una vez.

Para compilar producción sin ofuscación, por ejemplo un build de depuración puntual, define [`{ build: { autorun: false } }`](https://www.afterpack.dev/es/docs/config#build-autorun) en tu configuración o [`AFTERPACK_build_autorun=false`](https://www.afterpack.dev/es/docs/config#build-autorun) en el entorno.

## Compila una vez, promueve el artefacto

El bundle ofuscado es el artefacto que despliegas, así que el pipeline más limpio lo compila una vez y mueve los mismos bytes por todas las etapas.

*(build-once, promote-everywhere flow diagram — see the web page)*

Volver a ejecutar AfterPack por entorno es la alternativa, y te cuesta la señal de tus pruebas: cada salto produce un bundle con una forma distinta, así que lo que QA ejercitó no es lo que sirve producción. Promover un único artefacto mantiene las pruebas de integración que corres en QA ejecutándose contra los mismos bytes exactos que llegan a producción. Tu ciclo de dev queda sin ofuscar de todos modos.

## La superficie de comandos

```bash
npx afterpack@latest dist/
```

Una ruta al JavaScript emitido, más [cada clave de configuración como flag](https://www.afterpack.dev/es/docs/cli#flags). Nombrar el directorio del build de forma explícita vale la pena en CI, donde uno autodetectado es una forma silenciosa de ofuscar el árbol equivocado. `npx afterpack@latest verify [dir]` vuelve a comprobar un build contra el [recibo de protección](https://www.afterpack.dev/es/docs/frameworks/nextjs#verify) que escribió, que es el paso a ejecutar antes de desplegar. La [referencia de CLI](https://www.afterpack.dev/es/docs/cli#usage) tiene la superficie completa.

## Semilla aleatoria por defecto, fija para reproducibilidad

[La semilla](https://www.afterpack.dev/es/docs/config#seed) hace que cada release sea estructuralmente distinto del anterior, así que una herramienta o un parche construido contra el bundle de la semana pasada falla en este. Por defecto, cada release usa una semilla aleatoria nueva. No tienes que configurar nada para conseguirlo.

La semilla no es un secreto. Guárdala como cualquier otro parámetro de build, en una variable de CI.

Fíjala solo cuando necesites una salida reproducible: aciertos de caché de build, pruebas de golden-snapshot o rebuilds auditables byte a byte.

```bash
npx afterpack@latest dist/ --seed=git
```

[`--seed=git`](https://www.afterpack.dev/es/docs/config#seed) resuelve a `git rev-parse HEAD`. Fuera de un repositorio recurre a una semilla aleatoria nueva y muestra un aviso en lugar de hacer fallar el build. Cualquier otra cadena es hasheada por el motor; un entero se usa tal cual. Una semilla fija produce una salida idéntica cada vez.

> **Fija algo que siga cambiando por release**
>
> Una semilla constante entre releases traslada el análisis de un atacante de uno al siguiente, que es justo la propiedad para la que instalaste AfterPack. Cuando una semilla fija se gana su lugar, fíjala a un valor que cambie en cada release, como el SHA del commit.

## Nombres de archivo y caché

Un plugin de framework da a cada archivo ofuscado un nombre con hash de contenido que sigue a sus bytes ofuscados, así que un mismo nombre nunca sirve dos archivos distintos y los navegadores, las CDN y los service workers nunca reciben un archivo obsoleto bajo un nombre reutilizado. El nombre se deriva del contenido sin ofuscar del archivo más la semilla, la versión del motor, tu configuración, tus directivas y el [modo de build](https://www.afterpack.dev/es/docs/config#build-mode).

Eso convierte la caché en una decisión sobre la semilla. Un release que toma una semilla nueva, por defecto o con `--seed=git`, renombra cada archivo ofuscado, así que los navegadores y los service workers vuelven a descargar cada uno. Una semilla constante mantiene los bytes y el nombre de un archivo sin cambios de un release a otro mientras la versión del motor y la configuración sigan iguales, así que las cachés de larga duración se conservan, con el coste que describe el aviso de arriba.

Con una [`key`](https://www.afterpack.dev/es/docs/config#key), la versión del motor en la nube forma parte del nombre. Cuando el build no puede leer esa versión desde la API, avisa y da a ese build nombres aleatorios, que son seguros pero no se pueden reproducir con una semilla fija.

La [CLI](https://www.afterpack.dev/es/docs/cli#content-hashed-file-names) nombra los archivos del mismo modo en los builds de Next.js, Angular, Vite y webpack. En cualquier otra salida mantiene los nombres que escribió tu bundler e imprime un aviso; si ahí dependes de una caché de larga duración, usa el plugin de tu framework o fija una semilla constante.

## Rota en un calendario

Una semilla aleatoria por release ya varía el artefacto cada vez que despliegas. Los equipos que despliegan poco a menudo suman además un rebuild programado, una ejecución nocturna o semanal desde el mismo código fuente, para que el bundle desplegado cambie de forma en cada ciclo incluso cuando el código no cambia.

Un atacante que empezó a revertir el build N vuelve a cero en el build N+1: no hay estructura compartida entre ambos builds que le permita trasladar el trabajo. Esto importa más para la lógica anti-bot, anti-cheat y resistente a CAPTCHA, donde el adversario tiene tiempo. La rotación por sí sola no detiene las trampas ni el tráfico de bots; la lógica del lado del cliente sigue necesitando validación autoritativa del servidor detrás.

## En CI

El pipeline es: compilar, luego ofuscar, luego mantener los artefactos fuera del deploy. Con un plugin de framework, el paso de ofuscación forma parte de tu `build` normal y puedes omitir la llamada explícita a AfterPack. En un stack de bundler puro, ejecútalo tú mismo.

```yaml
# .github/workflows/release.yml (excerpt)
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 1. Build. With @afterpack/<fw> installed, obfuscation runs here automatically.
      - run: npm ci
      - run: npm run build

      # 2. Pure-bundler stacks only (no framework plugin): obfuscate explicitly.
      #    Pin --seed for Turborepo/Nx remote-cache hits; omit it for a fresh
      #    random seed per release. --diagnostics.level=all prints every diagnostic
      #    instead of rolling the info ones up to one line.
      - run: npx afterpack@latest dist/ --seed=git --diagnostics.level=all --protectionMap.enabled

      # 3. Keep the Protection Map as a reviewable CI artifact. It is off under
      #    CI=true unless asked for: --protectionMap.enabled above, or
      #    protectionMap: { enabled: true } in your plugin options. It is written to
      #    the gitignored .afterpack/ at the PROJECT ROOT, never into the deploy
      #    tree. It embeds your ORIGINAL SOURCE — treat the upload as
      #    source-confidential.
      - uses: actions/upload-artifact@v4
        with:
          name: protection-map
          path: .afterpack/

      # 4. Deploy only the bundle. Never the .afterpack/ directory, never a .map.
      - run: ./deploy.sh dist/
```

Turborepo y Nx indexan su caché remota por hashes de salida. Una semilla aleatoria produce un bundle distinto en cada ejecución, así que esas cachés siempre fallan. Pasa [`--seed=git`](https://www.afterpack.dev/es/docs/config#seed) para aciertos de caché: el SHA sigue cambiando por release, así que los builds se mantienen reproducibles dentro de un commit mientras cambian entre releases.

Adjunta el Protection Map al PR para que un revisor pueda confirmar qué regiones cambiaron de nivel de protección. Es la forma más rápida de detectar un refactor que sacó por accidente un secreto de una región marcada. Trátalo como un artefacto para el revisor.

> **No existe `afterpack audit ./dist`**
>
> La auditoría que impulsa el [escáner de seguridad](https://www.afterpack.dev/es/security-scanner) se ejecuta contra una URL e imprime texto; no acepta un directorio local. Conéctala contra tu URL de staging desplegada después del paso de deploy. Consulta [Auditoría](https://www.afterpack.dev/es/docs/audit).

## Artefactos, y cómo mantenerlos fuera del deploy

AfterPack escribe tres tipos de artefacto local. Cada uno es más sensible que el bundle que describe, y los tres por defecto se inclinan hacia el lado seguro.

| Artefacto | Por defecto | Dónde va | Flag |
| --- | --- | --- | --- |
| [**Protection Map**](https://www.afterpack.dev/es/docs/protection-map) (`protectionMap.html`) | [Activado](https://www.afterpack.dev/es/docs/config#protectionMap-enabled) cuando el bundler emitió un source map detectable y el build no se ejecuta en CI, opt-in en caso contrario | En un build Free, siempre el directorio [`.afterpack/`](https://www.afterpack.dev/es/docs/cli#files) ignorado por git, en la raíz del proyecto, nunca junto al build servido. Un build Pro en la nube no escribe ningún archivo local: su mapa se guarda en el [panel](https://www.afterpack.dev/es/docs/dashboard/builds) y el build imprime un enlace a él al terminar | [`--protectionMap.enabled=false`](https://www.afterpack.dev/es/docs/config#protectionMap-enabled) |
| **Copia de seguridad de tus archivos originales** | [Activado](https://www.afterpack.dev/es/docs/config#build-backup) para la CLI, desactivado para un plugin de framework | `.afterpack/backups/<build dir>/` en la raíz del proyecto para la CLI; un `.backup.<hash>` junto a la salida cuando se le pide a un plugin | [`--build.backup=false`](https://www.afterpack.dev/es/docs/config#build-backup) para desactivarlo |
| **`.map` complementario de source map** | [Desactivado en producción](https://www.afterpack.dev/es/docs/config#sourceMap-enabled), emitido en dev solo si se detectó un map de entrada | Junto al JS emitido | [`--sourceMap.enabled=false`](https://www.afterpack.dev/es/docs/config#sourceMap-enabled) |

Dos cosas ocurren automáticamente, en cada integración:

- **`.afterpack/` se ignora a sí mismo.** AfterPack escribe un `.gitignore` dentro de `.afterpack/` y nunca edita el tuyo. Un archivo `.map` o `.backup.<hash>` escrito junto a tu build no se ignora: mantenlo tú mismo fuera de git y fuera del despliegue.
- **Un artefacto en una ruta servida genera un aviso.** AfterPack avisa si un artefacto termina en un directorio servido. Un source map servido es desofuscación completa.

La copia de seguridad es tu código fuente original tal cual. La CLI la guarda en la raíz del proyecto, bajo el `.afterpack/backups/` ignorado por git, con un manifiesto que registra a qué se ofuscó cada archivo; `npx afterpack@latest restore` devuelve los originales sobre un run. A un plugin al que se le pide `build.backup` en cambio escribe su copia junto a la salida, donde el deploy la recogería.

> **Un source map filtrado deshace la ofuscación**
>
> Los source maps encadenan la salida ofuscada directo de vuelta a tu código fuente original, con [`sourcesContent`](https://www.afterpack.dev/es/docs/config#sourceMap-sourcesContent) llevando el propio código fuente. Nunca despliegues uno.

Un [build de producción](https://www.afterpack.dev/es/docs/config#build-mode) también elimina los source maps que escribió tu bundler, porque contienen tu código fuente original: archivos `.map`, mapas ocultos e inline, comentarios `//# sourceMappingURL=` y mapas de CSS. Lo hacen la CLI y todos los plugins. Con [`sourceMap.enabled`](https://www.afterpack.dev/es/docs/config#sourceMap-enabled) activado, el mapa de AfterPack sustituye al mapa de JavaScript del bundler y los mapas de CSS se conservan. Un mapa que no se puede eliminar hace fallar el build. `node_modules` y cualquier carpeta dentro del directorio del build con su propio `package.json` quedan intactos.

> **Un archivo que el motor no puede ofuscar hace fallar el build**
>
> El build se detiene y nombra el archivo. Usa [`paths.exclude`](https://www.afterpack.dev/es/docs/config#paths-exclude) para excluir uno que deba enviarse sin tocar; [Diagnósticos](https://www.afterpack.dev/es/docs/diagnostics) tiene los códigos y qué significa cada uno.

## La puerta de CI

Cualquier [salida distinta de cero](https://www.afterpack.dev/es/docs/diagnostics#exit-codes) de la CLI significa que el build no envió lo que configuraste, así que la puerta es `if ! npx afterpack@latest dist/ ...; then fail; fi` y no una rama sobre un código específico.

Eso cubre un error de uso (`64`), un límite de tamaño (`3`), un motor que necesita actualizarse (`6`) y, como `1`, un directorio de build vacío, un error del motor, o un build [Pro](https://www.afterpack.dev/es/docs/tiers) con una [`key`](https://www.afterpack.dev/es/docs/config#key) que no pudo alcanzar la [nube de AfterPack](https://www.afterpack.dev/es/docs/deployment-modes). Ese último caso hace fallar el build en lugar de recurrir a una salida más débil, así que un run en verde significa que la protección que pediste es la que se envió. Los builds Free son totalmente locales y nunca llaman a la nube. [Diagnósticos](https://www.afterpack.dev/es/docs/diagnostics#exit-codes) posee el contrato completo y cada código que un build puede emitir.

## Vigilar los builds después de enviarlos

Cada build Pro Cloud llega al panel: cuándo se ejecutó, de qué commit vino, cuántos archivos procesó, qué costó en [MB](https://www.afterpack.dev/es/docs/dashboard/usage) y tiempo de build, y su cobertura e [inflación](https://www.afterpack.dev/es/docs/config#inflation-max). Úsalo para seguir tu cadencia de rotación y comparar builds a lo largo del tiempo. Consulta [Builds en el dashboard](https://www.afterpack.dev/es/docs/dashboard/builds).

Los builds Free locales nunca llegan a AfterPack, así que nunca aparecen ahí.

## Siguiente

- [Buenas prácticas](https://www.afterpack.dev/es/docs/best-practices): qué marcar, qué dejar en paz y cómo mantener CI honesto.
- [Builds en el dashboard](https://www.afterpack.dev/es/docs/dashboard/builds): el registro por build de todo lo que produjo esta página.
- [Modelo de amenaza](https://www.afterpack.dev/es/docs/threat-model): por qué un build que cambia de forma en cada release es la defensa principal.
- [Referencia de CLI](https://www.afterpack.dev/es/docs/cli#flags): la tabla de flags completa detrás de la sinopsis de arriba.
- [Diagnósticos](https://www.afterpack.dev/es/docs/diagnostics#exit-codes): el contrato de salida y la regla de fallo cerrado, completos.
- [Protection Map](https://www.afterpack.dev/es/docs/protection-map): cómo leer el artefacto que subió tu job de CI.
