# velastack/action

`velastack/action` despliega una aplicación de Vela en tu propio servidor desde GitHub Actions. Compila en el runner y entrega la release a [`vela deploy`](/es/deploy) por SSH, de modo que un despliegue desde CI y uno desde tu portátil hacen lo mismo.

### Antes de empezar

Aprovisiona el servidor y despliega una vez desde tu propia máquina — ese primer despliegue es el que genera el id de la aplicación:

```
$ vela provision root@your-server
$ vela deploy --server root@your-server --domain example.com
$ git add .vela/project.json && git commit -m "Add the vela project id"
```

La acción se niega a ejecutarse hasta que `.vela/project.json` esté versionado. Un runner que no encuentra ningún id genera uno nuevo, y como su copia del repositorio se descarta al final del job, cada despliegue se instalaría como una aplicación nueva y dejaría huérfana la anterior en el servidor.

Después, dale a la acción una forma de entrar:

```
$ ssh-keygen -t ed25519 -f vela-deploy -C "github actions"
$ ssh-copy-id -i vela-deploy.pub root@your-server
```

Añade la clave privada al repositorio como un secreto llamado `SSH_PRIVATE_KEY`.

### Uso

```yaml
name: Deploy

on:
  push:
    branches: [main]

concurrency:
  group: deploy-prod
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: velastack/action@v1
        with:
          server: root@your-server
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
          domain: example.com
```

La acción instala las dependencias, compila, sube una release, ejecuta las migraciones, reinicia los servicios y comprueba la salud del resultado. Un despliegue que falla su comprobación de salud restaura la release anterior, migraciones incluidas, y hace fallar el job. Usa un grupo de `concurrency` para que dos pushes se pongan en cola en lugar de competir; el servidor también serializa los despliegues de un mismo objetivo, y descarta uno que resulte ser más antiguo que lo que se puso en línea entretanto — consulta [Dos despliegues a la vez](/es/deploy).

### Previsualizaciones de pull requests

Añade los eventos de pull request y una clave de API, y cada pull request recibe su propia [previsualización](/es/deploy/previews): una copia completa de la aplicación en el servidor, en `<proyecto>--<rama>.velastack.app`.

```yaml
name: Deploy

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened, closed]

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: deploy-${{ github.head_ref || github.ref_name }}
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: velastack/action@v1
        with:
          server: root@your-server
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
          api-key: ${{ secrets.VELA_API_KEY }}
          domain: example.com
```

El objetivo sale del evento cuando `target` no está definido: un push despliega `production`, un pull request despliega `preview:<rama>`. Cerrar el pull request, fusionado o no, elimina la previsualización del servidor — base de datos y archivos subidos incluidos, con una instantánea que se conserva dos semanas en la papelera del servidor — y retira su nombre de host. Se mantiene un comentario en el pull request actualizado con la URL de la previsualización en cada push, y para eso sirve `pull-requests: write`; el token de un fork no puede comentar, y eso nunca hace fallar el despliegue.

La eliminación va montada en el evento `closed`, así que mantenlo en el disparador. Si el workflow ejecuta pruebas antes de desplegar, deja que ese evento se las salte: una rama se cierra a menudo precisamente porque está en rojo, y condicionar la limpieza a que esté en verde deja la previsualización atrás para siempre.

```yaml
jobs:
  check:
    if: github.event.action != 'closed'
    # ...
  deploy:
    needs: check
    if: ${{ !cancelled() && (github.event.action == 'closed' || needs.check.result == 'success') }}
    # ...
```

Las previsualizaciones nunca heredan `domain` — ese es de producción. Para servirlas también en tu propio dominio, añade una base de previsualización en la página de [Dominios](/es/deploy/domains) del proyecto.

`VELA_API_KEY` es lo que registra los despliegues en velastack.dev y da a las previsualizaciones su nombre de host. Crea una en [velastack.dev/api-keys/new](https://velastack.dev/api-keys/new) y añádela como secreto del repositorio. El proyecto también tiene que estar [vinculado](/es/link), lo que `vela link` escribe en el mismo `.vela/project.json` que ya versionas. Sin la clave el despliegue sigue ejecutándose; simplemente no se registra, y una previsualización no tiene dónde servirse.

Si tu objetivo de producción tiene un nombre distinto de `production`, pásalo en todos los eventos salvo en un pull request, y déjalo vacío ahí para que el objetivo de previsualización pueda tomar el relevo. El valor vacío tiene que ir al final: `''` es falso en las expresiones de GitHub, así que `cond && '' || 'prod'` parece un ternario pero devuelve `prod` en todos los eventos — y en un pull request cerrado eso intentaría eliminar producción. La acción se niega a eliminar cualquier cosa que no sea una previsualización en modo `auto`, pero los despliegues habrían aterrizado igualmente en producción.

```yaml
          target: ${{ !startsWith(github.event_name, 'pull_request') && 'prod' || '' }}
```

### Dependabot y forks

Los pull requests que abre Dependabot, y cualquier pull request desde un fork, se ejecutan sin los secretos del repositorio, así que la acción no tiene clave SSH y no puede llegar al servidor. Sáltate el job para ellos en lugar de dejar que falle; sin previsualización desplegada tampoco hay nada que eliminar al cerrar, de modo que la misma condición cubre el evento `closed`:

```yaml
  deploy:
    if: github.event_name != 'pull_request' || (github.event.pull_request.user.login != 'dependabot[bot]' && !github.event.pull_request.head.repo.fork)
```

### Entradas

- `server` - Obligatorio. Destino SSH, como `user@host` o `host`.
- `ssh-key` - Obligatorio. Clave privada con acceso al servidor.
- `ssh-port` - Puerto, cuando el servidor no escucha en el 22.
- `known-hosts` - Contenido para `known_hosts`. Sin él, la clave del host se obtiene en la primera conexión.
- `target` - Qué copia de la aplicación desplegar. Por defecto `production`, o `preview:<rama>` en un pull request. `environment` es el nombre obsoleto, aún respetado cuando `target` no está definido.
- `domain` - Nombre(s) de host en los que servir, separados por comas. Solo se necesita la primera vez, o cuando cambia. Nunca se aplica a las previsualizaciones.
- `api-key` - Clave de API de velastack.dev, para que el despliegue se registre en el proyecto vinculado y las previsualizaciones reciban un nombre de host `velastack.app`.
- `action` - `deploy`, `destroy` o `auto`. `auto` despliega en todos los eventos salvo en un pull request cerrado, donde elimina la previsualización — y únicamente una previsualización: un evento `closed` cuyo objetivo sea cualquier otra cosa hace fallar el job. Por defecto `auto`.
- `confirm-name` - El nombre de la aplicación, obligatorio cuando `action: destroy` apunta a producción o a un entorno con nombre como `staging`. Nunca hace falta para las previsualizaciones.
- `comment` - En pull requests, mantener un comentario actualizado con la URL de la previsualización. Por defecto `true`.
- `github-token` - Token con el que se publica el comentario. Por defecto el token del workflow, que necesita `pull-requests: write`.
- `project` - Sobrescribir el nombre del proyecto.
- `health-path` - Ruta que solicita la comprobación de salud.
- `remote-db` - Compilar contra la base de datos del servidor. Sin definir, decide la CLI: activado una vez que el objetivo ha sido desplegado. Pon `false` para compilar contra una desechable.
- `working-directory` - Directorio que contiene la aplicación. Por defecto `.`.
- `node-version` - Versión de Node.js con la que compilar. Por defecto la de `.nvmrc` o `.node-version`, y si no, 24.
- `install` - Ejecutar `npm ci` primero. Por defecto `true`.
- `vela-version` - Versión de la CLI a ejecutar. Por defecto la que fija el proyecto.

### Salidas

- `release` - Identificador de la release que se activó
- `url` - URL en la que se sirve la aplicación
- `hostnames` - Todos los nombres de host en los que se sirve el objetivo, separados por comas
- `target` - El objetivo que se desplegó o eliminó, como `production` o `preview:my-branch`

En una eliminación, `release`, `url` y `hostnames` quedan vacíos.

### Páginas que se prerenderizan a partir de datos

Una página prerenderizada se renderiza una vez, en tiempo de compilación, contra la base de datos que la compilación pueda ver. En un runner esa sería una base de datos vacía y desechable, así que esas páginas saldrían llenas de valores por defecto.

El despliegue lo evita por sí solo: una vez que el objetivo ha sido desplegado, la compilación se renderiza contra la base de datos de esa instancia, tunelizada por la misma conexión SSH. Las credenciales del superusuario se obtienen del servidor, así que no entran secretos nuevos en CI, y la compilación solo lee. No hay que configurar nada para esto.

Pon `remote-db: false` para renunciar y compilar contra una base de datos desechable:

```yaml
      - uses: velastack/action@v1
        with:
          server: root@your-server
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
          remote-db: false
```

Pasa `domain` en el primer despliegue desde CI, para que las páginas prerenderizadas vean el dominio como `url.origin` y no el host de relleno de SvelteKit. Sus enlaces canónicos, como el nombre de la aplicación, salen de [`src/lib/site.ts`](/es/cli/project-structure), no de la base de datos.

### Secretos

Las variables de entorno de producción viven en tu servidor, no en la acción ni en la release. Defínelas una vez con [`vela env`](/es/env). Todo lo que necesite la _compilación_ — a diferencia de la aplicación en ejecución — va en el workflow, porque tiene que existir en el runner:

```yaml
      - uses: velastack/action@v1
        env:
          POCKETBASE_SUPERUSER_EMAIL: ${{ secrets.POCKETBASE_SUPERUSER_EMAIL }}
          POCKETBASE_SUPERUSER_PASSWORD: ${{ secrets.POCKETBASE_SUPERUSER_PASSWORD }}
        with:
          server: root@your-server
          ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
```

### Fijar la clave del host

Por defecto, la acción confía en la clave del host del servidor la primera vez que se conecta. Para fijarla en su lugar, captúrala una vez y pasa la salida como entrada `known-hosts`:

```
$ ssh-keyscan -H your-server
```

### Requisitos

- La aplicación se compila con `@sveltejs/adapter-node`
- `.vela/project.json` está versionado — contiene el id de la aplicación con el que el servidor indexa todo
- El repositorio usa npm (existe un `package-lock.json`)