Use native Gitea contexts, audit the full dependency tree, and document the runner and branch-protection requirements needed for reliable deployments.
104 lines
3.9 KiB
Markdown
104 lines
3.9 KiB
Markdown
# Deployment
|
|
|
|
The Gitea Actions workflow in `.gitea/workflows/ci-cd.yml` verifies every pull
|
|
request and every push to `main`. A successful push to `main` is then deployed
|
|
over SSH. The server builds the exact Git archive tested by CI, starts the
|
|
Compose service, and waits for its container health check.
|
|
|
|
Releases are stored in `<DEPLOY_PATH>/releases/<commit-sha>`. The `current`
|
|
symlink is changed only after the new container is healthy. If startup fails,
|
|
the deployment script automatically rebuilds and starts the previous release.
|
|
|
|
## Gitea Actions setup
|
|
|
|
Enable **Repository Actions** in the repository settings and register a trusted
|
|
Gitea Runner. The workflow uses the `ubuntu-latest` label; that runner image must
|
|
provide Bash, Git, `tar`, OpenSSH (`ssh` and `scp`), and standard GNU utilities.
|
|
Node.js 22 is installed by the workflow.
|
|
|
|
After the first successful verification run, protect `main` in Gitea and make
|
|
the **Verify production build** status check required. This prevents a pull
|
|
request from merging when linting, type-checking, tests, the production build,
|
|
or the dependency audit fails.
|
|
|
|
## Gitea repository secrets
|
|
|
|
Add these secrets under the repository's **Settings > Actions > Secrets**:
|
|
|
|
| Secret | Value |
|
|
| --- | --- |
|
|
| `DEPLOY_HOST` | Server hostname or IP address |
|
|
| `DEPLOY_USER` | SSH user allowed to run Docker |
|
|
| `DEPLOY_PORT` | SSH port; optional, defaults to `22` |
|
|
| `DEPLOY_PATH` | Absolute server directory, for example `/srv/portfolio` |
|
|
| `DEPLOY_SSH_KEY` | Private SSH key for the deployment user |
|
|
| `DEPLOY_KNOWN_HOSTS` | Pinned SSH host-key line for the server |
|
|
|
|
Generate the known-hosts value from a trusted machine and verify its fingerprint
|
|
before saving it:
|
|
|
|
```sh
|
|
ssh-keyscan -p 22 your-server.example.com
|
|
```
|
|
|
|
Do not copy an unverified key directly into the secret; doing so would remove
|
|
the protection against a man-in-the-middle attack.
|
|
|
|
## One-time server setup
|
|
|
|
The server needs Docker Engine with the Compose v2 plugin. The deployment user
|
|
must be able to run `docker` without an interactive password prompt.
|
|
|
|
Create the deployment root and the external network once:
|
|
|
|
```sh
|
|
sudo mkdir -p /srv/portfolio/releases
|
|
sudo chown -R deploy:deploy /srv/portfolio
|
|
docker network create caddy_proxy
|
|
```
|
|
|
|
Replace `deploy:deploy` and `/srv/portfolio` with the values used for
|
|
`DEPLOY_USER` and `DEPLOY_PATH`. Add the public half of `DEPLOY_SSH_KEY` to the
|
|
deployment user's `~/.ssh/authorized_keys`.
|
|
|
|
The standalone Next.js container joins `caddy_proxy` under the `portfolio-app`
|
|
service name. Caddy must also join that network and proxy to
|
|
`portfolio-app:3000`. No host port is published by this project.
|
|
|
|
A minimal Caddy route is:
|
|
|
|
```caddyfile
|
|
portfolio.example.com {
|
|
reverse_proxy portfolio-app:3000
|
|
}
|
|
```
|
|
|
|
Caddy forwards the browser's `Accept` header by default; keep that behavior so
|
|
Next.js can negotiate WebP responses. Do not replace the cache headers emitted
|
|
for `/_next/image`. The named `next-image-cache` volume persists `.next/cache`
|
|
between container replacements so optimized variants can be reused.
|
|
|
|
The application expects immutable media URLs. When an admin or future upload
|
|
workflow replaces an image, write it to a UUID or content-versioned URL instead
|
|
of overwriting the previous object at the same URL. Next.js has no per-image
|
|
optimizer-cache invalidation API.
|
|
|
|
## Manual deployment
|
|
|
|
For an emergency manual deployment from a checkout on the server:
|
|
|
|
```sh
|
|
docker compose --project-name portfolio up --detach --build --remove-orphans
|
|
```
|
|
|
|
Old versioned release directories are deliberately retained for rollback. They
|
|
can be removed manually after a newer release has been proven stable; keep the
|
|
directory referenced by the `current` symlink.
|
|
|
|
## Runtime verification
|
|
|
|
The production image contains `.next/standalone`, `.next/static`, and `public`,
|
|
and runs the generated `server.js` with Node.js 22 on port 3000. CI smoke-tests
|
|
the English and Persian routes, a built static asset, and `/_next/image` with
|
|
WebP negotiation before deployment.
|