diff --git a/docs/plan/00-legacy-analysis.md b/docs/plan/00-legacy-analysis.md index 66d458b..5a9f1ff 100644 --- a/docs/plan/00-legacy-analysis.md +++ b/docs/plan/00-legacy-analysis.md @@ -90,7 +90,7 @@ No status changes observable during a build (control loop): Verify need before porting any of these: -- `WERKATOR_BUILD_DOCKER_PREFLIGHT_COMMAND`, `WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS` — highly hsadmin-ng-specific defaults. -- `WERKATOR_ARTIFACT_NGINX_*`, `WERKATOR_ARTIFACT_LETSENCRYPT_EMAIL` — dropped with nginx management; revived as `server.nginx.*` by step 13 (ADR 0005). -- `WERKATOR_IMPRESSUM_URL` — keep as optional simple footer link if wanted. -- `WERKATOR_INSTALL_DIR` — dropped with self-install. +- `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; revived as `server.nginx.*` by step 13 (ADR 0005). +- `GITTALLY_IMPRESSUM_URL` — keep as optional simple footer link if wanted. +- `GITTALLY_INSTALL_DIR` — dropped with self-install. diff --git a/docs/plan/13-nginx-tls.md b/docs/plan/13-nginx-tls.md index c86f7ea..4b6afb4 100644 --- a/docs/plan/13-nginx-tls.md +++ b/docs/plan/13-nginx-tls.md @@ -38,7 +38,7 @@ Port the legacy nginx subsystem (functions `configure_artifact_nginx_defaults` ~ - 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 werkator. 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 `WERKATOR_ARTIFACT_NGINX_*`/`WERKATOR_ARTIFACT_LETSENCRYPT_EMAIL` variables. +- `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. ## Result (2026-07-08) diff --git a/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md b/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md index bf39bbd..6f2a3c9 100644 --- a/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md +++ b/docs/prs/2026-08-31-PR#1-rename-gittally-to-werkator.md @@ -134,8 +134,6 @@ The rollback section names the asymmetry: the new version reads both names, the - The default `gitea.statusContext` is now `werkator` (lowercase, as a value read by a machine). It is also the label a human sees in Gitea; `Werkator` there would be a deliberate exception to the spelling rule. -- The legacy environment-variable names documented for the old bash script are now spelled `WERKATOR_*`, which is a name that never existed — the real ones were `GITTALLY_*`. - Kept, because every host they addressed has long since moved to the YAML configuration, and the converter in `tools/setup-werkator-instance` is a one-shot tool for exactly those hosts. ## Additional Changes @@ -145,6 +143,11 @@ The rollback section names the asymmetry: the new version reads both names, the - Fixed a broken web UI: five templates already referenced `/werkator.js` and `/werkator.css` while the static files were still named `gittally.*`, so every page but the release notes served neither stylesheet nor live updates. - Renamed the Kotlin types `werkatorCommand` and `werkatorMeta`, which a case-insensitive replace had left lowercase, and the constant `werkator_LABEL` to `WERKATOR_LABEL`. - Added the release-notes entry for the rename, as `unreleased`. +- Removed the legacy environment-variable conversion from `tools/setup-werkator-instance`. + The blanket rename had rewritten the old script's `GITTALLY_*` variables to a spelling that never existed on any host, so the conversion would have read nothing from a real legacy file and written an almost empty configuration — silently. + The conversion has served its purpose with the vm2176 → vm4006 migration; what remains is the setup of a new instance: the preconditions, the credential prompt, and the machine configuration written mode 600. + It also stops emitting a legacy `branches:` section, which [plan step 18](../plan/18-remove-branches-section.md) is about to reject outright. +- Restored the real `GITTALLY_*` spelling in `docs/plan/00-legacy-analysis.md` and `docs/plan/13-nginx-tls.md`, which record what the old script read. ## Follow-up PRs diff --git a/tools/setup-werkator-instance b/tools/setup-werkator-instance index e719b49..4fb8b9e 100755 --- a/tools/setup-werkator-instance +++ b/tools/setup-werkator-instance @@ -1,26 +1,25 @@ #!/usr/bin/env bash # -# Set up a fresh Werkator instance on a new Docker host by converting a legacy -# .werkator env configuration to the new YAML format. +# Set up a fresh Werkator instance on a Docker host. # -# The legacy config comes either from a file passed as the optional third -# argument, or — when omitted — from the embedded snapshot of the hs.hsadmin.ng -# .werkator on vm2176. Either way the script sources it, converts it to YAML -# (mapping the legacy env vars to YAML keys), substitutes the public hostname -# from the command line, and prompts for the Gitea secrets. +# Checks the preconditions, prompts for the Gitea credentials and writes them — +# together with the host's public URL — into the machine configuration, which is +# the layer that is never committed. Then runs `init`, which writes the commented +# templates for everything else. # -# Run this ON the new Docker host. The repository must already be cloned at the -# given repo-dir. Builds run in Docker; Werkator itself runs from the jar via -# java, so a JRE and the jar are required (see checks below). +# The secrets are written with umask 077, so the file is mode 600 at creation and +# the token is never world-readable, not even between the redirect and a chmod. # -# Usage: setup-werkator-instance [--force] [legacy-config] -# FQDN this instance is reachable at, e.g. vm2180.hostsharing.net +# Run this ON the host. The repository must already be cloned at the given +# repo-dir. Builds run in Docker; Werkator itself runs from the jar via java, so +# a JRE and the jar are required (see checks below). +# +# Usage: setup-werkator-instance [--force] +# FQDN this instance is reachable at, e.g. vm4006.hostsharing.net # (used for server.publicBaseUrl) # path to the cloned git repository to configure, # e.g. ~/hs.hsadmin.ng -# [legacy-config] optional path to a legacy .werkator file to convert; -# if omitted, the embedded vm2176 snapshot is used -# --force overwrite .werkator.yml files that already exist +# --force overwrite a machine configuration that already exists set -euo pipefail @@ -32,8 +31,8 @@ die() { echo "ERROR: $*" >&2; exit 1; } warn() { echo "WARNING: $*" >&2; } usage() { - echo "usage: setup-werkator-instance [--force] [legacy-config]" >&2 - echo "example: setup-werkator-instance vm2180.hostsharing.net ~/hs.hsadmin.ng" >&2 + echo "usage: setup-werkator-instance [--force] " >&2 + echo "example: setup-werkator-instance vm4006.hostsharing.net ~/hs.hsadmin.ng" >&2 exit 1 } @@ -48,55 +47,13 @@ for arg in "$@"; do *) positional+=("$arg") ;; esac done -case ${#positional[@]} in - 2) legacy_file="" ;; - 3) legacy_file="${positional[2]}" ;; - *) usage ;; -esac +[ ${#positional[@]} -eq 2 ] || usage hostname="${positional[0]}" REPO_DIR="${positional[1]}" -[ -z "$legacy_file" ] || [ -f "$legacy_file" ] || die "legacy config not found: $legacy_file" -# ---------------------------------------------------- legacy config source -- -# load_legacy_config populates the werkator_* variables, either by sourcing the -# file passed on the command line, or from the embedded vm2176 snapshot below. -# Secrets (git username/token) are intentionally never read here — they are -# prompted for, so a token in a legacy file is not echoed or reused implicitly. +# ------------------------------------------------------------- yaml helper -- -load_legacy_config() { - if [ -n "$legacy_file" ]; then - # shellcheck source=/dev/null - . "$legacy_file" - return - fi - # embedded, non-secret snapshot of tallyman@vm2176:~/hs.hsadmin.ng/.werkator - export WERKATOR_BUILD_COMMAND='./gradlew --console=plain --no-daemon --no-build-cache jacocoTestReport check generateDocumentation -x pitest -x dependencyCheckAnalyze' - export WERKATOR_BUILD_CLEAN_COMMAND='rm -rf build' - export WERKATOR_BUILD_ARTEFACT_DIRS='build/reports:build/doc' - export WERKATOR_BUILD_STDOUT_LOG='build.stdout.log' - export WERKATOR_BUILD_STDERR_LOG='build.stderr.log' - export WERKATOR_BUILD_DOCKER_IMAGE='hsadmin-ng-build-env:latest' - export WERKATOR_BUILD_DOCKERFILE='Jenkins/jenkins-agent/Dockerfile' - export WERKATOR_BUILD_DOCKER_CONTEXT='Jenkins/jenkins-agent' - export WERKATOR_BUILD_DOCKER_NETWORK='host' - export WERKATOR_BUILD_DOCKER_ENV='TESTCONTAINERS_RYUK_DISABLED=true' - # legacy hsadmin-ng-specific JAVA_TOOL_OPTIONS — carried into docker.env so the - # Testcontainers builds keep finding the docker socket (see migration doc). - export WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS='-Ddocker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy -Dtestcontainers.docker.socket.override=/var/run/docker.sock' - export WERKATOR_ARTIFACT_SERVER_PORT='18080' - export WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS='0.0.0.0' - export WERKATOR_ARTIFACT_BUILD_RETENTION_PER_BRANCH='3' - export WERKATOR_IMPRESSUM_URL='https://michael.hoennig.de/imprint.html' - export WERKATOR_GITEA_BASE_URL='https://dev.hostsharing.net' - export WERKATOR_GITEA_OWNER='hostsharing' - export WERKATOR_GITEA_REPO='hs.hsadmin.ng' - export WERKATOR_GITEA_STATUS_CONTEXT='werkator' -} - -# ----------------------------------------------------------------- helpers -- - -# quote a scalar for double-quoted YAML yaml_quote() { local s=${1//\\/\\\\} s=${s//\"/\\\"} @@ -117,154 +74,41 @@ if ! command -v java >/dev/null 2>&1; then warn "no java in PATH — install a JRE (e.g. Temurin 21 into ~/opt) before running init/server" fi -# ----------------------------------------------------------- prompt secrets -- - -# suggest the account from the legacy config's git username, if it sets one -suggested_account=$( set +u; load_legacy_config >/dev/null 2>&1; printf '%s' "${WERKATOR_GITEA_GIT_USERNAME:-jenkins-ci}" ) - -echo "== Gitea credentials for this instance (used for git HTTPS auth + status API)" -read -r -p " Gitea git account [$suggested_account]: " git_account -git_account=${git_account:-$suggested_account} -read -r -s -p " Gitea API token: " git_token; echo -[ -n "$git_token" ] || die "the Gitea token must not be empty" - -# -------------------------------------------------------------- convert cfg -- - -project_yml="$REPO_DIR/.werkator.yml" machine_yml="$REPO_DIR/.git/werkator/.werkator.yml" -public_base_url="https://$hostname/" - if ! $force; then - for f in "$project_yml" "$machine_yml"; do - [ -e "$f" ] && die "$f already exists — re-run with --force to overwrite" - done + [ -e "$machine_yml" ] && die "$machine_yml already exists — re-run with --force to overwrite" fi -config_source=${legacy_file:-"embedded vm2176 snapshot"} -echo "== writing $project_yml (public host: $hostname, source: $config_source)" +# ----------------------------------------------------------- prompt secrets -- -( - set +u - load_legacy_config - - # warn about legacy keys that have no counterpart in the new config - for var in \ - WERKATOR_ARTIFACT_NGINX_SERVER_NAME WERKATOR_ARTIFACT_NGINX_HTTP_PORT \ - WERKATOR_ARTIFACT_NGINX_HTTPS_PORT WERKATOR_ARTIFACT_LETSENCRYPT_EMAIL \ - WERKATOR_ARTIFACT_AUTH_MODE WERKATOR_BUILD_DOCKER_PREFLIGHT_COMMAND \ - WERKATOR_INSTALL_DIR - do - [ -n "${!var}" ] && warn "$var is set but has no counterpart in the new config — skipped" - done - - # retention: strip a legacy age suffix (h/d), only the build count is supported - retention="$WERKATOR_ARTIFACT_BUILD_RETENTION_PER_BRANCH" - if [ -n "$retention" ] && [[ ! "$retention" =~ ^[0-9]+$ ]]; then - warn "retention '$retention' has an age suffix — only the build count is supported, using '${retention%%[!0-9]*}'" - retention=${retention%%[!0-9]*} - fi - - emit() { printf '%s\n' "$1" >>"$project_yml"; } - - : >"$project_yml" - emit "# Werkator configuration, converted from $config_source" - emit "# by tools/setup-werkator-instance; public host set to $hostname." - emit "# Omitted keys fall back to the application defaults — see docs/configuration.md." - emit "" - - emit "server:" - emit " publicBaseUrl: $(yaml_quote "$public_base_url")" - [ -n "$WERKATOR_ARTIFACT_SERVER_PORT" ] && emit " port: $WERKATOR_ARTIFACT_SERVER_PORT" - [ -n "$WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS" ] && emit " bindAddress: $WERKATOR_ARTIFACT_SERVER_BIND_ADDRESS" - [ -n "$WERKATOR_IMPRESSUM_URL" ] && emit " impressumUrl: $(yaml_quote "$WERKATOR_IMPRESSUM_URL")" - emit "" - - emit "gitea:" - [ -n "$WERKATOR_GITEA_BASE_URL" ] && emit " baseUrl: $(yaml_quote "$WERKATOR_GITEA_BASE_URL")" - [ -n "$WERKATOR_GITEA_OWNER" ] && emit " owner: $(yaml_quote "$WERKATOR_GITEA_OWNER")" - [ -n "$WERKATOR_GITEA_REPO" ] && emit " repo: $(yaml_quote "$WERKATOR_GITEA_REPO")" - [ -n "$WERKATOR_GITEA_STATUS_CONTEXT" ] && emit " statusContext: $(yaml_quote "$WERKATOR_GITEA_STATUS_CONTEXT")" - emit "" - - [ -n "$retention" ] && { emit "artifacts:"; emit " retentionPerBranch: $retention"; emit ""; } - - [ -n "$WERKATOR_NEW_BRANCH_COMMIT_MAX_AGE" ] && { emit "watcher:"; emit " newBranchMaxAge: $WERKATOR_NEW_BRANCH_COMMIT_MAX_AGE"; emit ""; } - - emit "branches:" - emit " default:" - [ -n "$WERKATOR_BUILD_CLEAN_COMMAND" ] && emit " cleanCommand: $(yaml_quote "$WERKATOR_BUILD_CLEAN_COMMAND")" - [ -n "$WERKATOR_BUILD_COMMAND" ] && emit " buildCommand: $(yaml_quote "$WERKATOR_BUILD_COMMAND")" - if [ -n "$WERKATOR_BUILD_ARTEFACT_DIRS" ]; then - emit " artifactDirs:" - # legacy separators: ';' (documented) and ':' (as used on vm2176) - IFS=';:' read -r -a dirs <<<"$WERKATOR_BUILD_ARTEFACT_DIRS" - for dir in "${dirs[@]}"; do - [ -n "$dir" ] && emit " - $(yaml_quote "$dir")" - done - fi - [ -n "$WERKATOR_BUILD_STDOUT_LOG" ] && emit " stdoutLog: $(yaml_quote "$WERKATOR_BUILD_STDOUT_LOG")" - [ -n "$WERKATOR_BUILD_STDERR_LOG" ] && emit " stderrLog: $(yaml_quote "$WERKATOR_BUILD_STDERR_LOG")" - - if [ -n "$WERKATOR_BUILD_DOCKER_IMAGE" ] || [ -n "$WERKATOR_BUILD_DOCKERFILE" ]; then - emit " docker:" - emit " enabled: true" - [ -n "$WERKATOR_BUILD_DOCKER_IMAGE" ] && emit " image: $(yaml_quote "$WERKATOR_BUILD_DOCKER_IMAGE")" - [ -n "$WERKATOR_BUILD_DOCKERFILE" ] && emit " dockerfile: $(yaml_quote "$WERKATOR_BUILD_DOCKERFILE")" - [ -n "$WERKATOR_BUILD_DOCKER_CONTEXT" ] && emit " context: $(yaml_quote "$WERKATOR_BUILD_DOCKER_CONTEXT")" - # legacy default network was host; new default is Docker's default - emit " network: $(yaml_quote "${WERKATOR_BUILD_DOCKER_NETWORK:-host}")" - if [ -n "$WERKATOR_BUILD_DOCKER_ENV" ] || [ -n "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS" ]; then - emit " env:" - for assignment in $WERKATOR_BUILD_DOCKER_ENV; do - emit " ${assignment%%=*}: $(yaml_quote "${assignment#*=}")" - done - # hsadmin-ng-specific JAVA_TOOL_OPTIONS carried into docker.env so the - # Testcontainers builds keep finding the docker socket (see migration doc) - [ -n "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS" ] && \ - emit " JAVA_TOOL_OPTIONS: $(yaml_quote "$WERKATOR_BUILD_DOCKER_JAVA_TOOL_OPTIONS")" - fi - fi - - # legacy auto-build: a branch list plus global times -> per-branch autoBuild - if [ -n "$WERKATOR_AUTO_BUILD_BRANCHES" ]; then - times_yaml="" - for t in ${WERKATOR_AUTO_BUILD_TIMES//,/ }; do - times_yaml+="${times_yaml:+, }$(yaml_quote "$t")" - done - for branch in ${WERKATOR_AUTO_BUILD_BRANCHES//,/ }; do - emit " $(yaml_quote "$branch"):" - emit " autoBuild:" - emit " enabled: true" - [ -n "$times_yaml" ] && emit " times: [$times_yaml]" - done - fi -) +echo "== Gitea credentials for this instance (used for git HTTPS auth + status API)" +read -r -p " Gitea git account [jenkins-ci]: " git_account +git_account=${git_account:-jenkins-ci} +read -r -s -p " Gitea API token: " git_token; echo +[ -n "$git_token" ] || die "the Gitea token must not be empty" # -------------------------------------------------------- machine + secrets -- echo "== writing $machine_yml (secrets, mode 600)" mkdir -p "$(dirname "$machine_yml")" -# the umask in the subshell makes the file mode 600 at creation, so the token is -# never world-readable — not even between the redirect and a follow-up chmod; # an existing file is removed first, because the redirect would keep its mode rm -f "$machine_yml" ( umask 077 { echo "# Machine-specific overrides and secrets. Keys here win over .werkator.yml." + echo "server:" + echo " publicBaseUrl: $(yaml_quote "https://$hostname/")" echo "git:" echo " account: $(yaml_quote "$git_account")" echo " token: $(yaml_quote "$git_token")" } >"$machine_yml" ) -echo "== converted project config:" -sed 's/^/ /' "$project_yml" - # ------------------------------------------------------------------- finish -- if $have_jar && $have_java; then - echo "== running init (keeps the config files just written)" + echo "== running init (keeps the machine config just written)" ( cd "$REPO_DIR" && java -jar "$JAR_PATH" init ) fi @@ -272,6 +116,9 @@ cat <