# Deployment

Production runs as a Docker container named `konturai-parser-app`, deployed from
`main` by **Gitea Actions** on a self-hosted runner.

> The project was migrated from GitLab CI to Gitea in August 2026. The GitLab pipeline
> and its runner scripts have been removed; the historical description is preserved in
> [archive/setup-notes/DEPLOYMENT.md](archive/setup-notes/DEPLOYMENT.md).

## Pipeline

[`.gitea/workflows/deploy.yml`](../.gitea/workflows/deploy.yml) triggers on every push
to `main` and delegates to a host script:

```yaml
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - name: Deploy
        run: /usr/local/bin/konturai-deploy.sh marketing-parser
```

`konturai-deploy.sh` lives on the deployment host, not in this repository. It is
shared across KonturAI services and is responsible for syncing the checkout, building
the image and recreating the container.

Note that this workflow does **not** run the test suite — it deploys directly. Run
`./mvnw clean verify` locally before pushing to `main`.

## Container

[`Dockerfile`](../Dockerfile) is a two-stage build:

1. `maven:3.9.9-eclipse-temurin-21` resolves dependencies and runs
   `mvn clean install -DskipTests`.
2. `eclipse-temurin:21-jre-jammy` receives the jar and runs it as the non-root
   `appuser`, with `-XX:+HeapDumpOnOutOfMemoryError` writing to `/dumps`.

Because stage 1 does `COPY . .`, anything required at build time must be present in
the checkout on the host — including
`src/main/resources/keys/google-key.json`, which is not in version control. See
[configuration.md](configuration.md#the-google-service-account-key).

## Compose

[`deployment/docker-compose.server.yml`](../deployment/docker-compose.server.yml)
defines the production service:

- Reads all configuration from `../.env.production` (**not** in version control).
- Joins the pre-existing external Docker network `common_network` under the alias
  `parser-service`, so sibling KonturAI services can reach it by name.
- `restart: unless-stopped`.
- Maps `host.docker.internal` to the host gateway.

No ports are published to the host — traffic arrives through the shared network.

## Server-side environment

`.env.production` sits next to the checkout on the deployment host and supplies every
variable listed in [`.env.example`](../.env.example). **The application will not start
if a required variable is missing** — this is intentional, see
[configuration.md](configuration.md#secret-handling).

Manual operations on the host:

```bash
cd <deploy-dir>/deployment

docker compose ps                          # status
docker compose logs -f parser-service      # follow logs
docker compose up -d --build               # rebuild and recreate
docker compose restart parser-service      # pick up .env.production changes
```

## Verifying a deploy

```bash
docker compose ps                                    # container is Up
docker compose logs --tail=100 parser-service        # no placeholder-resolution errors
curl -fsS http://parser-service:8080/api/parser/health   # from inside common_network
```

A failure to resolve a `${VAR}` placeholder appears immediately in the logs and names
the missing variable.

## Rollback

The deploy script builds from the synced checkout, so rolling back means checking out
the previous commit on the host and rebuilding:

```bash
cd <deploy-dir>
git checkout <previous-good-sha>
cd deployment && docker compose up -d --build
```
