added ADR 0005 and step 13: reintroduce managed nginx+certbot as an opt-in feature for hosts without a reverse proxy (e.g. Hostsharing); partially supersedes ADR 0004; updated plans, documentation, and migration guides
This commit is contained in:
@@ -162,3 +162,4 @@ All major decisions are in `docs/adrs/`. Run `adr-status` (after `source .envrc`
|
|||||||
- **Gradle**: 8.14.5 (ADR 0002)
|
- **Gradle**: 8.14.5 (ADR 0002)
|
||||||
- **Spring Boot**: 4.0.6 (ADR 0003)
|
- **Spring Boot**: 4.0.6 (ADR 0003)
|
||||||
- **Rewrite architecture**: JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx — systemd unit behind the host's reverse proxy (ADR 0004)
|
- **Rewrite architecture**: JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx — systemd unit behind the host's reverse proxy (ADR 0004)
|
||||||
|
- **Managed nginx/TLS**: revises ADR 0004 — an opt-in nginx+certbot container for hosts without a reverse proxy (e.g. Hostsharing), planned as `docs/plan/13-nginx-tls.md` (ADR 0005)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
- proposed: 2026-07-07
|
- proposed: 2026-07-07
|
||||||
- accepted: 2026-07-07
|
- accepted: 2026-07-07
|
||||||
- rejected: -
|
- rejected: -
|
||||||
- superseded: -
|
- superseded: partially on 2026-07-07 — the "no managed nginx/TLS" part is revised by ADR 0005 (opt-in managed nginx/TLS container for hosts without a reverse proxy)
|
||||||
|
|
||||||
**Decision [accepted]:** JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx/TLS — deployment via systemd user unit behind the host's reverse proxy.
|
**Decision [accepted, nginx part revised by ADR 0005]:** JSON-file persistence behind a repository interface, server-rendered UI with JSON polling, no managed nginx/TLS by default — deployment via systemd user unit behind the host's reverse proxy.
|
||||||
|
|
||||||
## Context and Problem Statement
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# Optional Managed nginx/TLS Container
|
||||||
|
|
||||||
|
**Status:**
|
||||||
|
- proposed: 2026-07-07
|
||||||
|
- accepted: 2026-07-07
|
||||||
|
- rejected: -
|
||||||
|
- superseded: -
|
||||||
|
|
||||||
|
**Decision [accepted]:** GitTally optionally manages an nginx+certbot Docker container for hosts without a usable reverse proxy — revises the "no managed nginx" part of ADR 0004; deployment behind an existing reverse proxy stays the default.
|
||||||
|
|
||||||
|
## Context and Problem Statement
|
||||||
|
|
||||||
|
ADR 0004 dropped the legacy nginx/Let's Encrypt container management and documented deployment behind an existing reverse proxy instead.
|
||||||
|
That decision was carried over from the rewrite plan without validating it against the primary target environment.
|
||||||
|
|
||||||
|
### Technical Background
|
||||||
|
|
||||||
|
GitTally must run on Hostsharing managed container environments.
|
||||||
|
These hosts provide Docker but no root access and no host web server that GitTally could sit behind.
|
||||||
|
Without the managed nginx container, GitTally cannot be served over HTTPS there at all.
|
||||||
|
The legacy script already solved this: it wrote an nginx config, ran an nginx Docker container, and obtained/renewed Let's Encrypt certificates via a certbot container in webroot mode.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
* Keep ADR 0004 as is (host reverse proxy only)
|
||||||
|
* Re-add the legacy managed nginx+certbot container as an opt-in feature
|
||||||
|
* External tooling (user-maintained compose stack next to GitTally)
|
||||||
|
|
||||||
|
### Host reverse proxy only
|
||||||
|
|
||||||
|
#### Advantages
|
||||||
|
|
||||||
|
- No container lifecycle or certificate code in GitTally.
|
||||||
|
|
||||||
|
#### Disadvantages
|
||||||
|
|
||||||
|
- Unusable on Hostsharing container hosts — the primary deployment target.
|
||||||
|
|
||||||
|
### Opt-in managed nginx+certbot container
|
||||||
|
|
||||||
|
GitTally starts and supervises a labelled nginx container and handles certificate issuance/renewal via certbot, only when explicitly enabled in the config.
|
||||||
|
|
||||||
|
#### Advantages
|
||||||
|
|
||||||
|
- Works on hosts that provide only Docker; HTTPS without root or a host web server.
|
||||||
|
- The behavior is proven — it is a port of the working legacy subsystem.
|
||||||
|
- Opt-in: hosts with a reverse proxy keep the simple ADR 0004 setup.
|
||||||
|
|
||||||
|
#### Disadvantages
|
||||||
|
|
||||||
|
- Re-adds container lifecycle and certificate renewal complexity to GitTally.
|
||||||
|
|
||||||
|
### External compose stack
|
||||||
|
|
||||||
|
#### Advantages
|
||||||
|
|
||||||
|
- Keeps GitTally itself simple.
|
||||||
|
|
||||||
|
#### Disadvantages
|
||||||
|
|
||||||
|
- Pushes nginx config templating, cert bootstrap ordering, and renewal onto every operator; exactly the manual work the legacy script automated.
|
||||||
|
|
||||||
|
## Decision Outcome
|
||||||
|
|
||||||
|
Re-add the managed nginx+certbot container as an opt-in feature (`docs/plan/13-nginx-tls.md`).
|
||||||
|
This partially supersedes ADR 0004: its persistence and UI decisions stay in force; "no managed nginx/TLS" becomes "no managed nginx/TLS by default".
|
||||||
|
The reverse-proxy deployment from `docs/deployment.md` remains the recommended setup where a host web server exists.
|
||||||
+2
-1
@@ -121,4 +121,5 @@ Obtain and renew the certificate with the host's existing certbot, e.g.:
|
|||||||
sudo certbot --nginx -d ci.example.org
|
sudo certbot --nginx -d ci.example.org
|
||||||
```
|
```
|
||||||
|
|
||||||
This replaces the legacy script's managed nginx/Let's Encrypt Docker container, which was intentionally not ported (see [migration-from-legacy.md](migration-from-legacy.md)).
|
This replaces the legacy script's managed nginx/Let's Encrypt Docker container for hosts that have their own web server.
|
||||||
|
For hosts without a usable reverse proxy (e.g. Hostsharing managed containers), an opt-in managed nginx/TLS container is planned (see `docs/plan/13-nginx-tls.md`, ADR 0005).
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ New keys without a legacy counterpart: `builds.maxConcurrent`, `artifacts.rootDi
|
|||||||
|
|
||||||
## Intentionally Not Ported
|
## Intentionally Not Ported
|
||||||
|
|
||||||
- Managed nginx/Let's Encrypt container (`GITTALLY_ARTIFACT_NGINX_*`, `GITTALLY_ARTIFACT_LETSENCRYPT_EMAIL`) — use the host's reverse proxy, see [deployment.md](deployment.md).
|
- Managed nginx/Let's Encrypt container (`GITTALLY_ARTIFACT_NGINX_*`, `GITTALLY_ARTIFACT_LETSENCRYPT_EMAIL`) — not ported yet, but planned as an opt-in feature for hosts without a reverse proxy (see `docs/plan/13-nginx-tls.md`).
|
||||||
|
Until then, use the host's reverse proxy, see [deployment.md](deployment.md).
|
||||||
- Self-install and self-update (`--install`, `--pull`, `GITTALLY_INSTALL_DIR`) — replaced by jar deployment plus `init --systemd`.
|
- Self-install and self-update (`--install`, `--pull`, `GITTALLY_INSTALL_DIR`) — replaced by jar deployment plus `init --systemd`.
|
||||||
- `GITTALLY_BUILD_DOCKER_PREFLIGHT_COMMAND` and `GITTALLY_BUILD_DOCKER_JAVA_TOOL_OPTIONS` — hsadmin-ng-specific; use `branches.<name>.docker.env` if needed.
|
- `GITTALLY_BUILD_DOCKER_PREFLIGHT_COMMAND` and `GITTALLY_BUILD_DOCKER_JAVA_TOOL_OPTIONS` — hsadmin-ng-specific; use `branches.<name>.docker.env` if needed.
|
||||||
- `HSADMIN_NG_*` environment-variable fallbacks.
|
- `HSADMIN_NG_*` environment-variable fallbacks.
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ No status changes observable during a build (control loop):
|
|||||||
## Not Ported (decided)
|
## Not Ported (decided)
|
||||||
|
|
||||||
- nginx + certbot/Let's Encrypt container management — replaced by deployment documentation (step 12).
|
- nginx + certbot/Let's Encrypt container management — replaced by deployment documentation (step 12).
|
||||||
|
Revised by ADR 0005: it IS needed for Hostsharing container hosts and returns as an opt-in feature (step 13).
|
||||||
- Self-install (`--install`), self-update script generation — replaced by jar deployment plus systemd docs (step 12).
|
- Self-install (`--install`), self-update script generation — replaced by jar deployment plus systemd docs (step 12).
|
||||||
- Legacy `HSADMIN_NG_*` environment fallbacks and env-file config — replaced by YAML config (done).
|
- Legacy `HSADMIN_NG_*` environment fallbacks and env-file config — replaced by YAML config (done).
|
||||||
- Regex-based in-place HTML patching — replaced by server-rendered pages/JSON endpoints.
|
- Regex-based in-place HTML patching — replaced by server-rendered pages/JSON endpoints.
|
||||||
@@ -90,6 +91,6 @@ No status changes observable during a build (control loop):
|
|||||||
Verify need before porting any of these:
|
Verify need before porting any of these:
|
||||||
|
|
||||||
- `GITTALLY_BUILD_DOCKER_PREFLIGHT_COMMAND`, `GITTALLY_BUILD_DOCKER_JAVA_TOOL_OPTIONS` — highly hsadmin-ng-specific defaults.
|
- `GITTALLY_BUILD_DOCKER_PREFLIGHT_COMMAND`, `GITTALLY_BUILD_DOCKER_JAVA_TOOL_OPTIONS` — highly hsadmin-ng-specific defaults.
|
||||||
- `GITTALLY_ARTIFACT_NGINX_*`, `GITTALLY_ARTIFACT_LETSENCRYPT_EMAIL` — dropped with nginx management.
|
- `GITTALLY_ARTIFACT_NGINX_*`, `GITTALLY_ARTIFACT_LETSENCRYPT_EMAIL` — dropped with nginx management; revived as `server.nginx.*` by step 13 (ADR 0005).
|
||||||
- `GITTALLY_IMPRESSUM_URL` — keep as optional simple footer link if wanted.
|
- `GITTALLY_IMPRESSUM_URL` — keep as optional simple footer link if wanted.
|
||||||
- `GITTALLY_INSTALL_DIR` — dropped with self-install.
|
- `GITTALLY_INSTALL_DIR` — dropped with self-install.
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Step 13: Managed nginx/TLS Container
|
||||||
|
|
||||||
|
Prerequisites: steps 07, 11, 12.
|
||||||
|
Read `README.md`, `00-legacy-analysis.md`, and ADR 0005 first.
|
||||||
|
Consult `legacy/gitTally` for the functions referenced below.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Serve GitTally over HTTPS on hosts that provide Docker but no host reverse proxy (e.g. Hostsharing managed container environments).
|
||||||
|
GitTally optionally manages an nginx Docker container with Let's Encrypt certificates, ported from the legacy subsystem.
|
||||||
|
This is opt-in; the reverse-proxy deployment from step 12 stays the default (ADR 0005).
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
Port the legacy nginx subsystem (functions `configure_artifact_nginx_defaults` ~1545, `artifact_nginx_write_ssl_options` ~4051, `artifact_nginx_write_config` ~4077, `artifact_nginx_ports_free` ~4242, `cleanup_stale_artifact_nginx_containers` ~4274, `artifact_nginx_run_container` ~4281, `artifact_nginx_obtain_or_renew_certificate` ~4310, `start_artifact_nginx` ~4342, shutdown cleanup ~723):
|
||||||
|
|
||||||
|
- Config under `server.nginx.*`: `enabled` (default false), `serverName`, `httpPort` (8080), `httpsPort` (8443), `upstreamHost` (default: `serverName`), `containerName` (default: `gittally-nginx-<repo-name>`), `stateDir` (default: `${XDG_STATE_HOME:-~/.local/state}/gittally/nginx/<repoKey>`), `letsencryptEmail`.
|
||||||
|
Update all three config places (`GitTallyConfig`, `init` templates, `docs/configuration.md`).
|
||||||
|
- When `server.publicBaseUrl` is empty and `serverName` is set, default it to `https://<serverName>/` (legacy line ~541).
|
||||||
|
- Shell out to the `docker` CLI like `DockerBuildRunner` (no SDK); label the container `org.hoennig.gittally` for stale-container cleanup.
|
||||||
|
- Lifecycle as a server-profile component (like `ServerWatcherLifecycle`/`ServerMetricsLifecycle`): start after the web server is up, stop and remove the container on shutdown.
|
||||||
|
Nothing runs in CLI mode or tests.
|
||||||
|
- Two-phase startup, ported from legacy: write an HTTP-only nginx config for the ACME webroot challenge, run the container, obtain the certificate via a certbot container (webroot mode), then rewrite the full HTTPS config and restart nginx.
|
||||||
|
If a certificate already exists in the state dir, start with the full config directly.
|
||||||
|
- All failures are non-fatal warnings; the plain HTTP server keeps running (legacy behavior).
|
||||||
|
- Improvement over legacy (fix by design, do not port): legacy renewed certificates only at process start, relying on frequent self-update restarts.
|
||||||
|
Schedule a periodic renewal check (e.g. daily) in the lifecycle component instead.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
- Unit tests for nginx config generation (init vs. full mode, upstream/port substitution, server name escaping).
|
||||||
|
- Unit tests for docker argv assembly (run, certbot, cleanup), mocking the command runner — no real Docker or ACME interaction.
|
||||||
|
- Renewal scheduling logic with a fake clock/scheduler.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- `./gradlew ktlintFormat` then `./gradlew build` is green.
|
||||||
|
- With `server.nginx.enabled: false` (default) nothing changes; no container is touched.
|
||||||
|
- Manual walkthrough on a Docker host: nginx container starts with the init config and proxies HTTP to GitTally.
|
||||||
|
Full ACME issuance needs a public DNS name; if none is available, verify the certbot argv and the full-config path against the legacy script and document that in this file.
|
||||||
|
- `docs/deployment.md` gains a section for hosts without a reverse proxy; `docs/migration-from-legacy.md` maps the `GITTALLY_ARTIFACT_NGINX_*`/`GITTALLY_ARTIFACT_LETSENCRYPT_EMAIL` variables.
|
||||||
@@ -37,6 +37,7 @@ Revisit them in an ADR if a step uncovers problems.
|
|||||||
- The web UI is server-rendered HTML plus small JavaScript polling JSON endpoints (no SPA framework).
|
- The web UI is server-rendered HTML plus small JavaScript polling JSON endpoints (no SPA framework).
|
||||||
- The watcher is a Spring-managed scheduled component, decoupled from the build executor via the result repository and events.
|
- The watcher is a Spring-managed scheduled component, decoupled from the build executor via the result repository and events.
|
||||||
- nginx/Let's Encrypt container management is NOT ported; deployment behind an existing reverse proxy is documented instead.
|
- nginx/Let's Encrypt container management is NOT ported; deployment behind an existing reverse proxy is documented instead.
|
||||||
|
Revised after step 12 (ADR 0005): an opt-in managed nginx/TLS container is required for Hostsharing container hosts — see step 13.
|
||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
@@ -64,7 +65,12 @@ Completion:
|
|||||||
- [x] `11-docker-build-runtime.md` — optional Docker build execution
|
- [x] `11-docker-build-runtime.md` — optional Docker build execution
|
||||||
- [x] `12-deployment.md` — systemd service, migration from legacy, docs
|
- [x] `12-deployment.md` — systemd service, migration from legacy, docs
|
||||||
|
|
||||||
|
Added after the initial plan (ADR 0005):
|
||||||
|
|
||||||
|
- [ ] `13-nginx-tls.md` — opt-in managed nginx/TLS container for hosts without a reverse proxy
|
||||||
|
|
||||||
Steps 01–03 are independent of each other.
|
Steps 01–03 are independent of each other.
|
||||||
Steps 04–06 depend on 01–03.
|
Steps 04–06 depend on 01–03.
|
||||||
Steps 07–09 depend on 04–06.
|
Steps 07–09 depend on 04–06.
|
||||||
Steps 11 and 12 are optional/deferrable; 10 only needs 04–06.
|
Steps 11 and 12 are optional/deferrable; 10 only needs 04–06.
|
||||||
|
Step 13 depends on 07, 11, and 12.
|
||||||
|
|||||||
Reference in New Issue
Block a user