From aa8a2e927887531dd94b3b1a258b2ab7d9a7bd8b Mon Sep 17 00:00:00 2001 From: mptyl Date: Sat, 15 Aug 2026 21:56:40 +0200 Subject: [PATCH] refactor(cli): rename operator command to tht --- .github/workflows/deployment.yml | 8 +- .gitignore | 2 +- .../{thothctl.Dockerfile => tht.Dockerfile} | 16 +- scripts/{build-thothctl.sh => build-tht.sh} | 6 +- scripts/test-server-operator-permissions.sh | 26 +-- ...contract.sh => test-tht-build-contract.sh} | 24 +-- scripts/test-verify-workspace-install-docs.sh | 6 +- scripts/test-windows-clone-contract.ps1 | 16 +- ...tl-update-smoke.sh => tht-update-smoke.sh} | 2 +- scripts/unified-deployment-smoke.sh | 74 ++++---- scripts/verify-workspace-install-docs.sh | 40 ++--- .../cmd/thothctl => tht/cmd/tht}/main.go | 52 +++--- .../cmd/thothctl => tht/cmd/tht}/main_test.go | 162 +++++++++++------- tools/{thothctl => tht}/go.mod | 2 +- tools/{thothctl => tht}/go.sum | 0 .../internal/compose/runner.go | 0 .../internal/compose/runner_test.go | 0 .../internal/config/installation.go | 8 +- .../internal/config/installation_test.go | 6 +- .../internal/output/sanitize.go | 2 +- .../internal/output/sanitize_test.go | 2 +- .../{thothctl => tht}/internal/pi/commands.go | 10 +- .../internal/pi/commands_test.go | 10 +- .../internal/pi/durable_unix.go | 0 .../internal/pi/durable_windows.go | 0 .../internal/pi/recovery_error.go | 0 .../{thothctl => tht}/internal/pi/restart.go | 0 .../internal/pi/restart_test.go | 4 +- tools/{thothctl => tht}/internal/pi/state.go | 0 .../internal/pi/state_test.go | 6 +- tools/{thothctl => tht}/internal/pi/update.go | 4 +- .../internal/pi/update_test.go | 38 ++-- .../internal/safeio/files.go | 0 .../internal/safeio/files_test.go | 8 +- .../internal/safeio/files_unix.go | 2 +- .../internal/safeio/files_unix_test.go | 2 +- .../internal/safeio/files_windows.go | 2 +- .../internal/safeio/files_windows_test.go | 0 .../internal/safeio/linkcount_unix.go | 0 .../internal/safeio/linkcount_windows.go | 0 .../internal/serverops/operations.go | 4 +- .../internal/serverops/operations_test.go | 6 +- .../internal/testsupport/symlink.go | 2 +- .../testsupport/symlink_nonwindows.go | 0 .../internal/testsupport/symlink_test.go | 0 .../internal/testsupport/symlink_windows.go | 0 .../testsupport/symlink_windows_test.go | 0 .../internal/workspaceops/operations.go | 6 +- .../internal/workspaceops/operations_test.go | 6 +- 49 files changed, 303 insertions(+), 261 deletions(-) rename docker/{thothctl.Dockerfile => tht.Dockerfile} (54%) rename scripts/{build-thothctl.sh => build-tht.sh} (55%) rename scripts/{test-thothctl-build-contract.sh => test-tht-build-contract.sh} (58%) rename scripts/{thothctl-update-smoke.sh => tht-update-smoke.sh} (67%) rename tools/{thothctl/cmd/thothctl => tht/cmd/tht}/main.go (93%) rename tools/{thothctl/cmd/thothctl => tht/cmd/tht}/main_test.go (89%) rename tools/{thothctl => tht}/go.mod (89%) rename tools/{thothctl => tht}/go.sum (100%) rename tools/{thothctl => tht}/internal/compose/runner.go (100%) rename tools/{thothctl => tht}/internal/compose/runner_test.go (100%) rename tools/{thothctl => tht}/internal/config/installation.go (98%) rename tools/{thothctl => tht}/internal/config/installation_test.go (98%) rename tools/{thothctl => tht}/internal/output/sanitize.go (98%) rename tools/{thothctl => tht}/internal/output/sanitize_test.go (98%) rename tools/{thothctl => tht}/internal/pi/commands.go (98%) rename tools/{thothctl => tht}/internal/pi/commands_test.go (96%) rename tools/{thothctl => tht}/internal/pi/durable_unix.go (100%) rename tools/{thothctl => tht}/internal/pi/durable_windows.go (100%) rename tools/{thothctl => tht}/internal/pi/recovery_error.go (100%) rename tools/{thothctl => tht}/internal/pi/restart.go (100%) rename tools/{thothctl => tht}/internal/pi/restart_test.go (99%) rename tools/{thothctl => tht}/internal/pi/state.go (100%) rename tools/{thothctl => tht}/internal/pi/state_test.go (90%) rename tools/{thothctl => tht}/internal/pi/update.go (99%) rename tools/{thothctl => tht}/internal/pi/update_test.go (97%) rename tools/{thothctl => tht}/internal/safeio/files.go (100%) rename tools/{thothctl => tht}/internal/safeio/files_test.go (91%) rename tools/{thothctl => tht}/internal/safeio/files_unix.go (96%) rename tools/{thothctl => tht}/internal/safeio/files_unix_test.go (91%) rename tools/{thothctl => tht}/internal/safeio/files_windows.go (98%) rename tools/{thothctl => tht}/internal/safeio/files_windows_test.go (100%) rename tools/{thothctl => tht}/internal/safeio/linkcount_unix.go (100%) rename tools/{thothctl => tht}/internal/safeio/linkcount_windows.go (100%) rename tools/{thothctl => tht}/internal/serverops/operations.go (98%) rename tools/{thothctl => tht}/internal/serverops/operations_test.go (98%) rename tools/{thothctl => tht}/internal/testsupport/symlink.go (85%) rename tools/{thothctl => tht}/internal/testsupport/symlink_nonwindows.go (100%) rename tools/{thothctl => tht}/internal/testsupport/symlink_test.go (100%) rename tools/{thothctl => tht}/internal/testsupport/symlink_windows.go (100%) rename tools/{thothctl => tht}/internal/testsupport/symlink_windows_test.go (100%) rename tools/{thothctl => tht}/internal/workspaceops/operations.go (99%) rename tools/{thothctl => tht}/internal/workspaceops/operations_test.go (98%) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 53b341c1..80abaf9e 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -85,8 +85,8 @@ jobs: persist-credentials: false - name: Run unified deployment smoke run: timeout --signal=TERM --kill-after=45s 32m bash scripts/unified-deployment-smoke.sh - - name: Run thothctl update smoke - run: timeout --signal=TERM --kill-after=45s 32m bash scripts/thothctl-update-smoke.sh + - name: Run tht update smoke + run: timeout --signal=TERM --kill-after=45s 32m bash scripts/tht-update-smoke.sh - name: Run Linux server deployment smoke run: timeout --signal=TERM --kill-after=45s 32m bash scripts/server-deployment-smoke.sh @@ -114,7 +114,7 @@ jobs: uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: "1.26.5" - cache-dependency-path: tools/thothctl/go.sum + cache-dependency-path: tools/tht/go.sum - name: Verify Windows clone contract shell: pwsh run: ./scripts/test-windows-clone-contract.ps1 @@ -133,7 +133,7 @@ jobs: uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: "1.26.5" - cache-dependency-path: tools/thothctl/go.sum + cache-dependency-path: tools/tht/go.sum - name: Run spaced-path Windows Docker release gate shell: pwsh run: ./scripts/test-windows-clone-contract.ps1 -DockerStartup diff --git a/.gitignore b/.gitignore index 8aad16e8..6011cd14 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ Thoth/ # === Visual companion brainstorming artifacts (local-only) === .superpowers/ .worktrees/ -.thothctl/ +.tht/ # === Python === __pycache__/ diff --git a/docker/thothctl.Dockerfile b/docker/tht.Dockerfile similarity index 54% rename from docker/thothctl.Dockerfile rename to docker/tht.Dockerfile index 5fd51bb8..6d6544c9 100644 --- a/docker/thothctl.Dockerfile +++ b/docker/tht.Dockerfile @@ -1,16 +1,16 @@ FROM golang:1.26.5-bookworm@sha256:1ecb7edf62a0408027bd5729dfd6b1b8766e578e8df93995b225dfd0944eb651 AS build -WORKDIR /src/tools/thothctl -COPY tools/thothctl/go.mod tools/thothctl/go.sum ./ +WORKDIR /src/tools/tht +COPY tools/tht/go.mod tools/tht/go.sum ./ RUN go mod download -COPY tools/thothctl ./ +COPY tools/tht ./ RUN mkdir -p /out \ - && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/thothctl-windows-amd64.exe ./cmd/thothctl \ - && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/thothctl-darwin-amd64 ./cmd/thothctl \ - && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o /out/thothctl-darwin-arm64 ./cmd/thothctl \ - && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/thothctl-linux-amd64 ./cmd/thothctl \ - && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o /out/thothctl-linux-arm64 ./cmd/thothctl + && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/tht-windows-amd64.exe ./cmd/tht \ + && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/tht-darwin-amd64 ./cmd/tht \ + && CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o /out/tht-darwin-arm64 ./cmd/tht \ + && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags='-s -w' -o /out/tht-linux-amd64 ./cmd/tht \ + && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -ldflags='-s -w' -o /out/tht-linux-arm64 ./cmd/tht FROM scratch AS export COPY --from=build /out/ / diff --git a/scripts/build-thothctl.sh b/scripts/build-tht.sh similarity index 55% rename from scripts/build-thothctl.sh rename to scripts/build-tht.sh index d0ff7a37..b3a15fbb 100755 --- a/scripts/build-thothctl.sh +++ b/scripts/build-tht.sh @@ -3,14 +3,14 @@ set -euo pipefail export DOCKER_BUILDKIT=1 repository_root=$(cd "$(dirname "$0")/.." && pwd) -output_directory="${THT_THOTHCTL_OUTPUT_DIRECTORY:-$repository_root/dist/thothctl}" +output_directory="${THT_THT_OUTPUT_DIRECTORY:-$repository_root/dist/tht}" if [[ "$output_directory" != /* || "$output_directory" == / || "$output_directory" == */ || "$output_directory" == *//* || "/$output_directory/" == */../* || "/$output_directory/" == */./* ]]; then - echo "THT_THOTHCTL_OUTPUT_DIRECTORY must be an absolute canonical path" >&2 + echo "THT_THT_OUTPUT_DIRECTORY must be an absolute canonical path" >&2 exit 2 fi mkdir -p "$output_directory" -docker build --file "$repository_root/docker/thothctl.Dockerfile" --output "type=local,dest=$output_directory" "$repository_root" +docker build --file "$repository_root/docker/tht.Dockerfile" --output "type=local,dest=$output_directory" "$repository_root" diff --git a/scripts/test-server-operator-permissions.sh b/scripts/test-server-operator-permissions.sh index f5d7c0f0..42c3ca28 100755 --- a/scripts/test-server-operator-permissions.sh +++ b/scripts/test-server-operator-permissions.sh @@ -22,7 +22,7 @@ install -d -o 10001 -g 20002 -m 2750 /srv/thothii/secrets install -d -o 10001 -g 10001 -m 0750 /srv/thothii/data /srv/thothii/pi-state /srv/thothii/workspace-registry install -d -o 10001 -g 10001 -m 0700 /srv/thothii/data/workspace-secrets install -d -o 10001 -g 20002 -m 2750 /srv/thothii/source/ThothII /srv/thothii/source/ThothII/scripts -install -o 10001 -g 20002 -m 0750 /repository/scripts/build-thothctl.sh /srv/thothii/source/ThothII/scripts/build-thothctl.sh +install -o 10001 -g 20002 -m 0750 /repository/scripts/build-tht.sh /srv/thothii/source/ThothII/scripts/build-tht.sh install -o 10001 -g 20002 -m 0750 /repository/scripts/prepare-server-pi-state.sh /srv/thothii/source/ThothII/scripts/prepare-server-pi-state.sh /srv/thothii/source/ThothII/scripts/prepare-server-pi-state.sh /srv/thothii/pi-state 10001 10001 @@ -37,8 +37,8 @@ printf "%s\n" \ "if [[ \"\${1:-}\" == build ]]; then" \ " destination=; for argument in \"\$@\"; do case \"\$argument\" in type=local,dest=*) destination=\"\${argument#type=local,dest=}\" ;; esac; done" \ " test -n \"\$destination\"; mkdir -p \"\$destination\"" \ - " printf \"%s\\n\" \"#!/bin/bash\" \"set -euo pipefail\" \"test -r \\\"\\\$2\\\"\" \"docker compose up --detach\" > \"\$destination/thothctl-linux-amd64\"" \ - " chmod 0750 \"\$destination/thothctl-linux-amd64\"; exit 0" \ + " printf \"%s\\n\" \"#!/bin/bash\" \"set -euo pipefail\" \"test -r \\\"\\\$2\\\"\" \"docker compose up --detach\" > \"\$destination/tht-linux-amd64\"" \ + " chmod 0750 \"\$destination/tht-linux-amd64\"; exit 0" \ "fi" \ "test \"\${1:-}\" = compose; : > /srv/thothii/operator/start.marker" \ > /usr/local/bin/docker @@ -52,17 +52,17 @@ for protected in /srv/thothii /srv/thothii/source /srv/thothii/secrets \ /srv/thothii/data /srv/thothii/pi-state /srv/thothii/workspace-registry; do if touch "$protected/operator-must-not-write" 2>/dev/null; then exit 42; fi done -THT_THOTHCTL_OUTPUT_DIRECTORY=/srv/thothii/operator/build-output \ - /srv/thothii/source/ThothII/scripts/build-thothctl.sh -if THT_THOTHCTL_OUTPUT_DIRECTORY=relative-output \ - /srv/thothii/source/ThothII/scripts/build-thothctl.sh 2>/dev/null; then exit 44; fi +THT_THT_OUTPUT_DIRECTORY=/srv/thothii/operator/build-output \ + /srv/thothii/source/ThothII/scripts/build-tht.sh +if THT_THT_OUTPUT_DIRECTORY=relative-output \ + /srv/thothii/source/ThothII/scripts/build-tht.sh 2>/dev/null; then exit 44; fi root_output_error=/srv/thothii/operator/root-output.error -if THT_THOTHCTL_OUTPUT_DIRECTORY=/ \ - /srv/thothii/source/ThothII/scripts/build-thothctl.sh 2>"$root_output_error"; then exit 45; fi -grep -Fq "THT_THOTHCTL_OUTPUT_DIRECTORY must be an absolute canonical path" \ +if THT_THT_OUTPUT_DIRECTORY=/ \ + /srv/thothii/source/ThothII/scripts/build-tht.sh 2>"$root_output_error"; then exit 45; fi +grep -Fq "THT_THT_OUTPUT_DIRECTORY must be an absolute canonical path" \ "$root_output_error" || exit 46 rm -f "$root_output_error" -/srv/thothii/operator/build-output/thothctl-linux-amd64 \ +/srv/thothii/operator/build-output/tht-linux-amd64 \ --installation /srv/thothii/operator/thothii-installation.yaml start '\'' @@ -74,8 +74,8 @@ for target in auth.json models.json settings.json; do test "$(stat -c %u:%g /srv/thothii/pi-state/agent/$target)" = 10001:10001 test "$(stat -c %a /srv/thothii/pi-state/agent/$target)" = 600 done -test "$(stat -c %u:%g /srv/thothii/operator/build-output/thothctl-linux-amd64)" = 20001:20002 -test "$(stat -c %a /srv/thothii/operator/build-output/thothctl-linux-amd64)" = 750 +test "$(stat -c %u:%g /srv/thothii/operator/build-output/tht-linux-amd64)" = 20001:20002 +test "$(stat -c %a /srv/thothii/operator/build-output/tht-linux-amd64)" = 750 test -f /srv/thothii/operator/start.marker for protected in /srv/thothii /srv/thothii/source /srv/thothii/secrets \ /srv/thothii/data /srv/thothii/pi-state /srv/thothii/workspace-registry; do diff --git a/scripts/test-thothctl-build-contract.sh b/scripts/test-tht-build-contract.sh similarity index 58% rename from scripts/test-thothctl-build-contract.sh rename to scripts/test-tht-build-contract.sh index 7c785dc3..7fbfd697 100755 --- a/scripts/test-thothctl-build-contract.sh +++ b/scripts/test-tht-build-contract.sh @@ -2,19 +2,19 @@ set -euo pipefail repository_root=$(cd "$(dirname "$0")/.." && pwd) -dockerfile="$repository_root/docker/thothctl.Dockerfile" +dockerfile="$repository_root/docker/tht.Dockerfile" builder_image=$(awk '$1 == "FROM" && $3 == "AS" && $4 == "build" { print $2; exit }' "$dockerfile") expected_builder='golang:1.26.5-bookworm@sha256:1ecb7edf62a0408027bd5729dfd6b1b8766e578e8df93995b225dfd0944eb651' if [[ "$builder_image" != "$expected_builder" ]]; then - echo "thothctl builder must pin golang:1.26.5-bookworm by the approved multi-platform digest" >&2 + echo "tht builder must pin golang:1.26.5-bookworm by the approved multi-platform digest" >&2 exit 1 fi -grep -qx 'go 1.26.0' "$repository_root/tools/thothctl/go.mod" -grep -qx 'toolchain go1.26.5' "$repository_root/tools/thothctl/go.mod" -grep -Eq '^[[:space:]]*github.com/sirupsen/logrus v1\.9\.1$' "$repository_root/tools/thothctl/go.mod" -grep -Eq '^[[:space:]]*golang.org/x/sys v0\.47\.0$' "$repository_root/tools/thothctl/go.mod" +grep -qx 'go 1.26.0' "$repository_root/tools/tht/go.mod" +grep -qx 'toolchain go1.26.5' "$repository_root/tools/tht/go.mod" +grep -Eq '^[[:space:]]*github.com/sirupsen/logrus v1\.9\.1$' "$repository_root/tools/tht/go.mod" +grep -Eq '^[[:space:]]*golang.org/x/sys v0\.47\.0$' "$repository_root/tools/tht/go.mod" manifest=$(docker buildx imagetools inspect "$builder_image") printf '%s\n' "$manifest" | grep -Eq 'Platform:[[:space:]]+linux/amd64' @@ -24,10 +24,10 @@ temporary_output=$(mktemp -d) trap 'rm -rf "$temporary_output"' EXIT HUP INT TERM docker build --file "$dockerfile" --output "type=local,dest=$temporary_output" "$repository_root" >/dev/null -test -s "$temporary_output/thothctl-windows-amd64.exe" -test -s "$temporary_output/thothctl-darwin-amd64" -test -s "$temporary_output/thothctl-darwin-arm64" -test -s "$temporary_output/thothctl-linux-amd64" -test -s "$temporary_output/thothctl-linux-arm64" +test -s "$temporary_output/tht-windows-amd64.exe" +test -s "$temporary_output/tht-darwin-amd64" +test -s "$temporary_output/tht-darwin-arm64" +test -s "$temporary_output/tht-linux-amd64" +test -s "$temporary_output/tht-linux-arm64" -echo "thothctl build contract passed." +echo "tht build contract passed." diff --git a/scripts/test-verify-workspace-install-docs.sh b/scripts/test-verify-workspace-install-docs.sh index 6805c18a..be63746d 100755 --- a/scripts/test-verify-workspace-install-docs.sh +++ b/scripts/test-verify-workspace-install-docs.sh @@ -87,7 +87,7 @@ for required in \ } done grep -Fq '"$THTCTL" --help' "$server_guide" || { - echo "server guide lacks plain thothctl --help" >&2 + echo "server guide lacks plain tht --help" >&2 exit 1 } if grep -Fq '"$THTCTL" --installation "$INSTALLATION" --help' "$server_guide"; then @@ -106,7 +106,7 @@ for manual in "$root/docs/install/local-workspace-registry.md"; do fi done -grep -Fq 'THTCTL=/srv/thothii/operator/thothctl' \ +grep -Fq 'THTCTL=/srv/thothii/operator/tht' \ "$root/docs/install/server-workspace-registry.md" || { echo "server installation manual does not use the installation-aware operator CLI" >&2 exit 1 @@ -957,7 +957,7 @@ expect_guide_rejected \ "$root/docs/install/local.md" docs/install/local.md failed-pull \ "POSIX source update does not fail closed: source pull" expect_guide_rejected \ - "failed thothctl Pi status" verify_local_guide \ + "failed tht Pi status" verify_local_guide \ "$root/docs/install/local.md" docs/install/local.md failed-status \ "POSIX source update does not fail closed: Pi status" expect_guide_rejected \ diff --git a/scripts/test-windows-clone-contract.ps1 b/scripts/test-windows-clone-contract.ps1 index 887079c7..6fc9e484 100644 --- a/scripts/test-windows-clone-contract.ps1 +++ b/scripts/test-windows-clone-contract.ps1 @@ -134,11 +134,11 @@ try { Copy-Item -LiteralPath $source -Destination $destination } - $thothctl = Join-Path $spacedRepository "dist/thothctl/thothctl-windows-amd64.exe" - [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($thothctl)) | Out-Null - Invoke-BoundedNative -FilePath "go" -Arguments @("build", "-trimpath", "-o", $thothctl, "./cmd/thothctl") ` - -WorkingDirectory (Join-Path $spacedRepository "tools/thothctl") -Label "build native Windows thothctl in spaced path" | Out-Null - Invoke-BoundedNative -FilePath $thothctl -Arguments @("--help") -Label "invoke native Windows thothctl from spaced path" | Out-Null + $tht = Join-Path $spacedRepository "dist/tht/tht-windows-amd64.exe" + [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($tht)) | Out-Null + Invoke-BoundedNative -FilePath "go" -Arguments @("build", "-trimpath", "-o", $tht, "./cmd/tht") ` + -WorkingDirectory (Join-Path $spacedRepository "tools/tht") -Label "build native Windows tht in spaced path" | Out-Null + Invoke-BoundedNative -FilePath $tht -Arguments @("--help") -Label "invoke native Windows tht from spaced path" | Out-Null $piAuth = Join-Path $fixtureRoot "Pi Auth/pi-auth.json" $secrets = Join-Path $fixtureRoot "Secrets/thothii.secrets" @@ -266,7 +266,7 @@ overrides: if (($runningServices -join ",") -ne "core,frontend") { throw "bounded Windows startup did not leave exactly core and frontend running" } - Invoke-BoundedNative -FilePath $thothctl -Arguments @("--installation", $installation, "status") -Label "invoke installation-aware Windows thothctl in spaced path" | Out-Null + Invoke-BoundedNative -FilePath $tht -Arguments @("--installation", $installation, "status") -Label "invoke installation-aware Windows tht in spaced path" | Out-Null } } finally { @@ -311,7 +311,7 @@ if (-not $cleanupSucceeded) { throw "Windows cleanup proof failed; fixture path retained for recovery" } if ($DockerStartup) { - Write-Output "Windows spaced-path build, native thothctl, bounded two-service startup, and exact cleanup passed." + Write-Output "Windows spaced-path build, native tht, bounded two-service startup, and exact cleanup passed." } else { - Write-Output "Windows spaced-path clone, LF-byte, Compose render, and native thothctl build/invocation contracts passed; Docker startup mode was not requested." + Write-Output "Windows spaced-path clone, LF-byte, Compose render, and native tht build/invocation contracts passed; Docker startup mode was not requested." } diff --git a/scripts/thothctl-update-smoke.sh b/scripts/tht-update-smoke.sh similarity index 67% rename from scripts/thothctl-update-smoke.sh rename to scripts/tht-update-smoke.sh index b6c3f6c5..9aa2ba8b 100755 --- a/scripts/thothctl-update-smoke.sh +++ b/scripts/tht-update-smoke.sh @@ -5,4 +5,4 @@ root="$(cd "$(dirname "$0")/.." && pwd -P)" # shellcheck source=./unified-deployment-smoke.sh source "$root/scripts/unified-deployment-smoke.sh" -task13_supervise "$TASK13_SMOKE_TIMEOUT" "thothctl update smoke" task13_smoke_main update +task13_supervise "$TASK13_SMOKE_TIMEOUT" "tht update smoke" task13_smoke_main update diff --git a/scripts/unified-deployment-smoke.sh b/scripts/unified-deployment-smoke.sh index 4d7235da..060ef5ac 100755 --- a/scripts/unified-deployment-smoke.sh +++ b/scripts/unified-deployment-smoke.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # End-to-end release gate for the canonical two-service Compose distribution. -# This file is also sourced by thothctl-update-smoke.sh so both entry points use the same +# This file is also sourced by tht-update-smoke.sh so both entry points use the same # isolated fixture, exact cleanup, and sanitized failure reporting. set -euo pipefail @@ -556,23 +556,23 @@ task13_seed_registry() { task13_commit_registry_change 'Seed Task 13 workspace registry' } -task13_build_thothctl() { +task13_build_tht() { local os arch - mkdir -p "$TASK13_THOTHCTL_DIR" - task13_run_logged "build thothctl cross-platform binaries" env \ - THT_THOTHCTL_OUTPUT_DIRECTORY="$TASK13_THOTHCTL_DIR" \ - bash "$TASK13_ROOT/scripts/build-thothctl.sh" + mkdir -p "$TASK13_THT_DIR" + task13_run_logged "build tht cross-platform binaries" env \ + THT_THT_OUTPUT_DIRECTORY="$TASK13_THT_DIR" \ + bash "$TASK13_ROOT/scripts/build-tht.sh" os="$(uname -s)" arch="$(uname -m)" case "$os/$arch" in - Darwin/x86_64) TASK13_THOTHCTL="$TASK13_THOTHCTL_DIR/thothctl-darwin-amd64" ;; - Darwin/arm64) TASK13_THOTHCTL="$TASK13_THOTHCTL_DIR/thothctl-darwin-arm64" ;; - Linux/x86_64|Linux/amd64) TASK13_THOTHCTL="$TASK13_THOTHCTL_DIR/thothctl-linux-amd64" ;; - Linux/aarch64|Linux/arm64) TASK13_THOTHCTL="$TASK13_THOTHCTL_DIR/thothctl-linux-arm64" ;; + Darwin/x86_64) TASK13_THT="$TASK13_THT_DIR/tht-darwin-amd64" ;; + Darwin/arm64) TASK13_THT="$TASK13_THT_DIR/tht-darwin-arm64" ;; + Linux/x86_64|Linux/amd64) TASK13_THT="$TASK13_THT_DIR/tht-linux-amd64" ;; + Linux/aarch64|Linux/arm64) TASK13_THT="$TASK13_THT_DIR/tht-linux-arm64" ;; *) task13_fail "unsupported smoke host: $os/$arch" ;; esac - chmod 0700 "$TASK13_THOTHCTL" - task13_run_logged "invoke host thothctl" "$TASK13_THOTHCTL" --help + chmod 0700 "$TASK13_THT" + task13_run_logged "invoke host tht" "$TASK13_THT" --help } task13_assert_rendered_contract() { @@ -670,12 +670,12 @@ task13_assert_runtime() { || task13_fail "core lacks the explicit Task 13 resource label" task13_compose_logged "internal Pi provider smoke" exec -T core \ curl --connect-timeout 3 --max-time 45 -fsS -X POST \ - -H 'x-thoth-principal-issuer: thothctl' \ - -H 'x-thoth-principal-subject: thothctl-maintenance' \ - -H 'x-thoth-principal-display-name: Thothctl maintenance' \ + -H 'x-thoth-principal-issuer: tht' \ + -H 'x-thoth-principal-subject: tht-maintenance' \ + -H 'x-thoth-principal-display-name: Tht maintenance' \ -H 'x-thoth-is-admin: 1' \ http://127.0.0.1:8787/pi-management/test - task13_run_logged "thothctl Pi doctor" "$TASK13_THOTHCTL" --installation "$TASK13_INSTALLATION" pi doctor + task13_run_logged "tht Pi doctor" "$TASK13_THT" --installation "$TASK13_INSTALLATION" pi doctor } task13_server_auth_headers() { @@ -950,20 +950,20 @@ task13_update_rollback() { TASK13_PREVIOUS_IMAGE_ID="$before_image" before_mounts="$(task13_mount_fingerprint)" before_head="$(task13_active_registry_head)" - output="$TASK13_TMP/thothctl-update.out" + output="$TASK13_TMP/tht-update.out" set +e - "$TASK13_THOTHCTL" --installation "$TASK13_INSTALLATION" pi update \ + "$TASK13_THT" --installation "$TASK13_INSTALLATION" pi update \ --version "$TASK13_BAD_PI_VERSION" --source pull --image "$TASK13_BAD_CANDIDATE_IMAGE" --yes \ >"$output" 2>&1 rc=$? set -e [[ "$rc" -ne 0 ]] || task13_fail "bad Pi candidate unexpectedly passed update verification" if grep -Fq "$TASK13_SECRET_VALUE" "$output"; then - task13_fail "thothctl update output exposed the fixture secret" + task13_fail "tht update output exposed the fixture secret" fi grep -Fq 'previous core image was restored' "$output" \ - || { task13_sanitize <"$output" >&2; task13_fail "thothctl did not report automatic rollback"; } - [[ -f "$TASK13_UPDATE_STATE" ]] || task13_fail "thothctl update state was not persisted" + || { task13_sanitize <"$output" >&2; task13_fail "tht did not report automatic rollback"; } + [[ -f "$TASK13_UPDATE_STATE" ]] || task13_fail "tht update state was not persisted" phase="$(sed -n 's/.*"phase": "\([^"]*\)".*/\1/p' "$TASK13_UPDATE_STATE" | head -n 1)" [[ "$phase" == rolled_back ]] || task13_fail "update state phase is not rolled_back" if grep -Fq "$TASK13_SECRET_VALUE" "$TASK13_UPDATE_STATE"; then @@ -977,8 +977,8 @@ task13_update_rollback() { [[ "$after_mounts" == "$before_mounts" ]] || task13_fail "rollback changed persistence volume identity" [[ "$after_head" == "$before_head" ]] || task13_fail "rollback changed the active registry revision" task13_assert_sentinels - task13_run_logged "post-rollback thothctl doctor" \ - "$TASK13_THOTHCTL" --installation "$TASK13_INSTALLATION" pi doctor + task13_run_logged "post-rollback tht doctor" \ + "$TASK13_THT" --installation "$TASK13_INSTALLATION" pi doctor task13_compose exec -T core curl --connect-timeout 3 --max-time 10 -fsS \ http://127.0.0.1:8787/workspaces \ | grep -Fq 'Task 13 Smoke' || task13_fail "rollback lost the active workspace" @@ -1020,7 +1020,7 @@ task13_remove_transaction_image() { if ! docker image inspect "$reference" >/dev/null 2>&1; then return 0 fi - if [[ ! "$reference" =~ ^thothii-core:thothctl-[0-9a-f]{16}-(candidate|previous)$ \ + if [[ ! "$reference" =~ ^thothii-core:tht-[0-9a-f]{16}-(candidate|previous)$ \ || ! "$expected_id" =~ ^sha256:([0-9a-f]{64}|owned)$ ]]; then printf 'refusing to remove invalid transaction image reference %s\n' "$reference" >&2 return 1 @@ -1108,9 +1108,9 @@ task13_cleanup() { transaction="$(sed -n 's/.*"transaction": "\([^"]*\)".*/\1/p' "$TASK13_UPDATE_STATE" | head -n 1)" fi if [[ -n "$transaction" ]]; then - task13_remove_transaction_image "thothii-core:thothctl-$transaction-candidate" \ + task13_remove_transaction_image "thothii-core:tht-$transaction-candidate" \ "${TASK13_BAD_CANDIDATE_ID:-}" >>"${TASK13_LOG:-/dev/null}" 2>&1 || cleanup_rc=1 - task13_remove_transaction_image "thothii-core:thothctl-$transaction-previous" \ + task13_remove_transaction_image "thothii-core:tht-$transaction-previous" \ "${TASK13_PREVIOUS_IMAGE_ID:-}" >>"${TASK13_LOG:-/dev/null}" 2>&1 || cleanup_rc=1 fi for image in \ @@ -1126,7 +1126,7 @@ task13_cleanup() { done < <(docker image ls -q --filter "label=io.thothii.task13.run=$TASK13_RUN_ID" | sort -u) fi if [[ -n "${TASK13_CONTROL_DIR:-}" ]]; then - if [[ "$TASK13_CONTROL_DIR" == "$TASK13_ROOT/.thothctl/$TASK13_PROJECT" \ + if [[ "$TASK13_CONTROL_DIR" == "$TASK13_ROOT/.tht/$TASK13_PROJECT" \ && "$TASK13_PROJECT" =~ ^thothii-[0-9a-f]{12}$ ]]; then rm -rf "$TASK13_CONTROL_DIR" else @@ -1319,8 +1319,8 @@ task13_self_test_transaction_image_cleanup() { local calls foreign_error owned_ref foreign_ref calls="$(mktemp "${TMPDIR:-/tmp}/thothii-task13-transaction-cleanup-contract.XXXXXX")" foreign_error="$calls.foreign-error" - owned_ref="thothii-core:thothctl-0123456789abcdef-candidate" - foreign_ref="thothii-core:thothctl-fedcba9876543210-candidate" + owned_ref="thothii-core:tht-0123456789abcdef-candidate" + foreign_ref="thothii-core:tht-fedcba9876543210-candidate" if ! declare -F task13_remove_transaction_image >/dev/null; then rm -f "$calls" "$foreign_error" @@ -1478,7 +1478,7 @@ task13_self_test_public_timeout_contract() { "$root/scripts/unified-deployment-smoke.sh" \ || task13_fail "direct unified smoke invocation lacks an internal supervisor" grep -Eq 'task13_supervise[[:space:]].*task13_smoke_main[[:space:]]+update' \ - "$root/scripts/thothctl-update-smoke.sh" \ + "$root/scripts/tht-update-smoke.sh" \ || task13_fail "direct update smoke invocation lacks an internal supervisor" grep -Eq 'task13_supervise[[:space:]].*task13_internal_semantic_smoke_main' \ "$root/scripts/internal-semantic-smoke.sh" \ @@ -1562,7 +1562,7 @@ task13_self_test_source_contract() { registry_function='task13_start_''registry' if rg -n 'docker[[:space:]]+(system[[:space:]]+)?prune' \ "$root/scripts/unified-deployment-smoke.sh" \ - "$root/scripts/thothctl-update-smoke.sh" \ + "$root/scripts/tht-update-smoke.sh" \ "$root/scripts/internal-semantic-smoke.sh" >/dev/null; then task13_fail "Task 13 smoke scripts must never prune global Docker state" fi @@ -1577,8 +1577,8 @@ task13_self_test_source_contract() { || task13_fail "the bad rollback candidate must be an immutable digest reference" grep -Eq 'timeout .*scripts/unified-deployment-smoke\.sh' "$workflow" \ || task13_fail "CI lacks an outer timeout for the unified deployment smoke" - grep -Eq 'timeout .*scripts/thothctl-update-smoke\.sh' "$workflow" \ - || task13_fail "CI lacks an outer timeout for the thothctl update smoke" + grep -Eq 'timeout .*scripts/tht-update-smoke\.sh' "$workflow" \ + || task13_fail "CI lacks an outer timeout for the tht update smoke" uses_count="$(grep -Ec '^[[:space:]]+uses:' "$workflow")" pinned_uses_count="$(grep -Ec '^[[:space:]]+uses: [^[:space:]]+@[0-9a-f]{40}([[:space:]]|$)' "$workflow")" [[ "$uses_count" -gt 0 && "$uses_count" -eq "$pinned_uses_count" ]] \ @@ -1697,8 +1697,8 @@ task13_initialize() { TASK13_PROFILE="local" TASK13_INSTALLATION="$TASK13_TMP/thothii-installation.yaml" TASK13_PROJECT="thothii-$(task13_sha256_text "$TASK13_INSTALLATION" | cut -c1-12)" - TASK13_CONTROL_DIR="$TASK13_ROOT/.thothctl/$TASK13_PROJECT" - [[ ! -e "$TASK13_CONTROL_DIR" ]] || task13_fail "unique thothctl control directory already exists" + TASK13_CONTROL_DIR="$TASK13_ROOT/.tht/$TASK13_PROJECT" + [[ ! -e "$TASK13_CONTROL_DIR" ]] || task13_fail "unique tht control directory already exists" TASK13_CURRENT_IMAGE_OVERRIDE="$TASK13_CONTROL_DIR/current-image.yaml" TASK13_UPDATE_STATE="$TASK13_CONTROL_DIR/update-state.json" TASK13_REMOTE="$TASK13_TMP/remote.git" @@ -1711,7 +1711,7 @@ task13_initialize() { TASK13_PI_MODELS="$TASK13_TMP/models.json" TASK13_PI_SETTINGS="$TASK13_TMP/pi-settings.json" TASK13_LLM_SERVER="$TASK13_TMP/fake-llm.mjs" - TASK13_THOTHCTL_DIR="$TASK13_TMP/thothctl" + TASK13_THT_DIR="$TASK13_TMP/tht" TASK13_LLM_CONTAINER="$TASK13_PROJECT-llm" TASK13_BAD_CANDIDATE_CONTAINER="$TASK13_PROJECT-bad-candidate" TASK13_CORE_IMAGE="task13-core-$TASK13_RUN_ID:local" @@ -1748,7 +1748,7 @@ task13_smoke_main() { task13_write_fixture_files task13_write_environment /fixtures/remote.git task13_seed_registry - task13_build_thothctl + task13_build_tht task13_start_stack task13_assert_project_ownership task13_assert_built_image_ownership diff --git a/scripts/verify-workspace-install-docs.sh b/scripts/verify-workspace-install-docs.sh index 651b326d..7841f505 100755 --- a/scripts/verify-workspace-install-docs.sh +++ b/scripts/verify-workspace-install-docs.sh @@ -768,7 +768,7 @@ verify_local_guide() { "Clone and verify LF" \ "Create the local operator files" \ "Address external services" \ - "Build ThothII and thothctl" \ + "Build ThothII and tht" \ "Start and verify" \ "Update an installation" \ "Back up and restore" \ @@ -783,8 +783,8 @@ verify_local_guide() { "container 127.0.0.1" \ "bash scripts/build-local.sh" \ "scripts/build-local.ps1" \ - "bash scripts/build-thothctl.sh" \ - "thothctl --installation" \ + "bash scripts/build-tht.sh" \ + "tht --installation" \ "curl --fail http://127.0.0.1:8080/health" \ "http://127.0.0.1:8080" \ "git pull --ff-only" \ @@ -856,8 +856,8 @@ requirePattern("POSIX source update does not fail closed: Pi status", updateShel /if ! RUNNING_PI_VERSION="\$\("\$THTCTL" --installation "\$INSTALLATION" pi status\)"; then/); requirePattern("POSIX source update does not fail closed: local build", updateShell, /if ! bash scripts\/build-local\.sh; then/); -requirePattern("POSIX source update does not fail closed: thothctl build", updateShell, - /if ! bash scripts\/build-thothctl\.sh; then/); +requirePattern("POSIX source update does not fail closed: tht build", updateShell, + /if ! bash scripts\/build-tht\.sh; then/); requirePattern("POSIX source update lacks the same-version/no-selector path", updateShell, /if \[\[ "\$NEXT_PI_VERSION" == "\$RUNNING_PI_VERSION" \]\]; then[\s\S]*"\$USES_BASE_CORE" == true[\s\S]*TRANSACTIONAL_PI_UPDATE=false/); for (const [label, pattern] of [ @@ -886,7 +886,7 @@ for (const [command, step] of [ ["$InstallationStatus = @(& $THTCTL --installation $INSTALLATION status)", "installation status"], ["$RunningPiStatus = (& $THTCTL --installation $INSTALLATION pi status)", "Pi status"], ["powershell -ExecutionPolicy Bypass -File scripts/build-local.ps1", "local image build"], - ["& \"C:\\Program Files\\Git\\bin\\bash.exe\" scripts/build-thothctl.sh", "thothctl build"], + ["& \"C:\\Program Files\\Git\\bin\\bash.exe\" scripts/build-tht.sh", "tht build"], ["curl.exe --fail --silent --show-error http://127.0.0.1:8080/health", "frontend health check"], ["curl.exe --fail --silent --show-error http://127.0.0.1:8787/health", "core health check"], ["$FinalPiStatus = (& $THTCTL --installation $INSTALLATION pi status)", "final Pi status"], @@ -942,18 +942,18 @@ NODE 'exit 0' >"$update_fixture/bin/bash" printf '%s\n' \ '#!/bin/sh' \ - 'printf "thothctl %s\n" "$*" >>"$CALLS"' \ + 'printf "tht %s\n" "$*" >>"$CALLS"' \ 'case " $* " in' \ ' *" pi status "*) [ "$FAIL_STEP" != status ] || exit 7; printf "Pi version: 0.80.3\n" ;;' \ ' *" status "*) printf "[{\"Service\":\"core\",\"Image\":\"thothii-core:local\"}]\n" ;;' \ 'esac' \ - 'exit 0' >"$update_fixture/bin/thothctl" + 'exit 0' >"$update_fixture/bin/tht" printf '%s\n' \ '#!/bin/sh' \ 'printf "curl %s\n" "$*" >>"$CALLS"' \ 'exit 0' >"$update_fixture/bin/curl" chmod 0700 "$update_fixture/bin/git" "$update_fixture/bin/bash" \ - "$update_fixture/bin/thothctl" "$update_fixture/bin/curl" + "$update_fixture/bin/tht" "$update_fixture/bin/curl" for fixture_step in clean dirty pull status build; do calls="$update_fixture/calls-$fixture_step" @@ -963,7 +963,7 @@ NODE ( cd "$update_fixture/project" env PATH="$update_fixture/bin:$PATH" CALLS="$calls" FAIL_STEP="$fixture_step" \ - THTCTL="$update_fixture/bin/thothctl" INSTALLATION="$update_fixture/installation.yaml" \ + THTCTL="$update_fixture/bin/tht" INSTALLATION="$update_fixture/installation.yaml" \ /bin/bash "$update_script" ) >"$output" 2>&1 status=$? @@ -978,7 +978,7 @@ NODE return 1 fi grep -Fq 'bash scripts/build-local.sh' "$calls" || return 1 - grep -Fq 'thothctl --installation ' "$calls" || return 1 + grep -Fq 'tht --installation ' "$calls" || return 1 else [[ $status -ne 0 ]] || { echo "$fixture_step source failure fixture was accepted" >&2; return 1; } if grep -Fq 'Built source revision:' "$output"; then @@ -1131,13 +1131,13 @@ NODE verify_pi_management_guide() { local guide="$root/docs/install/pi-management.md" - local lifecycle_contract="$root/docs/contracts/thothctl-pi.md" + local lifecycle_contract="$root/docs/contracts/tht-pi.md" [[ -f "$guide" ]] || { echo "missing Pi management guide: docs/install/pi-management.md" >&2 return 1 } [[ -f "$lifecycle_contract" ]] || { - echo "missing Pi lifecycle contract: docs/contracts/thothctl-pi.md" >&2 + echo "missing Pi lifecycle contract: docs/contracts/tht-pi.md" >&2 return 1 } require_text "$lifecycle_contract" "Pi lifecycle contract" \ @@ -1192,7 +1192,7 @@ const marker = "## Direct support access"; const start = source.indexOf(marker); const support = start < 0 ? "" : source.slice(start, source.indexOf("\n## ", start + marker.length) < 0 ? source.length : source.indexOf("\n## ", start + marker.length)); -for (const token of ["unsupported", "thothctl", "pi status", "pi doctor", "pi test", "pi logs"]) { +for (const token of ["unsupported", "tht", "pi status", "pi doctor", "pi test", "pi logs"]) { if (!support.toLowerCase().includes(token.toLowerCase())) { throw new Error(`direct support section lacks installation-aware diagnostic: ${token}`); } @@ -1214,7 +1214,7 @@ verify_server_guide() { "Address co-resident external services" \ "Prepare operator files and secrets" \ "Build locally or select pinned images" \ - "Install thothctl" \ + "Install tht" \ "Start and verify readiness" \ "Configure TLS and upstream authentication" \ "Operate Pi, drain, and roll back" \ @@ -1228,7 +1228,7 @@ verify_server_guide() { "thothii-ops" \ "-m 2770 /srv/thothii/operator" \ "chmod 0660 /srv/thothii/operator/server.env" \ - "THT_THOTHCTL_OUTPUT_DIRECTORY=/srv/thothii/operator/build-output" \ + "THT_THT_OUTPUT_DIRECTORY=/srv/thothii/operator/build-output" \ "/srv/thothii" \ "example operator root" \ "/run/secrets" \ @@ -1243,8 +1243,8 @@ verify_server_guide() { "embedding" \ "bash scripts/build-local.sh" \ "@sha256:" \ - "bash scripts/build-thothctl.sh" \ - "thothctl --installation" \ + "bash scripts/build-tht.sh" \ + "tht --installation" \ "sessions migrate --yes" \ '"pending":[]' \ '"drifted":[]' \ @@ -1325,7 +1325,7 @@ if (/session-migrate:[\s\S]{0,180}image:\s*thothii-core:local/.test(source) && throw new Error("server pinned migration image must equal the pinned core image"); } if (/```(?:sh|bash)\n[\s\S]*?\bdocker compose\s+(?:up|stop|down|restart|pull|build)\b[\s\S]*?```/i.test(source)) { - throw new Error("server lifecycle must use thothctl, not raw Docker Compose"); + throw new Error("server lifecycle must use tht, not raw Docker Compose"); } NODE echo "server installation guide contract passed" @@ -1735,7 +1735,7 @@ verify_manual() { ) else expected_steps=( - 'THTCTL=/srv/thothii/operator/thothctl' + 'THTCTL=/srv/thothii/operator/tht' 'INSTALLATION=/srv/thothii/operator/thothii-installation.yaml' '"$THTCTL" --installation "$INSTALLATION" start' '"$THTCTL" --installation "$INSTALLATION" doctor' diff --git a/tools/thothctl/cmd/thothctl/main.go b/tools/tht/cmd/tht/main.go similarity index 93% rename from tools/thothctl/cmd/thothctl/main.go rename to tools/tht/cmd/tht/main.go index 362b07db..df845235 100644 --- a/tools/thothctl/cmd/thothctl/main.go +++ b/tools/tht/cmd/tht/main.go @@ -1,4 +1,4 @@ -// thothctl is the host-side operator command for a local ThothII installation. +// tht is the host-side operator command for a local ThothII installation. package main import ( @@ -14,15 +14,15 @@ import ( "strconv" "strings" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" - "github.com/aritmolab/thothii/tools/thothctl/internal/output" - "github.com/aritmolab/thothii/tools/thothctl/internal/pi" - "github.com/aritmolab/thothii/tools/thothctl/internal/serverops" - "github.com/aritmolab/thothii/tools/thothctl/internal/workspaceops" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/output" + "github.com/aritmolab/thothii/tools/tht/internal/pi" + "github.com/aritmolab/thothii/tools/tht/internal/serverops" + "github.com/aritmolab/thothii/tools/tht/internal/workspaceops" ) -const usage = `Usage: thothctl --installation /thothii-installation.yaml +const usage = `Usage: tht --installation /thothii-installation.yaml Commands: status Show the Compose service state. @@ -75,22 +75,22 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int { } installationPath, command, commandArgs, err := parseArgs(args) if err != nil { - fmt.Fprintf(stderr, "thothctl: %s\n\n%s", err, usage) + fmt.Fprintf(stderr, "tht: %s\n\n%s", err, usage) return 2 } installation, err := config.Load(installationPath) if err != nil { - fmt.Fprintf(stderr, "thothctl: %s\n", output.Sanitize(err.Error(), nil)) + fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), nil)) return 2 } secretFiles, err := installation.SecretFiles() if err != nil { - fmt.Fprintln(stderr, "thothctl: installation secret declarations could not be read") + fmt.Fprintln(stderr, "tht: installation secret declarations could not be read") return 2 } secretValues, err := output.SecretValuesFromFiles(secretFiles) if err != nil { - fmt.Fprintln(stderr, "thothctl: declared secret file could not be read") + fmt.Fprintln(stderr, "tht: declared secret file could not be read") return 2 } @@ -139,7 +139,7 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int { return serverOperationFailure(stderr, operationErr, secretValues) } if encodeErr := json.NewEncoder(stdout).Encode(status); encodeErr != nil { - fmt.Fprintln(stderr, "thothctl: migration status could not be written") + fmt.Fprintln(stderr, "tht: migration status could not be written") return 1 } return 0 @@ -154,7 +154,7 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int { removal, operationErr := serverops.Remove(ctx, installation, runner, confirmedIDs) writeRemovalTargets(stdout, installation.ProjectName(), removal.Targets) if errors.Is(operationErr, serverops.ErrConfirmationRequired) { - fmt.Fprint(stderr, "thothctl: inspect the exact targets above, then re-run with remove --yes") + fmt.Fprint(stderr, "tht: inspect the exact targets above, then re-run with remove --yes") for _, target := range removal.Targets { fmt.Fprintf(stderr, " %s", target.ID) } @@ -198,7 +198,7 @@ func workspaceCommand(ctx context.Context, installation config.Installation, run encoder := json.NewEncoder(stdout) encoder.SetEscapeHTML(false) if encodeErr := encoder.Encode(result); encodeErr != nil { - fmt.Fprintln(stderr, "thothctl: workspace result could not be written") + fmt.Fprintln(stderr, "tht: workspace result could not be written") return 1 } } else { @@ -219,9 +219,9 @@ func workspaceFailure(stderr io.Writer, err error, secretValues []string) int { var operationErr *workspaceops.OperationError if errors.As(err, &operationErr) && operationErr.Detail() != "" { detail := output.SanitizeDetail(operationErr.Detail(), secretValues) - fmt.Fprintf(stderr, "thothctl: %s: %s\n", message, detail) + fmt.Fprintf(stderr, "tht: %s: %s\n", message, detail) } else { - fmt.Fprintf(stderr, "thothctl: %s\n", message) + fmt.Fprintf(stderr, "tht: %s\n", message) } return 1 } @@ -231,9 +231,9 @@ func serverOperationFailure(stderr io.Writer, err error, secretValues []string) var operationErr *serverops.OperationError if errors.As(err, &operationErr) && operationErr.Detail() != "" { detail := output.SanitizeDetail(operationErr.Detail(), secretValues) - fmt.Fprintf(stderr, "thothctl: %s: %s\n", message, detail) + fmt.Fprintf(stderr, "tht: %s: %s\n", message, detail) } else { - fmt.Fprintf(stderr, "thothctl: %s\n", message) + fmt.Fprintf(stderr, "tht: %s\n", message) } if errors.Is(err, serverops.ErrConfirmationRequired) || errors.Is(err, serverops.ErrUnsafeState) { return 2 @@ -315,7 +315,7 @@ func piCommand(ctx context.Context, installation config.Installation, runner com if err := pi.Configure(ctx, controlled, defaults); err != nil { return piFailure(stderr, err, secretValues) } - fmt.Fprintf(stdout, "Pi defaults applied and read back. Provider credentials remain only in the host file %s (mode 0600). Never pass credentials to thothctl.\n", authFile) + fmt.Fprintf(stdout, "Pi defaults applied and read back. Provider credentials remain only in the host file %s (mode 0600). Never pass credentials to tht.\n", authFile) return 0 case "restart": request, err := parsePiRestartArgs( @@ -583,7 +583,7 @@ func piFailure(stderr io.Writer, err error, secretValues []string) int { if errors.As(err, &childExit) && childExit.ExitCode() != 0 { code = childExit.ExitCode() } - fmt.Fprintf(stderr, "thothctl: %s\n", output.Sanitize(err.Error(), secretValues)) + fmt.Fprintf(stderr, "tht: %s\n", output.Sanitize(err.Error(), secretValues)) return code } @@ -605,7 +605,7 @@ func logsArgs(args []string) ([]string, error) { } func commandUsageError(stderr io.Writer, message string) int { - fmt.Fprintf(stderr, "thothctl: %s\n", message) + fmt.Fprintf(stderr, "tht: %s\n", message) return 2 } @@ -620,7 +620,7 @@ func writeResult(result compose.Result, err error, secretValues []string, stdout return 0 } if errors.Is(err, exec.ErrNotFound) { - fmt.Fprintln(stderr, "thothctl: Docker is not installed or is not on PATH") + fmt.Fprintln(stderr, "tht: Docker is not installed or is not on PATH") } if result.ExitCode != 0 { return result.ExitCode @@ -650,15 +650,15 @@ func doctor(ctx context.Context, installation config.Installation, runner compos } } if err := requireLF(installation.ProjectDirectory); err != nil { - fmt.Fprintf(stderr, "thothctl: %s\n", err) + fmt.Fprintf(stderr, "tht: %s\n", err) return 1 } if err := requireVolumes(renderedConfig); err != nil { - fmt.Fprintf(stderr, "thothctl: %s\n", err) + fmt.Fprintf(stderr, "tht: %s\n", err) return 1 } if err := requireHealthyServices(status); err != nil { - fmt.Fprintf(stderr, "thothctl: %s\n", err) + fmt.Fprintf(stderr, "tht: %s\n", err) return 1 } fmt.Fprintln(stdout, "Doctor checks passed.") diff --git a/tools/thothctl/cmd/thothctl/main_test.go b/tools/tht/cmd/tht/main_test.go similarity index 89% rename from tools/thothctl/cmd/thothctl/main_test.go rename to tools/tht/cmd/tht/main_test.go index 736f1fe5..89500648 100644 --- a/tools/thothctl/cmd/thothctl/main_test.go +++ b/tools/tht/cmd/tht/main_test.go @@ -11,10 +11,10 @@ import ( "strings" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" - "github.com/aritmolab/thothii/tools/thothctl/internal/pi" - "github.com/aritmolab/thothii/tools/thothctl/internal/testsupport" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/pi" + "github.com/aritmolab/thothii/tools/tht/internal/testsupport" ) func TestInstallationRunnerMapsProfileToSessionInventoryScope(t *testing.T) { @@ -32,6 +32,48 @@ func TestInstallationRunnerMapsProfileToSessionInventoryScope(t *testing.T) { } } +func TestRootCommandIdentity(t *testing.T) { + fixture := newCLIFixture(t, "") + fixture.setEnvironment(t) + retiredCommand := "thoth" + "ctl" + + for _, test := range []struct { + name string + args []string + wantCode int + wantText string + }{ + { + name: "help banner", + args: []string{"--help"}, + wantCode: 0, + wantText: "Usage: tht ", + }, + { + name: "version path", + args: []string{"--installation", fixture.installationPath, "version"}, + wantCode: 2, + wantText: `tht: unknown command "version"`, + }, + { + name: "retired command is not an alias", + args: []string{"--installation", fixture.installationPath, retiredCommand}, + wantCode: 2, + wantText: `tht: unknown command "` + retiredCommand + `"`, + }, + } { + t.Run(test.name, func(t *testing.T) { + var stdout, stderr bytes.Buffer + if got := run(context.Background(), test.args, &stdout, &stderr); got != test.wantCode { + t.Fatalf("run(%v) exit code = %d, want %d", test.args, got, test.wantCode) + } + if got := stdout.String() + stderr.String(); !strings.Contains(got, test.wantText) { + t.Fatalf("run(%v) output = %q, want %q", test.args, got, test.wantText) + } + }) + } +} + // Catches interactive configuration prompts that use retired model-only data instead of the // provider, model, and reasoning choices supplied by the dedicated Pi Management API. func TestResolvePiConfigureUsesNumberedClosedChoicesOnlyForTTY(t *testing.T) { @@ -106,8 +148,8 @@ func TestUsageDocumentsClosedConfigureUpdateSourcesRestartAndMaintenanceRecovery } func TestParsePiRestartArgs(t *testing.T) { - restartPath := "/var/lib/thothctl/restart-state.json" - updatePath := "/var/lib/thothctl/update-state.json" + restartPath := "/var/lib/tht/restart-state.json" + updatePath := "/var/lib/tht/update-state.json" for _, test := range []struct { name string args []string @@ -191,9 +233,9 @@ func TestRunSessionsMigrateRedactsCompleteDetailBeforeDisplayBound(t *testing.T) t.Fatal(err) } fixture.setEnvironment(t, secretPath) - t.Setenv("THOTHCTL_FAKE_CONFIG", `{"services":{"core":{"image":"thothii-core:local"},"session-migrate":{"image":"thothii-core:local"}}}`) - t.Setenv("THOTHCTL_FAKE_MIGRATION_FAILURE", spec.failure) - t.Setenv("THOTHCTL_FAKE_MIGRATION_EXIT", "23") + t.Setenv("THT_FAKE_CONFIG", `{"services":{"core":{"image":"thothii-core:local"},"session-migrate":{"image":"thothii-core:local"}}}`) + t.Setenv("THT_FAKE_MIGRATION_FAILURE", spec.failure) + t.Setenv("THT_FAKE_MIGRATION_EXIT", "23") var stdout, stderr bytes.Buffer code := run(context.Background(), []string{ @@ -222,7 +264,7 @@ func TestRunRemoveDisplaysExactInstallationTargetsBeforeConfirmation(t *testing. fixture := newCLIFixture(t, "") fixture.setProfile(t, "server") fixture.setEnvironment(t) - t.Setenv("THOTHCTL_FAKE_STOPPED_PS", `[{"ID":"core-id","Name":"exact-core","Service":"core","State":"exited"},{"ID":"front-id","Name":"exact-frontend","Service":"frontend","State":"exited"}]`) + t.Setenv("THT_FAKE_STOPPED_PS", `[{"ID":"core-id","Name":"exact-core","Service":"core","State":"exited"},{"ID":"front-id","Name":"exact-frontend","Service":"frontend","State":"exited"}]`) var stdout, stderr bytes.Buffer code := run(context.Background(), []string{ @@ -258,7 +300,7 @@ func TestRunLogsRedactsAnUnlabelledDeclaredSecret(t *testing.T) { t.Fatal(err) } fixture.setEnvironment(t, secretPath) - t.Setenv("THOTHCTL_FAKE_LOG", "fake Docker log: unlabelled-secret") + t.Setenv("THT_FAKE_LOG", "fake Docker log: unlabelled-secret") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "logs"}, &stdout, &stderr) @@ -298,7 +340,7 @@ func TestRunPiLogsRedactsNestedAuthAndBundleScalars(t *testing.T) { } fixture.setEnvContents(t, "PI_AUTH_FILE="+authFile+"\nTHT_SECRETS_FILE="+bundleFile+"\n") t.Setenv( - "THOTHCTL_FAKE_LOG", + "THT_FAKE_LOG", "dummy-canary-pi-log-json dummy-canary-pi-log-nested "+ "dummy-canary-pi-log-model dummy-canary-pi-log-dwh", ) @@ -352,7 +394,7 @@ func TestRunResolvesComposeDotenvCommentsQuotesAndInterpolationForSecretFiles(t "DOUBLE_TOKEN_FILE=\""+doubleQuotedSecret+"\" # Compose comment\n"+ "SINGLE_TOKEN_FILE='"+singleQuotedSecret+"' # Compose comment\n"+ "INTERPOLATED_TOKEN_SOURCE=\"${SECRET_ROOT}/interpolated\"\n") - t.Setenv("THOTHCTL_FAKE_LOG", "inline-secret double-quoted-secret single-quoted-secret interpolated-secret") + t.Setenv("THT_FAKE_LOG", "inline-secret double-quoted-secret single-quoted-secret interpolated-secret") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "logs"}, &stdout, &stderr) @@ -374,9 +416,9 @@ func TestRunRedactsSecretSourceInBothStreams(t *testing.T) { t.Fatal(err) } fixture.setEnvContents(t, "UNLABELLED_SECRET_SOURCE="+secretPath+"\n") - t.Setenv("THOTHCTL_FAKE_LOG", "stdout source-secret") - t.Setenv("THOTHCTL_FAKE_FAILURE", "stderr source-secret") - t.Setenv("THOTHCTL_FAKE_EXIT", "17") + t.Setenv("THT_FAKE_LOG", "stdout source-secret") + t.Setenv("THT_FAKE_FAILURE", "stderr source-secret") + t.Setenv("THT_FAKE_EXIT", "17") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "logs"}, &stdout, &stderr) @@ -396,8 +438,8 @@ func TestRunRedactsSecretWhenDoctorFails(t *testing.T) { t.Fatal(err) } fixture.setEnvContents(t, "DOCTOR_SECRET_FILE="+secretPath+"\n") - t.Setenv("THOTHCTL_FAKE_FAIL_ON", "version") - t.Setenv("THOTHCTL_FAKE_FAILURE", "doctor saw doctor-secret") + t.Setenv("THT_FAKE_FAIL_ON", "version") + t.Setenv("THT_FAKE_FAILURE", "doctor saw doctor-secret") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "doctor"}, &stdout, &stderr) @@ -668,7 +710,7 @@ func TestRunDoctorAcceptsComposeJSONLinesServiceStatus(t *testing.T) { fixture := newCLIFixture(t, "SAFE_VALUE=1\n") fixture.setEnvironment(t) t.Setenv( - "THOTHCTL_FAKE_PS", + "THT_FAKE_PS", "{\"Service\":\"core\",\"State\":\"running\",\"Health\":\"healthy\"}\n"+ "{\"Service\":\"frontend\",\"State\":\"running\",\"Health\":\"healthy\"}", ) @@ -687,7 +729,7 @@ func TestRunDoctorAcceptsComposeJSONLinesServiceStatus(t *testing.T) { func TestRunPreservesChildExitCodes(t *testing.T) { fixture := newCLIFixture(t, "SAFE_VALUE=1\n") fixture.setEnvironment(t) - t.Setenv("THOTHCTL_FAKE_EXIT", "42") + t.Setenv("THT_FAKE_EXIT", "42") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "update", "--check-only"}, &stdout, &stderr) @@ -757,7 +799,7 @@ func TestRunPiRestartSanitizesSuccessOutput(t *testing.T) { t.Fatal(err) } fixture.setEnvironment(t, secretPath) - t.Setenv("THOTHCTL_FAKE_PI_VERSION", "pi-restart-secret") + t.Setenv("THT_FAKE_PI_VERSION", "pi-restart-secret") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{ @@ -886,7 +928,7 @@ func TestRunPiMaintenanceRecoverClearsRestartLifecycleState(t *testing.T) { func TestRunWorkspaceInspectDispatchesThroughTheMaintenanceService(t *testing.T) { fixture := newCLIFixture(t, "SAFE_VALUE=1\n") fixture.setEnvironment(t) - t.Setenv("THOTHCTL_FAKE_WORKSPACE_RESULT", `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"abc","workspaceRevision":"1234567890abcdef1234567890abcdef12345678","descriptorBlob":"sha256:`+strings.Repeat("a", 64)+`","operation":"inspect","completedStages":[]}`) + t.Setenv("THT_FAKE_WORKSPACE_RESULT", `{"schemaVersion":1,"status":"succeeded","code":"ok","workspaceId":"abc","workspaceRevision":"1234567890abcdef1234567890abcdef12345678","descriptorBlob":"sha256:`+strings.Repeat("a", 64)+`","operation":"inspect","completedStages":[]}`) var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "workspace", "inspect", "--workspace", "abc", "--json"}, &stdout, &stderr) @@ -904,7 +946,7 @@ func TestRunWorkspaceInspectDispatchesThroughTheMaintenanceService(t *testing.T) func TestRunWorkspaceBlockedResultsExitThreeAndRenderHumanOutput(t *testing.T) { fixture := newCLIFixture(t, "SAFE_VALUE=1\n") fixture.setEnvironment(t) - t.Setenv("THOTHCTL_FAKE_WORKSPACE_RESULT", `{"schemaVersion":1,"status":"blocked","code":"manual_review_required","workspaceId":"abc","workspaceRevision":"1234567890abcdef1234567890abcdef12345678","descriptorBlob":"sha256:`+strings.Repeat("b", 64)+`","operation":"schema-suggest-fks","completedStages":["dwh"],"warnings":["review required"]}`) + t.Setenv("THT_FAKE_WORKSPACE_RESULT", `{"schemaVersion":1,"status":"blocked","code":"manual_review_required","workspaceId":"abc","workspaceRevision":"1234567890abcdef1234567890abcdef12345678","descriptorBlob":"sha256:`+strings.Repeat("b", 64)+`","operation":"schema-suggest-fks","completedStages":["dwh"],"warnings":["review required"]}`) var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "workspace", "schema", "suggest-fks", "--workspace", "abc"}, &stdout, &stderr) @@ -1012,8 +1054,8 @@ func TestRunPiStatusPreservesDockerExitCodeAndRedactsDiagnostics(t *testing.T) { t.Fatal(err) } fixture.setEnvironment(t, secretPath) - t.Setenv("THOTHCTL_FAKE_FAIL_ON", "version") - t.Setenv("THOTHCTL_FAKE_FAILURE", "pi-status-secret") + t.Setenv("THT_FAKE_FAIL_ON", "version") + t.Setenv("THT_FAKE_FAILURE", "pi-status-secret") var stdout, stderr bytes.Buffer exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "pi", "status"}, &stdout, &stderr) @@ -1042,7 +1084,7 @@ func newCLIFixture(t *testing.T, envTemplate string) cliFixture { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-test-") + root, err := os.MkdirTemp(temporaryRoot, "tht-test-") if err != nil { t.Fatal(err) } @@ -1068,30 +1110,30 @@ func newCLIFixture(t *testing.T, envTemplate string) cliFixture { } argsFile := filepath.Join(root, "docker-args") fakeDocker := `#!/bin/sh -printf '%s\n' "$@" >> "$THOTHCTL_FAKE_ARGS" -printf '%s\n' -- >> "$THOTHCTL_FAKE_ARGS" +printf '%s\n' "$@" >> "$THT_FAKE_ARGS" +printf '%s\n' -- >> "$THT_FAKE_ARGS" case " $* " in - *" ps --all --format json core frontend "*) printf '%s\n' "${THOTHCTL_FAKE_STOPPED_PS:-[]}" ;; + *" ps --all --format json core frontend "*) printf '%s\n' "${THT_FAKE_STOPPED_PS:-[]}" ;; *" config --format json "*) - if [ -n "${THOTHCTL_FAKE_CONFIG:-}" ]; then - printf '%s\n' "$THOTHCTL_FAKE_CONFIG" + if [ -n "${THT_FAKE_CONFIG:-}" ]; then + printf '%s\n' "$THT_FAKE_CONFIG" else printf '%s\n' '{"volumes":{"settings":{}},"services":{"core":{"image":"thothii-core:local","environment":{"THT_LLM_URL":"https://llm.example.invalid"}}}}' fi ;; *" run --rm --no-deps --no-TTY session-migrate "*) - if [ "${THOTHCTL_FAKE_MIGRATION_EXIT:-0}" -ne 0 ]; then - printf '%s\n' "$THOTHCTL_FAKE_MIGRATION_FAILURE" >&2 - exit "$THOTHCTL_FAKE_MIGRATION_EXIT" + if [ "${THT_FAKE_MIGRATION_EXIT:-0}" -ne 0 ]; then + printf '%s\n' "$THT_FAKE_MIGRATION_FAILURE" >&2 + exit "$THT_FAKE_MIGRATION_EXIT" fi printf '%s\n' '{"applied":[],"drifted":[],"pending":[]}' ;; - *" ps --format json "*) printf '%s\n' "$THOTHCTL_FAKE_PS" ;; + *" ps --format json "*) printf '%s\n' "$THT_FAKE_PS" ;; *" ps -q core "*) printf '%s\n' 'core-id' ;; - *" image inspect --format {{.Id}} "*) printf '%s\n' "${THOTHCTL_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;; - *"inspect --format {{.Image}} core-id"*) printf '%s\n' "${THOTHCTL_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;; + *" image inspect --format {{.Id}} "*) printf '%s\n' "${THT_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;; + *"inspect --format {{.Image}} core-id"*) printf '%s\n' "${THT_FAKE_IMAGE_ID:-sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}" ;; *"inspect --format {{json .Mounts}} core-id"*) printf '%s\n' '[{"Type":"volume","Name":"settings","Source":"settings","Destination":"/home/thoth/.pi","RW":true}]' ;; - *"io.thothii.pi.version"*) printf '%s\n' "${THOTHCTL_FAKE_PI_VERSION:-0.80.3}" ;; - *"PI_VERSION"*) printf '%s\n' "${THOTHCTL_FAKE_PI_VERSION:-0.80.3}" ;; - *" pi --version "*) printf '%s\n' "${THOTHCTL_FAKE_PI_VERSION:-0.80.3}" ;; + *"io.thothii.pi.version"*) printf '%s\n' "${THT_FAKE_PI_VERSION:-0.80.3}" ;; + *"PI_VERSION"*) printf '%s\n' "${THT_FAKE_PI_VERSION:-0.80.3}" ;; + *" pi --version "*) printf '%s\n' "${THT_FAKE_PI_VERSION:-0.80.3}" ;; *"/pi-management/options "*) printf '%s\n' '{"providers":["provider"],"models":[{"provider":"provider","id":"model"}],"reasoning":["low","medium","high"]}' ;; *"settings-cli.js --snapshot"*) printf '%s\n' '{"exists":false,"rawBase64":""}' ;; *"/settings "*) printf '%s\n' '{"provider":"provider","model":"model","thinking":"medium"}' ;; @@ -1100,17 +1142,17 @@ case " $* " in *"/internal/maintenance/activate "*) printf '%s\n' '{"active":true,"admissions":0}' ;; *"/internal/maintenance/deactivate "*) printf '%s\n' '{"active":false,"admissions":0}' ;; *"/internal/maintenance/status "*) printf '%s\n' '{"active":true,"admissions":0}' ;; - *" logs "*) printf '%s\n' "$THOTHCTL_FAKE_LOG" ;; - *" run --rm --no-deps --no-TTY "*" workspace-maintenance "*) printf '%s\n' "$THOTHCTL_FAKE_WORKSPACE_RESULT" ;; + *" logs "*) printf '%s\n' "$THT_FAKE_LOG" ;; + *" run --rm --no-deps --no-TTY "*" workspace-maintenance "*) printf '%s\n' "$THT_FAKE_WORKSPACE_RESULT" ;; esac -if [ "${THOTHCTL_FAKE_FAIL_ON:-}" = "version" ]; then - printf '%s\n' "${THOTHCTL_FAKE_FAILURE:-fake Docker failure}" >&2 +if [ "${THT_FAKE_FAIL_ON:-}" = "version" ]; then + printf '%s\n' "${THT_FAKE_FAILURE:-fake Docker failure}" >&2 exit 41 fi -if [ "${THOTHCTL_FAKE_EXIT:-0}" -ne 0 ]; then - printf '%s\n' "${THOTHCTL_FAKE_FAILURE:-fake Docker failure}" >&2 +if [ "${THT_FAKE_EXIT:-0}" -ne 0 ]; then + printf '%s\n' "${THT_FAKE_FAILURE:-fake Docker failure}" >&2 fi -exit "${THOTHCTL_FAKE_EXIT:-0}" +exit "${THT_FAKE_EXIT:-0}" ` if err := os.WriteFile(filepath.Join(pathDirectory, "docker"), []byte(fakeDocker), 0o700); err != nil { t.Fatal(err) @@ -1133,19 +1175,19 @@ func (f cliFixture) setEnvContents(t *testing.T, env string) { t.Fatal(err) } t.Setenv("PATH", f.pathDirectory) - t.Setenv("THOTHCTL_FAKE_ARGS", f.argsFile) - t.Setenv("THOTHCTL_FAKE_EXIT", "0") - t.Setenv("THOTHCTL_FAKE_LOG", "") - t.Setenv("THOTHCTL_FAKE_FAILURE", "") - t.Setenv("THOTHCTL_FAKE_FAIL_ON", "") - t.Setenv("THOTHCTL_FAKE_STOPPED_PS", "[]") - t.Setenv("THOTHCTL_FAKE_PS", `[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"}]`) - t.Setenv("THOTHCTL_FAKE_CONFIG", "") - t.Setenv("THOTHCTL_FAKE_MIGRATION_FAILURE", "") - t.Setenv("THOTHCTL_FAKE_MIGRATION_EXIT", "0") - t.Setenv("THOTHCTL_FAKE_WORKSPACE_RESULT", "") - t.Setenv("THOTHCTL_FAKE_IMAGE_ID", "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") - t.Setenv("THOTHCTL_FAKE_PI_VERSION", "0.80.3") + t.Setenv("THT_FAKE_ARGS", f.argsFile) + t.Setenv("THT_FAKE_EXIT", "0") + t.Setenv("THT_FAKE_LOG", "") + t.Setenv("THT_FAKE_FAILURE", "") + t.Setenv("THT_FAKE_FAIL_ON", "") + t.Setenv("THT_FAKE_STOPPED_PS", "[]") + t.Setenv("THT_FAKE_PS", `[{"Service":"core","State":"running","Health":"healthy"},{"Service":"frontend","State":"running","Health":"healthy"}]`) + t.Setenv("THT_FAKE_CONFIG", "") + t.Setenv("THT_FAKE_MIGRATION_FAILURE", "") + t.Setenv("THT_FAKE_MIGRATION_EXIT", "0") + t.Setenv("THT_FAKE_WORKSPACE_RESULT", "") + t.Setenv("THT_FAKE_IMAGE_ID", "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") + t.Setenv("THT_FAKE_PI_VERSION", "0.80.3") } func (f cliFixture) setProfile(t *testing.T, profile string) { diff --git a/tools/thothctl/go.mod b/tools/tht/go.mod similarity index 89% rename from tools/thothctl/go.mod rename to tools/tht/go.mod index c1663424..647f13db 100644 --- a/tools/thothctl/go.mod +++ b/tools/tht/go.mod @@ -1,4 +1,4 @@ -module github.com/aritmolab/thothii/tools/thothctl +module github.com/aritmolab/thothii/tools/tht go 1.26.0 diff --git a/tools/thothctl/go.sum b/tools/tht/go.sum similarity index 100% rename from tools/thothctl/go.sum rename to tools/tht/go.sum diff --git a/tools/thothctl/internal/compose/runner.go b/tools/tht/internal/compose/runner.go similarity index 100% rename from tools/thothctl/internal/compose/runner.go rename to tools/tht/internal/compose/runner.go diff --git a/tools/thothctl/internal/compose/runner_test.go b/tools/tht/internal/compose/runner_test.go similarity index 100% rename from tools/thothctl/internal/compose/runner_test.go rename to tools/tht/internal/compose/runner_test.go diff --git a/tools/thothctl/internal/config/installation.go b/tools/tht/internal/config/installation.go similarity index 98% rename from tools/thothctl/internal/config/installation.go rename to tools/tht/internal/config/installation.go index 8ac424ec..b0b29e3d 100644 --- a/tools/thothctl/internal/config/installation.go +++ b/tools/tht/internal/config/installation.go @@ -1,4 +1,4 @@ -// Package config loads the non-secret, local installation descriptor used by thothctl. +// Package config loads the non-secret, local installation descriptor used by tht. package config import ( @@ -15,7 +15,7 @@ import ( "strings" "sync" - "github.com/aritmolab/thothii/tools/thothctl/internal/safeio" + "github.com/aritmolab/thothii/tools/tht/internal/safeio" "github.com/compose-spec/compose-go/v2/dotenv" "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -235,7 +235,7 @@ func (i Installation) ComposeFiles() []string { // ControlDirectory contains state that is private to one installation descriptor, even when // multiple installations intentionally share one source checkout. func (i Installation) ControlDirectory() string { - return filepath.Join(i.ProjectDirectory, ".thothctl", i.ProjectName()) + return filepath.Join(i.ProjectDirectory, ".tht", i.ProjectName()) } func (i Installation) CurrentImageOverridePath() string { @@ -284,7 +284,7 @@ func (i Installation) composeArgs(files []string, command ...string) []string { // SecretFiles returns canonical local secret paths declared through *_FILE or *_SOURCE variables. // Compose's dotenv parser resolves comments, quotes, escapes, and interpolation. Unsupported or -// unresolved source interpolation is rejected before thothctl invokes Docker. +// unresolved source interpolation is rejected before tht invokes Docker. func (i Installation) SecretFiles() ([]string, error) { contents, err := safeio.ReadCanonicalRegular(i.EnvFile, maxEnvironmentFileBytes) if err != nil { diff --git a/tools/thothctl/internal/config/installation_test.go b/tools/tht/internal/config/installation_test.go similarity index 98% rename from tools/thothctl/internal/config/installation_test.go rename to tools/tht/internal/config/installation_test.go index 1ac4f34f..30019883 100644 --- a/tools/thothctl/internal/config/installation_test.go +++ b/tools/tht/internal/config/installation_test.go @@ -218,8 +218,8 @@ func TestInstallationControlPathsAreIsolatedForDescriptorsSharingOneCheckout(t * t.Fatalf("shared-checkout installations reused %q", first.CurrentImageOverridePath()) } for _, installation := range []Installation{first, second} { - if filepath.Dir(filepath.Dir(installation.CurrentImageOverridePath())) != filepath.Join(projectDirectory, ".thothctl") { - t.Fatalf("current-image path %q is not installation-specific under .thothctl", installation.CurrentImageOverridePath()) + if filepath.Dir(filepath.Dir(installation.CurrentImageOverridePath())) != filepath.Join(projectDirectory, ".tht") { + t.Fatalf("current-image path %q is not installation-specific under .tht", installation.CurrentImageOverridePath()) } if filepath.Dir(installation.UpdateStatePath()) != filepath.Dir(installation.CurrentImageOverridePath()) { t.Fatalf("state %q and selector %q do not share one installation control directory", installation.UpdateStatePath(), installation.CurrentImageOverridePath()) @@ -246,7 +246,7 @@ func writeInstallation(t *testing.T, profile string) (string, string, string, st if err != nil { t.Fatal(err) } - physicalRoot, err := os.MkdirTemp(temporaryRoot, "thothctl-config-") + physicalRoot, err := os.MkdirTemp(temporaryRoot, "tht-config-") if err != nil { t.Fatal(err) } diff --git a/tools/thothctl/internal/output/sanitize.go b/tools/tht/internal/output/sanitize.go similarity index 98% rename from tools/thothctl/internal/output/sanitize.go rename to tools/tht/internal/output/sanitize.go index 24ae6547..6b2a4c9d 100644 --- a/tools/thothctl/internal/output/sanitize.go +++ b/tools/tht/internal/output/sanitize.go @@ -10,7 +10,7 @@ import ( "sort" "strings" - "github.com/aritmolab/thothii/tools/thothctl/internal/safeio" + "github.com/aritmolab/thothii/tools/tht/internal/safeio" "github.com/compose-spec/compose-go/v2/dotenv" ) diff --git a/tools/thothctl/internal/output/sanitize_test.go b/tools/tht/internal/output/sanitize_test.go similarity index 98% rename from tools/thothctl/internal/output/sanitize_test.go rename to tools/tht/internal/output/sanitize_test.go index 2e227fa0..e96f666f 100644 --- a/tools/thothctl/internal/output/sanitize_test.go +++ b/tools/tht/internal/output/sanitize_test.go @@ -161,7 +161,7 @@ func physicalTempDir(t *testing.T) string { if err != nil { t.Fatal(err) } - directory, err := os.MkdirTemp(root, "thothctl-output-test-") + directory, err := os.MkdirTemp(root, "tht-output-test-") if err != nil { t.Fatal(err) } diff --git a/tools/thothctl/internal/pi/commands.go b/tools/tht/internal/pi/commands.go similarity index 98% rename from tools/thothctl/internal/pi/commands.go rename to tools/tht/internal/pi/commands.go index c775fab0..0be67c6f 100644 --- a/tools/thothctl/internal/pi/commands.go +++ b/tools/tht/internal/pi/commands.go @@ -12,7 +12,7 @@ import ( "regexp" "strings" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/compose" ) var choicePattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._/-]{0,127}$`) @@ -40,9 +40,9 @@ type settingsFileSnapshot struct { } var internalIdentityHeaders = []string{ - "-H", "x-thoth-principal-issuer: thothctl", - "-H", "x-thoth-principal-subject: thothctl-maintenance", - "-H", "x-thoth-principal-display-name: Thothctl maintenance", + "-H", "x-thoth-principal-issuer: tht", + "-H", "x-thoth-principal-subject: tht-maintenance", + "-H", "x-thoth-principal-display-name: Tht maintenance", "-H", "x-thoth-is-admin: 1", } @@ -221,7 +221,7 @@ func readEffectiveSettings(ctx context.Context, runner Runner) ([]byte, error) { return canonical, nil } -// Runner is the narrow, shell-free command boundary shared with thothctl. +// Runner is the narrow, shell-free command boundary shared with tht. type Runner interface { Run(context.Context, []string, io.Reader) (compose.Result, error) } diff --git a/tools/thothctl/internal/pi/commands_test.go b/tools/tht/internal/pi/commands_test.go similarity index 96% rename from tools/thothctl/internal/pi/commands_test.go rename to tools/tht/internal/pi/commands_test.go index c04f5c18..bd96a006 100644 --- a/tools/thothctl/internal/pi/commands_test.go +++ b/tools/tht/internal/pi/commands_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/compose" ) func TestDoctorRequiresExternalEndpointAuthPiStateAndHealth(t *testing.T) { @@ -218,7 +218,7 @@ func TestConfigureCompensationRestoresAbsentAndExactEmptyPriorFiles(t *testing.T } } -// Catches thothctl reading the legacy public model route instead of the admin-only closed Pi +// Catches tht reading the legacy public model route instead of the admin-only closed Pi // Management choices before it writes shared installation defaults. func TestConfigureLoadsDedicatedClosedOptionsWritesRealCoreSettingsAndUsesUpstreamIdentity(t *testing.T) { fake := newFakeRunner() @@ -227,7 +227,7 @@ func TestConfigureLoadsDedicatedClosedOptionsWritesRealCoreSettingsAndUsesUpstre } assertCalled(t, fake.calls, "/pi-management/options") assertCalled(t, fake.calls, "node /app/backend/dist/settings/settings-cli.js --provider provider --model model --thinking medium") - assertCalled(t, fake.calls, "x-thoth-principal-subject: thothctl-maintenance") + assertCalled(t, fake.calls, "x-thoth-principal-subject: tht-maintenance") if got := strings.Join(fake.calls, "\n"); strings.Contains(got, "pi-defaults.json") || strings.Contains(got, "secret") { t.Fatalf("commands=%q", got) } @@ -243,7 +243,7 @@ func TestTestUsesDedicatedSmokeEndpointAndIndependentImageVersionProbe(t *testin if err := Test(context.Background(), fake); err != nil { t.Fatalf("Test() error = %v", err) } - for _, command := range []string{"pi --version", "/pi-management/test", "x-thoth-principal-subject: thothctl-maintenance"} { + for _, command := range []string{"pi --version", "/pi-management/test", "x-thoth-principal-subject: tht-maintenance"} { assertCalled(t, fake.calls, command) } for _, legacy := range []string{"/health", "/models", "/settings"} { @@ -271,7 +271,7 @@ func TestTestRequiresDedicatedSmokeEndpointToReportReady(t *testing.T) { assertCalled(t, fake.calls, "pi --version") } -// Catches thothctl accepting a reasoning level that the backend did not publish as a closed +// Catches tht accepting a reasoning level that the backend did not publish as a closed // installation option, which would bypass the Pi Management validation surface. func TestConfigureRejectsReasoningOutsideDedicatedClosedOptions(t *testing.T) { fake := newFakeRunner() diff --git a/tools/thothctl/internal/pi/durable_unix.go b/tools/tht/internal/pi/durable_unix.go similarity index 100% rename from tools/thothctl/internal/pi/durable_unix.go rename to tools/tht/internal/pi/durable_unix.go diff --git a/tools/thothctl/internal/pi/durable_windows.go b/tools/tht/internal/pi/durable_windows.go similarity index 100% rename from tools/thothctl/internal/pi/durable_windows.go rename to tools/tht/internal/pi/durable_windows.go diff --git a/tools/thothctl/internal/pi/recovery_error.go b/tools/tht/internal/pi/recovery_error.go similarity index 100% rename from tools/thothctl/internal/pi/recovery_error.go rename to tools/tht/internal/pi/recovery_error.go diff --git a/tools/thothctl/internal/pi/restart.go b/tools/tht/internal/pi/restart.go similarity index 100% rename from tools/thothctl/internal/pi/restart.go rename to tools/tht/internal/pi/restart.go diff --git a/tools/thothctl/internal/pi/restart_test.go b/tools/tht/internal/pi/restart_test.go similarity index 99% rename from tools/thothctl/internal/pi/restart_test.go rename to tools/tht/internal/pi/restart_test.go index 2c5651b5..369158ec 100644 --- a/tools/thothctl/internal/pi/restart_test.go +++ b/tools/tht/internal/pi/restart_test.go @@ -93,7 +93,7 @@ func TestRestartPinsCapturedImageWhenConfiguredTagMovesBeforeRecreate(t *testing if result.Version != "0.80.3" || fake.currentImage != "sha256:old" { t.Fatalf("restart result=%+v image=%q; want captured 0.80.3 / sha256:old", result, fake.currentImage) } - assertCalled(t, fake.calls, "image tag sha256:old thothii-core:thothctl-") + assertCalled(t, fake.calls, "image tag sha256:old thothii-core:tht-") assertCalled(t, fake.calls, "pi-lifecycle-") if matches, globErr := filepath.Glob(filepath.Join(dir, "pi-lifecycle-*.yaml")); globErr != nil || len(matches) != 0 { t.Fatalf("successful restart overrides = %v, error = %v; want safe cleanup", matches, globErr) @@ -285,7 +285,7 @@ func TestRecoverLifecycleMaintenanceVerifiesAndClearsRestartState(t *testing.T) delete(fake.tags, restartState.Previous.Reference) fake.tags[fake.configuredImage] = "sha256:moved-before-recovery" candidate := previous - candidate.Reference = "thothii-core:thothctl-recover-candidate" + candidate.Reference = "thothii-core:tht-recover-candidate" writeStateForTest(t, updateStatePath, State{ Transaction: "update-recovery", Phase: PhasePromoting, diff --git a/tools/thothctl/internal/pi/state.go b/tools/tht/internal/pi/state.go similarity index 100% rename from tools/thothctl/internal/pi/state.go rename to tools/tht/internal/pi/state.go diff --git a/tools/thothctl/internal/pi/state_test.go b/tools/tht/internal/pi/state_test.go similarity index 90% rename from tools/thothctl/internal/pi/state_test.go rename to tools/tht/internal/pi/state_test.go index 772b3dc9..2af63752 100644 --- a/tools/thothctl/internal/pi/state_test.go +++ b/tools/tht/internal/pi/state_test.go @@ -40,15 +40,15 @@ func TestUpdateAndRestartStatePathsShareOneLifecycleLock(t *testing.T) { func TestAdvisoryLockCrashReleasesAndReacquires(t *testing.T) { statePath := filepath.Join(t.TempDir(), "update-state.json") - if os.Getenv("THOTHCTL_LOCK_CRASH_HELPER") == "1" { - lock, err := acquireLock(os.Getenv("THOTHCTL_LOCK_STATE")) + if os.Getenv("THT_LOCK_CRASH_HELPER") == "1" { + lock, err := acquireLock(os.Getenv("THT_LOCK_STATE")) if err != nil || lock == nil { os.Exit(23) } os.Exit(0) // Deliberately bypass Release: the OS must release ownership. } command := exec.Command(os.Args[0], "-test.run=^TestAdvisoryLockCrashReleasesAndReacquires$") - command.Env = append(os.Environ(), "THOTHCTL_LOCK_CRASH_HELPER=1", "THOTHCTL_LOCK_STATE="+statePath) + command.Env = append(os.Environ(), "THT_LOCK_CRASH_HELPER=1", "THT_LOCK_STATE="+statePath) if output, err := command.CombinedOutput(); err != nil { t.Fatalf("crash helper failed: %v: %s", err, output) } diff --git a/tools/thothctl/internal/pi/update.go b/tools/tht/internal/pi/update.go similarity index 99% rename from tools/thothctl/internal/pi/update.go rename to tools/tht/internal/pi/update.go index edb33f04..9f71d6c8 100644 --- a/tools/thothctl/internal/pi/update.go +++ b/tools/tht/internal/pi/update.go @@ -15,7 +15,7 @@ import ( "strings" "time" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/compose" "github.com/distribution/reference" ) @@ -748,7 +748,7 @@ func lifecycleTransaction(statePath string) string { } func lifecycleImageTag(transaction, role string) string { - return "thothii-core:thothctl-" + transaction + "-" + role + return "thothii-core:tht-" + transaction + "-" + role } func lifecycleOverridePath(statePath, transaction string) string { diff --git a/tools/thothctl/internal/pi/update_test.go b/tools/tht/internal/pi/update_test.go similarity index 97% rename from tools/thothctl/internal/pi/update_test.go rename to tools/tht/internal/pi/update_test.go index 2923d275..475e730c 100644 --- a/tools/thothctl/internal/pi/update_test.go +++ b/tools/tht/internal/pi/update_test.go @@ -11,7 +11,7 @@ import ( "strings" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/compose" ) func TestActiveSessionsParsesAuthenticatedBackendBareArrayFixture(t *testing.T) { @@ -58,7 +58,7 @@ func TestUpdateBuildsPinnedVersionRecreatesOnlyCoreAndPersistsRecoveryState(t *t fake := newFakeRunner() dir := t.TempDir() result, err := Update(context.Background(), fake, Request{ - StatePath: filepath.Join(dir, ".thothctl", "update-state.json"), + StatePath: filepath.Join(dir, ".tht", "update-state.json"), Version: "0.81.0", Source: BuildSource, Confirm: true, @@ -85,11 +85,11 @@ func TestUpdateBuildsPinnedVersionRecreatesOnlyCoreAndPersistsRecoveryState(t *t func TestUpdateUsesATransactionScopedComposeOverrideWithoutMutatingTheConfiguredImage(t *testing.T) { fake := newFakeRunner() - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") if _, err := Update(context.Background(), fake, Request{StatePath: statePath, Version: "0.81.0", Source: BuildSource, Confirm: true}); err != nil { t.Fatal(err) } - if fake.buildReference == "" || fake.buildReference == fake.configuredImage || !strings.Contains(fake.buildReference, "thothctl-") { + if fake.buildReference == "" || fake.buildReference == fake.configuredImage || !strings.Contains(fake.buildReference, "tht-") { t.Fatalf("build reference = %q, configured = %q; want unique lifecycle tag", fake.buildReference, fake.configuredImage) } assertNotCalled(t, fake.calls, "image tag sha256:old "+fake.configuredImage) @@ -103,7 +103,7 @@ func TestUpdateUsesATransactionScopedComposeOverrideWithoutMutatingTheConfigured func TestSuccessfulUpdateAndRollbackRemainSelectedOnFreshRecreate(t *testing.T) { fake := newFakeRunner() - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") if _, err := Update(context.Background(), fake, Request{StatePath: statePath, Version: "0.81.0", Source: BuildSource, Confirm: true}); err != nil { t.Fatal(err) } @@ -205,7 +205,7 @@ func TestDigestPinnedConfiguredImageIsNeverUsedAsARollbackTagTarget(t *testing.T fake := newFakeRunner() fake.configuredImage = "registry.example.invalid/core@sha256:" + strings.Repeat("b", 64) fake.tags = map[string]string{fake.configuredImage: "sha256:old"} - statePath := filepath.Join(t.TempDir(), ".thothctl", "state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "state.json") if _, err := Update(context.Background(), fake, Request{StatePath: statePath, Version: "0.81.0", Source: BuildSource, Confirm: true}); err != nil { t.Fatal(err) } @@ -343,7 +343,7 @@ func TestCompensationReactivatesMaintenanceAndRescansBeforeRollback(t *testing.T func TestAutomaticRollbackSurvivesADeadCandidateCore(t *testing.T) { fake := newFakeRunner() fake.fail = "dead-candidate" - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") result, err := Update(context.Background(), fake, Request{ StatePath: statePath, @@ -367,9 +367,9 @@ func TestAutomaticRollbackSurvivesADeadCandidateCore(t *testing.T) { func TestManualRollbackSurvivesADeadCandidateCore(t *testing.T) { fake := newFakeRunner() - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") previous := stateImageForTest(t, fake) - previous.Reference = "thothii-core:thothctl-dead-candidate-previous" + previous.Reference = "thothii-core:tht-dead-candidate-previous" fake.tags[previous.Reference] = previous.ID writeStateForTest(t, statePath, State{ Transaction: "dead-candidate", @@ -405,7 +405,7 @@ func TestUpdatePullsOnlyDigestPinnedSource(t *testing.T) { t.Fatalf("Update() pull error = %v", err) } assertCalled(t, fake.calls, "pull "+digest) - assertCalled(t, fake.calls, "image tag "+digest+" thothii-core:thothctl-") + assertCalled(t, fake.calls, "image tag "+digest+" thothii-core:tht-") assertNotCalled(t, fake.calls, "image tag "+digest+" thothii-core:local") fake = newFakeRunner() @@ -442,7 +442,7 @@ func TestUpdateRollsBackAfterPostRecreateFailures(t *testing.T) { if result.Phase != PhaseRolledBack { t.Fatalf("phase = %q, want %q", result.Phase, PhaseRolledBack) } - assertCalled(t, fake.calls, "image tag sha256:old thothii-core:thothctl-") + assertCalled(t, fake.calls, "image tag sha256:old thothii-core:tht-") assertNotCalled(t, fake.calls, "image tag sha256:old thothii-core:local") assertCalled(t, fake.calls, "up --detach --wait --wait-timeout 45 --no-deps --force-recreate core") if got := string(readStateBytes(t, statePath)); !strings.Contains(got, `"phase": "rolled_back"`) { @@ -613,11 +613,11 @@ func TestRecoverMaintenanceCompletesAnInterruptedDurablePromotion(t *testing.T) fake.version = "0.81.0" fake.expectedVersion = "0.81.0" fake.labelVersion = "0.81.0" - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") previous := stateImageForTest(t, newFakeRunner()) candidate := previous candidate.ID = "sha256:candidate" - candidate.Reference = "thothii-core:thothctl-recover-candidate" + candidate.Reference = "thothii-core:tht-recover-candidate" fake.tags[candidate.Reference] = candidate.ID state := State{ Transaction: "promotion-recovery", @@ -647,7 +647,7 @@ func TestRollbackFinalStateWriteFailureKeepsMaintenanceAndOverrideForRecovery(t fake := newFakeRunner() statePath := filepath.Join(t.TempDir(), "state.json") previous := stateImageForTest(t, fake) - previous.Reference = "thothii-core:thothctl-rollback-test-previous" + previous.Reference = "thothii-core:tht-rollback-test-previous" fake.tags[previous.Reference] = previous.ID writeStateForTest(t, statePath, State{ Transaction: "rollback-test", @@ -700,7 +700,7 @@ func TestCandidateBuildAndPullFailuresRemainPreMutationAndNeverRecreateCore(t *t t.Run(testCase.name, func(t *testing.T) { fake := newFakeRunner() fake.fail = testCase.failure - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") result, err := Update(context.Background(), fake, Request{StatePath: statePath, Version: "0.81.0", Source: testCase.source, Image: testCase.image, Confirm: true}) if err == nil { t.Fatal("Update() error = nil, want preparation failure") @@ -727,7 +727,7 @@ func TestSuccessfulCompensationPreservesTheOriginalTypedCause(t *testing.T) { for _, cause := range []error{ErrActiveSessions, ErrInterruptedUpdate} { fake := newFakeRunner() fake.maintenance = true - statePath := filepath.Join(t.TempDir(), ".thothctl", "update-state.json") + statePath := filepath.Join(t.TempDir(), ".tht", "update-state.json") state := State{Transaction: "typed-cause", Phase: PhaseRecreated, MutationStarted: true, Previous: stateImageForTest(t, fake)} result, err, clear := compensate(context.Background(), fake, statePath, lifecycleOverridePath(statePath, state.Transaction), state, cause, defaultLifecycleHooks) if result.Phase != PhaseRolledBack || !clear { @@ -780,7 +780,7 @@ func TestRollbackRestoresInterruptedOrPreviouslyRecordedState(t *testing.T) { t.Fatal(err) } previous.ConfigurationSHA = configured.ConfigurationSHA - previous.Reference = "thothii-core:thothctl-test-previous" + previous.Reference = "thothii-core:tht-test-previous" fake.tags[previous.Reference] = previous.ID writeStateForTest(t, statePath, State{Version: 1, Phase: PhaseRecreated, Previous: previous}) result, err := Rollback(context.Background(), fake, statePath, pairedRestartStatePath(statePath), true) @@ -790,7 +790,7 @@ func TestRollbackRestoresInterruptedOrPreviouslyRecordedState(t *testing.T) { if result.Phase != PhaseRolledBack { t.Fatalf("phase = %q, want %q", result.Phase, PhaseRolledBack) } - assertCalled(t, fake.calls, "image tag sha256:old thothii-core:thothctl-test-previous") + assertCalled(t, fake.calls, "image tag sha256:old thothii-core:tht-test-previous") assertCalled(t, fake.calls, "up --detach --wait --wait-timeout 45 --no-deps --force-recreate core") } @@ -1349,6 +1349,6 @@ func stateImageForTest(t *testing.T, fake *fakeRunner) Image { t.Fatal(err) } image.ConfigurationSHA = configured.ConfigurationSHA - image.Reference = "thothii-core:thothctl-test-previous" + image.Reference = "thothii-core:tht-test-previous" return image } diff --git a/tools/thothctl/internal/safeio/files.go b/tools/tht/internal/safeio/files.go similarity index 100% rename from tools/thothctl/internal/safeio/files.go rename to tools/tht/internal/safeio/files.go diff --git a/tools/thothctl/internal/safeio/files_test.go b/tools/tht/internal/safeio/files_test.go similarity index 91% rename from tools/thothctl/internal/safeio/files_test.go rename to tools/tht/internal/safeio/files_test.go index 8c510113..e8a2e81b 100644 --- a/tools/thothctl/internal/safeio/files_test.go +++ b/tools/tht/internal/safeio/files_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/testsupport" + "github.com/aritmolab/thothii/tools/tht/internal/testsupport" ) func TestReadCanonicalRegularRejectsFinalAndParentSymlinks(t *testing.T) { @@ -14,7 +14,7 @@ func TestReadCanonicalRegularRejectsFinalAndParentSymlinks(t *testing.T) { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-safeio-") + root, err := os.MkdirTemp(temporaryRoot, "tht-safeio-") if err != nil { t.Fatal(err) } @@ -47,7 +47,7 @@ func TestReadCanonicalUTF8RejectsNonUTF8AndHardlinks(t *testing.T) { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-safeio-") + root, err := os.MkdirTemp(temporaryRoot, "tht-safeio-") if err != nil { t.Fatal(err) } @@ -79,7 +79,7 @@ func TestWriteCanonicalNewFileRejectsExistingTargets(t *testing.T) { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-safeio-") + root, err := os.MkdirTemp(temporaryRoot, "tht-safeio-") if err != nil { t.Fatal(err) } diff --git a/tools/thothctl/internal/safeio/files_unix.go b/tools/tht/internal/safeio/files_unix.go similarity index 96% rename from tools/thothctl/internal/safeio/files_unix.go rename to tools/tht/internal/safeio/files_unix.go index 05e999d2..773d7992 100644 --- a/tools/thothctl/internal/safeio/files_unix.go +++ b/tools/tht/internal/safeio/files_unix.go @@ -41,7 +41,7 @@ func ReadCanonicalRegular(path string, maximum int64) ([]byte, error) { if err != nil { return nil, ErrUnsafeFile } - file := os.NewFile(uintptr(descriptor), "thothctl-safeio") + file := os.NewFile(uintptr(descriptor), "tht-safeio") if file == nil { unix.Close(descriptor) return nil, ErrUnsafeFile diff --git a/tools/thothctl/internal/safeio/files_unix_test.go b/tools/tht/internal/safeio/files_unix_test.go similarity index 91% rename from tools/thothctl/internal/safeio/files_unix_test.go rename to tools/tht/internal/safeio/files_unix_test.go index 30aebf53..a8911396 100644 --- a/tools/thothctl/internal/safeio/files_unix_test.go +++ b/tools/tht/internal/safeio/files_unix_test.go @@ -16,7 +16,7 @@ func TestReadCanonicalRegularRejectsNamedPipeWithoutBlocking(t *testing.T) { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-safeio-") + root, err := os.MkdirTemp(temporaryRoot, "tht-safeio-") if err != nil { t.Fatal(err) } diff --git a/tools/thothctl/internal/safeio/files_windows.go b/tools/tht/internal/safeio/files_windows.go similarity index 98% rename from tools/thothctl/internal/safeio/files_windows.go rename to tools/tht/internal/safeio/files_windows.go index 8227c71e..abb8952a 100644 --- a/tools/thothctl/internal/safeio/files_windows.go +++ b/tools/tht/internal/safeio/files_windows.go @@ -47,7 +47,7 @@ func ReadCanonicalRegular(path string, maximum int64) ([]byte, error) { if err != nil { return nil, ErrUnsafeFile } - file := os.NewFile(uintptr(handle), "thothctl-safeio") + file := os.NewFile(uintptr(handle), "tht-safeio") if file == nil { windows.CloseHandle(handle) return nil, ErrUnsafeFile diff --git a/tools/thothctl/internal/safeio/files_windows_test.go b/tools/tht/internal/safeio/files_windows_test.go similarity index 100% rename from tools/thothctl/internal/safeio/files_windows_test.go rename to tools/tht/internal/safeio/files_windows_test.go diff --git a/tools/thothctl/internal/safeio/linkcount_unix.go b/tools/tht/internal/safeio/linkcount_unix.go similarity index 100% rename from tools/thothctl/internal/safeio/linkcount_unix.go rename to tools/tht/internal/safeio/linkcount_unix.go diff --git a/tools/thothctl/internal/safeio/linkcount_windows.go b/tools/tht/internal/safeio/linkcount_windows.go similarity index 100% rename from tools/thothctl/internal/safeio/linkcount_windows.go rename to tools/tht/internal/safeio/linkcount_windows.go diff --git a/tools/thothctl/internal/serverops/operations.go b/tools/tht/internal/serverops/operations.go similarity index 98% rename from tools/thothctl/internal/serverops/operations.go rename to tools/tht/internal/serverops/operations.go index 67cbed10..cee9eb28 100644 --- a/tools/thothctl/internal/serverops/operations.go +++ b/tools/tht/internal/serverops/operations.go @@ -12,8 +12,8 @@ import ( "strconv" "strings" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" ) var ( diff --git a/tools/thothctl/internal/serverops/operations_test.go b/tools/tht/internal/serverops/operations_test.go similarity index 98% rename from tools/thothctl/internal/serverops/operations_test.go rename to tools/tht/internal/serverops/operations_test.go index 22e219b2..3ccc21b0 100644 --- a/tools/thothctl/internal/serverops/operations_test.go +++ b/tools/tht/internal/serverops/operations_test.go @@ -11,8 +11,8 @@ import ( "strings" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" ) type fakeRunner struct { @@ -298,7 +298,7 @@ func testInstallation(t *testing.T) config.Installation { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-serverops-") + root, err := os.MkdirTemp(temporaryRoot, "tht-serverops-") if err != nil { t.Fatal(err) } diff --git a/tools/thothctl/internal/testsupport/symlink.go b/tools/tht/internal/testsupport/symlink.go similarity index 85% rename from tools/thothctl/internal/testsupport/symlink.go rename to tools/tht/internal/testsupport/symlink.go index ad499ad1..891da7be 100644 --- a/tools/thothctl/internal/testsupport/symlink.go +++ b/tools/tht/internal/testsupport/symlink.go @@ -1,4 +1,4 @@ -// Package testsupport provides portable helpers shared by thothctl tests. +// Package testsupport provides portable helpers shared by tht tests. package testsupport import ( diff --git a/tools/thothctl/internal/testsupport/symlink_nonwindows.go b/tools/tht/internal/testsupport/symlink_nonwindows.go similarity index 100% rename from tools/thothctl/internal/testsupport/symlink_nonwindows.go rename to tools/tht/internal/testsupport/symlink_nonwindows.go diff --git a/tools/thothctl/internal/testsupport/symlink_test.go b/tools/tht/internal/testsupport/symlink_test.go similarity index 100% rename from tools/thothctl/internal/testsupport/symlink_test.go rename to tools/tht/internal/testsupport/symlink_test.go diff --git a/tools/thothctl/internal/testsupport/symlink_windows.go b/tools/tht/internal/testsupport/symlink_windows.go similarity index 100% rename from tools/thothctl/internal/testsupport/symlink_windows.go rename to tools/tht/internal/testsupport/symlink_windows.go diff --git a/tools/thothctl/internal/testsupport/symlink_windows_test.go b/tools/tht/internal/testsupport/symlink_windows_test.go similarity index 100% rename from tools/thothctl/internal/testsupport/symlink_windows_test.go rename to tools/tht/internal/testsupport/symlink_windows_test.go diff --git a/tools/thothctl/internal/workspaceops/operations.go b/tools/tht/internal/workspaceops/operations.go similarity index 99% rename from tools/thothctl/internal/workspaceops/operations.go rename to tools/tht/internal/workspaceops/operations.go index 7ff6f848..50c5e2ce 100644 --- a/tools/thothctl/internal/workspaceops/operations.go +++ b/tools/tht/internal/workspaceops/operations.go @@ -15,9 +15,9 @@ import ( "sort" "strings" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" - "github.com/aritmolab/thothii/tools/thothctl/internal/safeio" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/safeio" ) var ( diff --git a/tools/thothctl/internal/workspaceops/operations_test.go b/tools/tht/internal/workspaceops/operations_test.go similarity index 98% rename from tools/thothctl/internal/workspaceops/operations_test.go rename to tools/tht/internal/workspaceops/operations_test.go index d1fc5d5a..04c11f2b 100644 --- a/tools/thothctl/internal/workspaceops/operations_test.go +++ b/tools/tht/internal/workspaceops/operations_test.go @@ -12,8 +12,8 @@ import ( "strings" "testing" - "github.com/aritmolab/thothii/tools/thothctl/internal/compose" - "github.com/aritmolab/thothii/tools/thothctl/internal/config" + "github.com/aritmolab/thothii/tools/tht/internal/compose" + "github.com/aritmolab/thothii/tools/tht/internal/config" ) type fakeRunner struct { @@ -167,7 +167,7 @@ func testInstallation(t *testing.T) config.Installation { if err != nil { t.Fatal(err) } - root, err := os.MkdirTemp(temporaryRoot, "thothctl-workspaceops-") + root, err := os.MkdirTemp(temporaryRoot, "tht-workspaceops-") if err != nil { t.Fatal(err) }