feat: add dedicated portfolio Gitea runner
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
GITEA_INSTANCE_URL=https://git.example.com
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN=
|
||||||
|
RUNNER_DATA_PATH=./data
|
||||||
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* text=auto
|
||||||
|
*.sh text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
Dockerfile text eol=lf
|
||||||
|
.env.example text eol=lf
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.env
|
||||||
|
data/
|
||||||
|
server-state/
|
||||||
|
*.log
|
||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM node:22-alpine@sha256:c610fcdfb1d5b4740dd70c284ed3cb16bb857e0f7166196e36a5501df7a3aa32
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
bash \
|
||||||
|
ca-certificates \
|
||||||
|
coreutils \
|
||||||
|
docker-cli \
|
||||||
|
docker-cli-buildx \
|
||||||
|
docker-cli-compose \
|
||||||
|
git \
|
||||||
|
openssh-client \
|
||||||
|
tar
|
||||||
140
README.md
Normal file
140
README.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Portfolio Gitea Runner
|
||||||
|
|
||||||
|
Machine-specific Gitea Actions runner and Caddy integration for the portfolio
|
||||||
|
host. The reusable `gitea-deployment` repository remains unchanged.
|
||||||
|
|
||||||
|
The runner is intentionally repository-scoped and has access to the host Docker
|
||||||
|
daemon. Only trusted workflows may use its `portfolio-ci` label.
|
||||||
|
|
||||||
|
## Server layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
~/gitea-deployment/ Generic reusable Gitea checkout
|
||||||
|
~/caddy-deployment/ Shared Caddy deployment
|
||||||
|
~/portfolio-gitea-runner/ This repository
|
||||||
|
```
|
||||||
|
|
||||||
|
`scripts/gitea-compose.sh` combines the generic Compose file with
|
||||||
|
`gitea.caddy.yaml`. The override connects Gitea to `caddy_proxy` and puts the
|
||||||
|
generic Nginx and Certbot services behind the inactive `bundled-proxy` profile.
|
||||||
|
|
||||||
|
## Migrate the existing server checkout
|
||||||
|
|
||||||
|
Clone this repository first, then validate and activate the external override
|
||||||
|
before restoring files in the generic checkout:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~
|
||||||
|
git clone https://git.amiirkhl.ir/amiirkhl/portfolio-gitea-runner.git
|
||||||
|
cd ~/portfolio-gitea-runner
|
||||||
|
bash scripts/gitea-compose.sh config --quiet
|
||||||
|
bash scripts/gitea-compose.sh up -d --remove-orphans
|
||||||
|
curl -fsS https://git.amiirkhl.ir/api/healthz
|
||||||
|
```
|
||||||
|
|
||||||
|
After Gitea is healthy through Caddy, preserve the obsolete generated Nginx
|
||||||
|
directory outside the generic checkout and restore only the known server-local
|
||||||
|
paths:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/gitea-deployment
|
||||||
|
git status --short
|
||||||
|
git diff -- docker-compose.yml
|
||||||
|
git diff --cached
|
||||||
|
|
||||||
|
git restore --staged -- nginx/conf.d/gitea.conf 2>/dev/null || true
|
||||||
|
mkdir -p ~/portfolio-gitea-runner/server-state
|
||||||
|
if [[ -d nginx ]]; then
|
||||||
|
mv nginx ~/portfolio-gitea-runner/server-state/legacy-nginx
|
||||||
|
fi
|
||||||
|
git restore --source=HEAD --staged --worktree -- docker-compose.yml
|
||||||
|
|
||||||
|
test -z "$(git status --porcelain)"
|
||||||
|
```
|
||||||
|
|
||||||
|
The last command must succeed. From this point onward, use
|
||||||
|
`scripts/gitea-compose.sh` for this server instead of running Compose directly
|
||||||
|
inside the generic checkout.
|
||||||
|
|
||||||
|
## Install the runner
|
||||||
|
|
||||||
|
Create a repository runner token under the portfolio repository's
|
||||||
|
**Settings > Actions > Runners**, then configure the server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.amiirkhl.ir/amiirkhl/portfolio-gitea-runner.git
|
||||||
|
cd portfolio-gitea-runner
|
||||||
|
cp .env.example .env
|
||||||
|
chmod 600 .env
|
||||||
|
nano .env
|
||||||
|
bash scripts/setup-runner.sh
|
||||||
|
docker compose logs --follow runner
|
||||||
|
```
|
||||||
|
|
||||||
|
Use these values in `.env`:
|
||||||
|
|
||||||
|
```dotenv
|
||||||
|
GITEA_INSTANCE_URL=https://git.amiirkhl.ir
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN=the-repository-runner-token
|
||||||
|
RUNNER_DATA_PATH=./data
|
||||||
|
```
|
||||||
|
|
||||||
|
After `/data/.runner` is created, clear the reusable token in `.env` and
|
||||||
|
recreate the runner:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sed -i 's/^GITEA_RUNNER_REGISTRATION_TOKEN=.*/GITEA_RUNNER_REGISTRATION_TOKEN=/' .env
|
||||||
|
docker compose up -d --force-recreate runner
|
||||||
|
bash scripts/check-runner.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The `portfolio-runner-docker-config` volume retains the Buildx builder
|
||||||
|
definition between job containers. The BuildKit container retains npm and
|
||||||
|
Next.js cache mounts. Capacity is limited to one job for the shared 2-core,
|
||||||
|
4-GB host.
|
||||||
|
|
||||||
|
## Manage the Caddy-backed Gitea instance
|
||||||
|
|
||||||
|
Always invoke the generic deployment through the wrapper on this server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/gitea-compose.sh config --quiet
|
||||||
|
bash scripts/gitea-compose.sh up -d --remove-orphans
|
||||||
|
bash scripts/gitea-compose.sh ps
|
||||||
|
bash scripts/gitea-compose.sh logs --tail=100 gitea
|
||||||
|
```
|
||||||
|
|
||||||
|
Set `GITEA_DEPLOYMENT_PATH` only if the generic checkout is not located at
|
||||||
|
`~/gitea-deployment`.
|
||||||
|
|
||||||
|
## Update and back up Gitea
|
||||||
|
|
||||||
|
Pull repository changes while Gitea is still running. Before changing the Gitea
|
||||||
|
image, stop the application and back up PostgreSQL, `/data`, and the generic
|
||||||
|
deployment's `.env`. Use the wrapper for every subsequent Compose operation so
|
||||||
|
the Caddy override is always active.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/gitea-compose.sh pull gitea
|
||||||
|
bash scripts/gitea-compose.sh stop gitea
|
||||||
|
bash scripts/gitea-compose.sh up -d --remove-orphans
|
||||||
|
bash scripts/gitea-compose.sh exec -u git gitea gitea doctor check
|
||||||
|
curl -fsS https://git.amiirkhl.ir/api/healthz
|
||||||
|
```
|
||||||
|
|
||||||
|
Restoring an older image after a minor-version database migration may require
|
||||||
|
restoring the database and data backup as well.
|
||||||
|
|
||||||
|
## Inspect and troubleshoot
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/inspect-runner.sh | tee runner-inspection.log
|
||||||
|
docker run --rm \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v portfolio-runner-docker-config:/root/.docker \
|
||||||
|
portfolio-runner-job:node22 \
|
||||||
|
docker buildx inspect portfolio-builder
|
||||||
|
```
|
||||||
|
|
||||||
|
The first portfolio pipeline run creates `portfolio-builder`. Later runs must
|
||||||
|
reuse the same builder and cached npm/Next.js layers.
|
||||||
20
compose.yaml
Normal file
20
compose.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
services:
|
||||||
|
runner:
|
||||||
|
image: docker.io/gitea/runner:3.0.0@sha256:e4b1a4b99272b78f2f06ac60feefc8de176c49ab4be845960bb2956eaac1be04
|
||||||
|
container_name: gitea-runner
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
CONFIG_FILE: /config.yaml
|
||||||
|
GITEA_INSTANCE_URL: ${GITEA_INSTANCE_URL:?set GITEA_INSTANCE_URL in runner/.env}
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN: ${GITEA_RUNNER_REGISTRATION_TOKEN:-}
|
||||||
|
GITEA_RUNNER_LABELS: portfolio-ci:docker://portfolio-runner-job:node22
|
||||||
|
GITEA_RUNNER_NAME: portfolio-runner
|
||||||
|
volumes:
|
||||||
|
- ${RUNNER_DATA_PATH:-./data}:/data
|
||||||
|
- ./config.yaml:/config.yaml:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- portfolio-runner-docker-config:/runner-docker-config
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
portfolio-runner-docker-config:
|
||||||
|
name: portfolio-runner-docker-config
|
||||||
23
config.yaml
Normal file
23
config.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
runner:
|
||||||
|
file: /data/.runner
|
||||||
|
capacity: 1
|
||||||
|
timeout: 20m
|
||||||
|
shutdown_timeout: 2m
|
||||||
|
action_shallow_clone: true
|
||||||
|
tool_cache_mode: none
|
||||||
|
|
||||||
|
cache:
|
||||||
|
enabled: false
|
||||||
|
offline_mode: true
|
||||||
|
|
||||||
|
container:
|
||||||
|
network: bridge
|
||||||
|
privileged: false
|
||||||
|
options: --volume portfolio-runner-docker-config:/root/.docker
|
||||||
|
valid_volumes:
|
||||||
|
- portfolio-runner-docker-config
|
||||||
|
docker_host: unix:///var/run/docker.sock
|
||||||
|
force_pull: false
|
||||||
|
force_rebuild: false
|
||||||
|
require_docker: true
|
||||||
|
docker_timeout: 10s
|
||||||
18
gitea.caddy.yaml
Normal file
18
gitea.caddy.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
networks:
|
||||||
|
- gitea_net
|
||||||
|
- caddy_proxy
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
profiles:
|
||||||
|
- bundled-proxy
|
||||||
|
|
||||||
|
certbot:
|
||||||
|
profiles:
|
||||||
|
- bundled-proxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
caddy_proxy:
|
||||||
|
external: true
|
||||||
|
name: caddy_proxy
|
||||||
16
scripts/check-runner.sh
Executable file
16
scripts/check-runner.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
|
docker compose config --quiet
|
||||||
|
docker compose ps
|
||||||
|
docker compose exec runner test -s /data/.runner
|
||||||
|
|
||||||
|
docker run --rm \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
portfolio-runner-job:node22 \
|
||||||
|
sh -lc 'node --version && npm --version && git --version && docker version && docker buildx version && docker compose version'
|
||||||
|
|
||||||
|
echo "Runner registration, Docker access, and job-image tools are ready."
|
||||||
24
scripts/gitea-compose.sh
Executable file
24
scripts/gitea-compose.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
deployment_root="${GITEA_DEPLOYMENT_PATH:-$HOME/gitea-deployment}"
|
||||||
|
base_compose="$deployment_root/docker-compose.yml"
|
||||||
|
base_env="$deployment_root/.env"
|
||||||
|
|
||||||
|
if [[ ! -f "$base_compose" ]]; then
|
||||||
|
echo "Generic Gitea Compose file not found: $base_compose" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$base_env" ]]; then
|
||||||
|
echo "Generic Gitea environment file not found: $base_env" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec docker compose \
|
||||||
|
--project-name gitea-deployment \
|
||||||
|
--project-directory "$deployment_root" \
|
||||||
|
--env-file "$base_env" \
|
||||||
|
--file "$base_compose" \
|
||||||
|
--file "$repo_root/gitea.caddy.yaml" \
|
||||||
|
"$@"
|
||||||
30
scripts/inspect-runner.sh
Executable file
30
scripts/inspect-runner.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
|
echo "Runner image"
|
||||||
|
docker inspect --format '{{.Config.Image}}' gitea-runner 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Runner version"
|
||||||
|
docker compose exec runner gitea-runner --version 2>/dev/null || \
|
||||||
|
docker compose exec runner act_runner --version 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Runner configuration"
|
||||||
|
docker compose exec runner sh -c 'cat /config.yaml' 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Host resources"
|
||||||
|
nproc
|
||||||
|
free -h
|
||||||
|
df -h / /var/lib/docker 2>/dev/null || df -h /
|
||||||
|
|
||||||
|
echo "Docker builders"
|
||||||
|
docker run --rm \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v portfolio-runner-docker-config:/root/.docker \
|
||||||
|
portfolio-runner-job:node22 \
|
||||||
|
docker buildx ls 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Recent runner logs"
|
||||||
|
docker compose logs --tail=200 runner 2>/dev/null || true
|
||||||
18
scripts/setup-runner.sh
Executable file
18
scripts/setup-runner.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
|
if [[ ! -f .env ]]; then
|
||||||
|
cp .env.example .env
|
||||||
|
chmod 600 .env
|
||||||
|
echo "Created $repo_root/.env. Add the repository runner token, then rerun this script." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build --pull --tag portfolio-runner-job:node22 .
|
||||||
|
docker compose config --quiet
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
echo "Runner started. Follow logs with: docker compose logs --follow runner"
|
||||||
Reference in New Issue
Block a user