Rename GitTally to Werkator

`gitTally` is the name of another product in the git space, so the
rename is a precaution; nothing about what the build system does changes.

The name follows one rule: `Werkator` where it is prose, capitalized
where it is a Kotlin type and its file, lowercase everywhere a machine
reads it — the command, packages, paths, configuration keys and values,
the Gitea check context. Environment variables keep their convention and
are uppercase throughout.

Every configuration file is still found under its pre-rename name
(`ConfigFiles`): `.gittally.yml` at the repository root, in a build
worktree and as committed on a branch, `.git/gittally/.gittally.yml` for
the machine layer. The current name wins where both exist, and the old
file is then ignored rather than merged — two files side by side are a
half-done rename, not a layering. Without the fallback an installation
that updated without renaming would not fail: a configuration that is
not found leaves every setting at its default, so it would come up
looking healthy while having forgotten its credentials and its builds.

`docs/werkator-migrationsplan.md` lists what the fallback does not
cover and has to be moved by hand — above all the state directory
`.git/werkator/`, which holds the build history, the control token and
the worktrees, and has no fallback of its own.

`docs/migration-from-legacy.md` is deleted with this: it mapped the
legacy script's environment variables, and every host it addressed has
long since moved to the YAML configuration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-30 19:39:55 +02:00
co-authored by Claude Opus 5
parent 7f550689dd
commit 35f06ec1ec
156 changed files with 604 additions and 401 deletions
@@ -1,16 +1,16 @@
#!/usr/bin/env bash
#
# Set up a fresh werkator instance on a new Docker host by converting a legacy
# Set up a fresh Werkator instance on a new Docker host by converting a legacy
# .werkator env configuration to the new YAML format.
#
# 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 per docs/migration-from-legacy.md), substitutes the public hostname
# (mapping the legacy env vars to YAML keys), substitutes the public hostname
# from the command line, and prompts for the Gitea secrets.
#
# 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
# 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] <public-hostname> <repo-dir> [legacy-config]
@@ -26,7 +26,7 @@ set -euo pipefail
# ---------------------------------------------------------------- settings --
JAR_PATH="$HOME/bin/werkator.jar" # where the werkator jar is expected
JAR_PATH="$HOME/bin/werkator.jar" # where the Werkator jar is expected
die() { echo "ERROR: $*" >&2; exit 1; }
warn() { echo "WARNING: $*" >&2; }
@@ -71,27 +71,27 @@ load_legacy_config() {
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'
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'
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 --
@@ -120,7 +120,7 @@ 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}" )
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
@@ -149,16 +149,16 @@ echo "== writing $project_yml (public host: $hostname, source: $config_source)"
# 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
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 (see docs/migration-from-legacy.md)"
[ -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"
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]*}
@@ -167,71 +167,71 @@ echo "== writing $project_yml (public host: $hostname, source: $config_source)"
emit() { printf '%s\n' "$1" >>"$project_yml"; }
: >"$project_yml"
emit "# werkator configuration, converted from $config_source"
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")"
[ -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")"
[ -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 ""; }
[ -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
[ -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"
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")"
[ -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
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")"
[ -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 " 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
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")"
[ -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
if [ -n "$WERKATOR_AUTO_BUILD_BRANCHES" ]; then
times_yaml=""
for t in ${werkator_AUTO_BUILD_TIMES//,/ }; do
for t in ${WERKATOR_AUTO_BUILD_TIMES//,/ }; do
times_yaml+="${times_yaml:+, }$(yaml_quote "$t")"
done
for branch in ${werkator_AUTO_BUILD_BRANCHES//,/ }; do
for branch in ${WERKATOR_AUTO_BUILD_BRANCHES//,/ }; do
emit " $(yaml_quote "$branch"):"
emit " autoBuild:"
emit " enabled: true"
@@ -270,7 +270,7 @@ fi
cat <<EOF
Done. werkator config for $hostname is in place.
Done. Werkator config for $hostname is in place.
Verify and start on this host:
@@ -279,7 +279,7 @@ Verify and start on this host:
java -jar $JAR_PATH config:print --full --show-secrets # ... including git.token in clear text
java -jar $JAR_PATH server # or: java -jar $JAR_PATH init --systemd
Note: werkator binds to $hostname:${werkator_ARTIFACT_SERVER_PORT:-18080} over plain HTTP.
Note: Werkator binds to $hostname:${WERKATOR_ARTIFACT_SERVER_PORT:-18080} over plain HTTP.
publicBaseUrl is https://$hostname/ — terminate TLS in front of it with the host's
reverse proxy (managed nginx/TLS is ADR 0005 / docs/plan/13-nginx-tls.md, not yet built).
EOF