Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# SPDX-FileCopyrightText: 2026 William Jin <AuraMindNest@outlook.com>
#
# SPDX-License-Identifier: BSL-1.0
#
# Copy to repo root as .env on the deploy server (e.g. /opt/cppa-weblate-plugin/.env).
# Used by docker/docker-compose.cd.yml (env_file + compose interpolation) and deploy.yml.
#
# Weblate Docker environment variables:
# https://docs.weblate.org/en/latest/admin/install/docker.html#docker-environment
#
# One-time host Postgres (as superuser; match POSTGRES_USER / POSTGRES_DB below):
# CREATE USER weblate_app WITH PASSWORD 'same-as-POSTGRES_PASSWORD-below';
# CREATE DATABASE weblate_db OWNER weblate_app;
# Ensure pg_hba.conf allows Docker bridge hosts to connect as that user.

# ---------------------------------------------------------------------------
# Compose-only (not passed into Weblate; used by docker-compose.cd.yml)
# ---------------------------------------------------------------------------

# Host port bound to 127.0.0.1; nginx proxies HTTPS /weblate/ to this port.
WEBLATE_PORT=9103

# ---------------------------------------------------------------------------
# Required secrets (compose fails if unset)
# ---------------------------------------------------------------------------

POSTGRES_PASSWORD=replace-with-strong-password
WEBLATE_ADMIN_PASSWORD=replace-with-strong-admin-password

# ---------------------------------------------------------------------------
# Weblate setup
# ---------------------------------------------------------------------------

WEBLATE_DEBUG=0
WEBLATE_LOGLEVEL=INFO
WEBLATE_SITE_TITLE=Example Weblate
WEBLATE_ADMIN_NAME=Weblate Admin
WEBLATE_ADMIN_EMAIL=admin@example.com
# Required. Hostname users see (no scheme); match your public URL host.
WEBLATE_SITE_DOMAIN=weblate.example.com
WEBLATE_SERVER_EMAIL=noreply@example.com
WEBLATE_DEFAULT_FROM_EMAIL=noreply@example.com
WEBLATE_MIN_PASSWORD_SCORE=0
WEBLATE_ALLOWED_HOSTS=weblate.example.com
WEBLATE_REGISTRATION_OPEN=0
WEBLATE_TIME_ZONE=UTC

# Subpath when nginx serves Weblate at https://<host>/weblate/ (see WEBLATE_URL_PREFIX).
WEBLATE_URL_PREFIX=/weblate

# WEBLATE_DATA_DIR=/app/data

# ---------------------------------------------------------------------------
# SSL / reverse proxy (required when TLS terminates at nginx)
# ---------------------------------------------------------------------------

WEBLATE_ENABLE_HTTPS=1
WEBLATE_IP_PROXY_HEADER=HTTP_X_FORWARDED_FOR
WEBLATE_SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https

# Optional: only if you see CSRF errors (not listed in official Docker env index).
# WEBLATE_CSRF_TRUSTED_ORIGINS=https://weblate.example.com

# ---------------------------------------------------------------------------
# Access control
# ---------------------------------------------------------------------------

# WEBLATE_REQUIRE_LOGIN=1

# ---------------------------------------------------------------------------
# LDAP auth (optional)
# ---------------------------------------------------------------------------

# WEBLATE_AUTH_LDAP_SERVER_URI=ldap://ldap.example.org
# WEBLATE_AUTH_LDAP_USER_DN_TEMPLATE=uid=%(user)s,ou=People,dc=example,dc=net
# WEBLATE_AUTH_LDAP_USER_ATTR_MAP=first_name:name,email:mail

# ---------------------------------------------------------------------------
# PostgreSQL (host; POSTGRES_HOST also set in docker-compose.cd.yml)
# ---------------------------------------------------------------------------

POSTGRES_USER=weblate_app
# Canonical name per Weblate Docker docs (POSTGRES_DATABASE is an alias some images accept).
POSTGRES_DB=weblate_db
POSTGRES_DATABASE=weblate_db
POSTGRES_HOST=host.docker.internal
POSTGRES_PORT=5432

# Optional: POSTGRES_PASSWORD_FILE=/run/secrets/db_password

# ---------------------------------------------------------------------------
# Redis (shared boost-data-collector stack; host/port in docker-compose.cd.yml)
# ---------------------------------------------------------------------------

# Logical DB 1 avoids clashing with other apps on the same Redis (default for Weblate is 1).
REDIS_DB=1

# Override only if you change the external network or service name:
# REDIS_HOST=redis
# REDIS_PORT=6379

# ---------------------------------------------------------------------------
# Mail server
# ---------------------------------------------------------------------------

WEBLATE_EMAIL_HOST=smtp.example.com
WEBLATE_EMAIL_PORT=587
WEBLATE_EMAIL_HOST_USER=
WEBLATE_EMAIL_HOST_PASSWORD=
WEBLATE_EMAIL_USE_TLS=1
WEBLATE_EMAIL_USE_SSL=0
# WEBLATE_EMAIL_BACKEND=django.core.mail.backends.dummy.EmailBackend

# ---------------------------------------------------------------------------
# GitHub (VCS push / PR; must use WEBLATE_ prefix per Weblate docs)
# ---------------------------------------------------------------------------

WEBLATE_GITHUB_HOST=api.github.com
WEBLATE_GITHUB_USERNAME=
WEBLATE_GITHUB_TOKEN=

# WEBLATE_GITLAB_USERNAME=
# WEBLATE_GITLAB_HOST=
# WEBLATE_GITLAB_TOKEN=

# ---------------------------------------------------------------------------
# Social auth (optional)
# ---------------------------------------------------------------------------

# WEBLATE_SOCIAL_AUTH_GITHUB_KEY=
# WEBLATE_SOCIAL_AUTH_GITHUB_SECRET=
# WEBLATE_SOCIAL_AUTH_GOOGLE_OAUTH2_KEY=
# WEBLATE_SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET=

# ---------------------------------------------------------------------------
# Monitoring / analytics (optional)
# ---------------------------------------------------------------------------

# SENTRY_DSN=
# SENTRY_ENVIRONMENT=
# WEBLATE_GOOGLE_ANALYTICS_ID=
# WEBLATE_MATOMO_SITE_ID=
# WEBLATE_MATOMO_URL=

# ---------------------------------------------------------------------------
# Nginx inside Weblate image
# ---------------------------------------------------------------------------

CLIENT_MAX_BODY_SIZE=1000M

# ---------------------------------------------------------------------------
# Celery (set in docker-compose.cd.yml; override if needed)
# ---------------------------------------------------------------------------

CELERY_SINGLE_PROCESS=1
33 changes: 33 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
SPDX-FileCopyrightText: 2026 William Jin <AuraMindNest@outlook.com>

SPDX-License-Identifier: BSL-1.0
-->

# `.github/`

GitHub Actions and CI/CD helpers for this repository.

## Workflows

| File | Role |
|------|------|
| [`workflows/ci.yml`](workflows/ci.yml) | Umbrella **CI** — runs on push/PR to `main` and `develop` |
| [`workflows/cd.yml`](workflows/cd.yml) | **Deploy** — after CI succeeds on `develop` (staging); no `workflow_dispatch` trigger |
| [`workflows/ci-lint.yml`](workflows/ci-lint.yml) | Lint and format (prek) |
| [`workflows/ci-test.yml`](workflows/ci-test.yml) | Unit tests and coverage |
| [`workflows/ci-package.yml`](workflows/ci-package.yml) | Build and package checks |
| [`workflows/ci-dependencies.yml`](workflows/ci-dependencies.yml) | Dependency and license audit |
| [`workflows/ci-combination-smoke.yml`](workflows/ci-combination-smoke.yml) | Integration smoke (Docker stack) |
| [`workflows/ci-combination-functional.yml`](workflows/ci-combination-functional.yml) | Integration functional tests |
| [`workflows/ci-combination-auth.yml`](workflows/ci-combination-auth.yml) | Integration auth tests |

Callable workflows (`ci-*`, `ci-combination-*`) are triggered only via `workflow_call` from `ci.yml`, not directly on push.

## Other paths

| Path | Role |
|------|------|
| [`ci/apt-install`](ci/apt-install) | Apt packages for Weblate-related CI jobs |

Deploy uses environment **staging** secrets (`SSH_HOST`, `SSH_USER`, `SSH_PRIVATE_KEY`) and [`docker/docker-compose.cd.yml`](../docker/docker-compose.cd.yml) on the server at `/opt/cppa-weblate-plugin`.
53 changes: 53 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: 2026 William Jin <AuraMindNest@outlook.com>
#
# SPDX-License-Identifier: BSL-1.0

name: CD

# workflow_run grants an elevated GITHUB_TOKEN by default. Mitigations:
# - permissions: contents: read (no write access)
# - job-level 'if' requires conclusion == 'success' AND event == 'push'
# (ignores PRs, forks, and failed/cancelled runs)
on:
workflow_run:
Comment thread
AuraMindNest marked this conversation as resolved.
workflows: [CI]
branches: [develop]
types: [completed]

permissions:
contents: read

concurrency:
group: deploy-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false

jobs:
cd:
if: >-
github.event.workflow_run.conclusion == 'success'
&& github.event.workflow_run.event == 'push'
runs-on: ubuntu-latest
timeout-minutes: 20
environment: staging
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
set -euo pipefail
cd /opt/cppa-weblate-plugin
git fetch origin develop
git checkout develop
git pull origin develop
docker compose -f docker/docker-compose.cd.yml --env-file .env build
docker compose -f docker/docker-compose.cd.yml --env-file .env up -d
for i in $(seq 1 36); do
curl -sf http://127.0.0.1:9103/weblate/healthz/ && exit 0
sleep 5
done
echo "Weblate not healthy after 180s"
docker compose -f docker/docker-compose.cd.yml --env-file .env logs weblate | tail -40
exit 1
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
#
# SPDX-License-Identifier: BSL-1.0

name: Integration auth
name: Combination auth

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_call:

permissions:
actions: write
contents: read

jobs:
integration-auth:
combination-auth:
name: Combination auth
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand All @@ -25,7 +27,6 @@ jobs:
with:
python-version: '3.12'


# astral-sh/setup-uv v8.1.0
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
with:
Expand All @@ -39,5 +40,5 @@ jobs:
# actions/upload-artifact v4.6.2
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: integration-auth-logs
name: ci-combination-auth-logs
path: /tmp/compose-logs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,28 @@
#
# SPDX-License-Identifier: BSL-1.0

name: Integration functional
name: Combination functional

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_call:
secrets:
# GitHub classic PAT with `repo` scope (repository secret, not a variable).
# Used by tests/integration/lib/gh_repo.py to create a temporary repo,
# push QuickBook fixtures, register Weblate's SSH deploy key, and delete
# the repo after the run. Enables add-or-update / BoostComponentService E2E
# in tests/integration/test_functional.py. If unset, those tests are skipped
# (smoke round-trip and other non-GitHub tests still run). Inherited from
# the caller via secrets: inherit in ci.yml.
GH_TEST_REPO_TOKEN:
required: false

permissions:
actions: write
contents: read

jobs:
integration-functional:
combination-functional:
name: Combination functional
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
Expand All @@ -32,7 +44,6 @@ jobs:

- name: Run integration functional tests
env:
# Use a repository *secret* (Settings → Secrets → Actions), not a variable.
GH_TEST_REPO_TOKEN: ${{ secrets.GH_TEST_REPO_TOKEN }}
run: bash scripts/integration-functional.sh

Expand All @@ -41,5 +52,5 @@ jobs:
# actions/upload-artifact v4.6.2
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: integration-functional-logs
name: ci-combination-functional-logs
path: /tmp/compose-logs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
#
# SPDX-License-Identifier: BSL-1.0

name: Integration smoke
name: Combination smoke

on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
workflow_call:

permissions:
actions: write
contents: read

jobs:
integration-smoke:
combination-smoke:
name: Combination smoke
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -38,5 +40,5 @@ jobs:
# actions/upload-artifact v4.6.2
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: integration-smoke-logs
name: ci-combination-smoke-logs
path: /tmp/compose-logs.txt
6 changes: 3 additions & 3 deletions .github/workflows/ci-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
name: Dependency audit

on:
push:
pull_request:
workflow_call:

permissions:
contents: read

jobs:
dep-audit:
dependency-audit:
name: Dependency audit
runs-on: ubuntu-latest
steps:
# actions/checkout v6.0.2
Expand Down
Loading