From 4d9dadc8e34a0ae62d9e2723b18d8a747fe920a5 Mon Sep 17 00:00:00 2001 From: FlanChanXwO Date: Wed, 26 Aug 2026 22:23:52 +0800 Subject: [PATCH] chore: remove unused repository artifacts --- .dockerignore | 4 - .gitignore | 11 +- goals/docker-container-release/GOAL.md | 111 ------------------ .../phases/phase-1.md | 38 ------ .../phases/phase-2.md | 39 ------ .../phases/phase-3.md | 41 ------- .../phases/phase-4.md | 39 ------ .../phases/phase-5.md | 37 ------ internal/architecture/architecture_test.go | 105 ----------------- 9 files changed, 3 insertions(+), 422 deletions(-) delete mode 100644 goals/docker-container-release/GOAL.md delete mode 100644 goals/docker-container-release/phases/phase-1.md delete mode 100644 goals/docker-container-release/phases/phase-2.md delete mode 100644 goals/docker-container-release/phases/phase-3.md delete mode 100644 goals/docker-container-release/phases/phase-4.md delete mode 100644 goals/docker-container-release/phases/phase-5.md delete mode 100644 internal/architecture/architecture_test.go diff --git a/.dockerignore b/.dockerignore index 07642ccd..d0c08fc0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -38,10 +38,6 @@ internal/media/ugoira/rust/target/ # Worktrees .worktrees/ -# Goal files (internal planning, not needed for container build) -goal-*/ -goals/ - # Templates (Homebrew-specific, not needed for container build) templates/ diff --git a/.gitignore b/.gitignore index 2f7fc632..5888d474 100644 --- a/.gitignore +++ b/.gitignore @@ -12,14 +12,9 @@ coverage.txt # Runtime outputs and local data downloads/ -# Structured E2E evidence may use `downloads` as a case group; only its text -# reports are stored there, while real downloaded media remains outside Git. -!output/runs/**/cases/downloads/ -!output/runs/**/cases/downloads/*/ -output/runs/**/cases/downloads/*/* -!output/runs/**/cases/downloads/*/report.md -!output/runs/**/cases/downloads/*/stdout.txt -!output/runs/**/cases/downloads/*/stderr.txt +/output/ +/goals/ +/goal-*/ *.db *.sqlite *.sqlite3 diff --git a/goals/docker-container-release/GOAL.md b/goals/docker-container-release/GOAL.md deleted file mode 100644 index 7f8da69a..00000000 --- a/goals/docker-container-release/GOAL.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -goal_id: docker-container-release -title: "Ship Docker as a first-class pixiv-cli release target" -status: PLANNED -planning_level: phase -iteration: 0 -max_iterations: 50 -current_phase: 1 -phases_total: 5 -started_at: null -last_evaluation: null -blocker: null -active_step: "phase-1 step 1" -execution_mode: tdd -last_memory_checkpoint: 0 -max_hours: null ---- - -# GOAL: Ship Docker as a first-class pixiv-cli release target - -## Objective - -Add official Docker container support to `pixiv-cli` as a first-class distribution target. Container builds must start from the same immutable release tag as the existing native production builds, run in parallel with the native production build path after the shared release quality gates, produce native `linux/amd64` and `linux/arm64` images, and publish a multi-architecture image to GHCR as part of the tagged release workflow. - -The container distribution must preserve the existing product model instead of creating a Docker-specific product: the same `pixiv` binary, the same `~/.pixiv-cli` state namespace, the same CLI/MCP behavior, and the same release provenance and validation expectations apply. - -## Repository facts that constrain the goal - -- Release source is an immutable `v*` tag and the existing release policy is fail-closed. -- Production builds require `CGO_ENABLED=1` and the committed Rust ugoira static library. -- Linux release compatibility is explicitly tied to glibc 2.35 and native `ubuntu-22.04` / `ubuntu-22.04-arm` runners. -- User state lives under `~/.pixiv-cli`; downloads already support an explicit output path. -- The repository has no `.cursor/goal.config.yml`, so evidence commands for this goal are derived from `AGENTS.md`, maintainer docs, and the existing release/documentation gates. - -## Scope decisions - -- Registry: GitHub Container Registry (`ghcr.io/flanchanxwo/pixiv-cli`). -- Architectures: `linux/amd64` and `linux/arm64` only for the first container release. -- Runtime base: a glibc-based Debian slim image pinned by immutable digest; Alpine/musl and `scratch` are out of scope. -- Builds use native Linux runners; QEMU cross-build is out of scope. -- Container build jobs must not receive registry write permission. They produce verified image artifacts; registry publication happens in a separate publish job. -- Stable releases publish `vX.Y.Z` and advance `latest`; prereleases publish only the exact version tag and must not advance `latest`. -- Container upgrades are performed by pulling a newer image. This goal documents that rule but does not change `pixiv update` behavior. - -## Non-goals - -- No Docker-specific authentication protocol or token storage format. -- No rewrite of `auth login`, OAuth callback handling, or MCP transport. -- No Docker Hub publication. -- No Kubernetes/Helm/Compose deployment layer. -- No Alpine/musl support, QEMU build path, or additional CPU architectures. -- No new Go runtime dependency solely for Docker support. -- No change to self-update implementation in this goal. -- No versioned changelog entry during this implementation PR; release notes remain release-preparation work per repository policy. - -## Completion Criteria (ALL must be satisfied) - -- [ ] C1: The release policy formally recognizes container build/publish jobs, preserves immutable-tag provenance, keeps registry write permission out of build jobs, and fails closed if the Docker release contract drifts. -- [ ] C2: A production container image can be built from the versioned native Linux binary with a pinned glibc runtime base, runs as a non-root user, preserves the `~/.pixiv-cli` state path, uses `/work` as the working directory, and reports the exact release version. -- [ ] C3: Tagged releases build `linux/amd64` and `linux/arm64` container images on native runners in parallel with native production builds and publish a GHCR multi-arch image with correct stable/prerelease tag semantics and OCI provenance labels. -- [ ] C4: English and Simplified Chinese user/maintainer documentation describe Docker installation, persistent state, download bind mounts, stdin-based `auth import`, MCP stdio usage, release tagging, and pull-based upgrades without claiming Docker-specific product behavior. -- [ ] C5: Focused container/release tests, documentation tests, repository Go tests, release workflow policy validation, diff checks, and the credential-free container smoke workflow all pass with fresh evidence. - -## Evidence Required - -| Criterion | Verification command / evidence | Evidence location | -|-----------|---------------------------------|-------------------| -| C1 | `go test ./scripts/internal/releaseworkflow -count=1` and `go run ./scripts/cmd/releaseworkflow --workflow .github/workflows/release.yml` | Test output + Progress Log | -| C2 | `go test ./scripts/tests/containerrelease -count=1` plus native Docker smoke: `pixiv --version`, `pixiv config path`, and non-root user assertion | Test output + container smoke CI log | -| C3 | `go test ./scripts/tests/containerrelease -count=1` plus successful `linux/amd64` and `linux/arm64` jobs in the credential-free container smoke workflow; release policy statically proves GHCR permissions/tag rules | Test output + GitHub Actions run | -| C4 | `go test ./scripts/tests/documentation -count=1` | Test output | -| C5 | `go test ./...`, `sh scripts/test-package-release.sh`, `go run ./scripts/cmd/releaseworkflow --workflow .github/workflows/release.yml`, `go test ./scripts/tests/documentation -count=1`, `git diff --check`, and required GitHub CI checks | Local/CI verification logs | - -## Budget Limits - -- Max iterations: 50 Goal Mode iterations. -- Max hours: unset; use host/session limits rather than inventing a repository-specific wall-clock cap. - -These are agent-execution controls only and must not become product runtime timeouts, retry limits, truncation rules, or hidden fallbacks. - -## Master Plan - -| Phase | Name | Status | Plan file | Exit criterion | -|-------|------|--------|-----------|----------------| -| 1 | Encode the release contract | pending | `phases/phase-1.md` | C1 passes with a witnessed Red → Green policy test cycle | -| 2 | Build the container runtime contract | pending | `phases/phase-2.md` | C2 passes on a real Docker image smoke build | -| 3 | Integrate native multi-arch build and GHCR publication | pending | `phases/phase-3.md` | C3 passes without registry write permission in build jobs | -| 4 | Document the supported Docker UX | pending | `phases/phase-4.md` | C4 passes and English/Chinese semantics match | -| 5 | Run integrated verification and review | pending | `phases/phase-5.md` | C5 is green with fresh evidence and no unresolved blocking review findings | - -## Current Execution Context - -- **Planning level**: phase -- **Current phase**: 1 -- **Active plan file**: `phases/phase-1.md` -- **Active step**: phase-1 step 1 - -## Release consistency boundary - -GitHub Release and GHCR are separate publication systems and cannot be made transactionally atomic. The implementation must keep container **build** failures ahead of GitHub Release publication by making verified container artifacts a prerequisite of the release publish path. Registry push occurs in a dedicated post-Release job with only `packages: write`; a push failure leaves the release workflow failed and must be recoverable by rerunning container publication without rebuilding or resigning the native release. - -The implementation must document this recovery boundary rather than hiding it behind retries or pretending cross-service rollback exists. - -## Progress Log - -### Iteration 0 — intake and planning completed - -- **Action**: Converted the Docker release brainstorm into a Goal Mode contract and five verifiable phases. -- **Status**: PLANNED -- **Evidence basis**: Existing repository release policy, native Linux/Rust build contract, configuration path contract, documentation rules, and Goal Mode GOAL/PHASE format. -- **Next step**: Phase 1 Step 1 — add a failing release-policy test that expresses the container job contract before editing the release workflow implementation. diff --git a/goals/docker-container-release/phases/phase-1.md b/goals/docker-container-release/phases/phase-1.md deleted file mode 100644 index 4df1ba4b..00000000 --- a/goals/docker-container-release/phases/phase-1.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -phase_id: 1 -goal_id: docker-container-release -name: "Encode the release contract" -status: pending -criterion_id: C1 ---- - -# Phase 1: Encode the release contract - -## Objective - -Extend the repository's fail-closed release policy so Docker is an audited release target rather than an unaudited side workflow. - -## Exit Criterion - -C1 is satisfied only when the release policy tests and policy command prove the container job topology, immutable-tag source, native Linux runner mapping, and least-privilege registry boundary. - -## Dependencies - -- Existing `.github/workflows/release.yml` contract. -- Existing `scripts/internal/releaseworkflow` policy and tests. -- Existing Linux release target/toolchain contract. - -## Plan - -- [ ] **Step 1 — Red:** Add focused release-policy tests that require the planned container jobs and reject missing jobs, wrong `needs`, movable source refs, registry permission in build jobs, QEMU/cross-build drift, and prerelease `latest` publication. Run the focused tests and confirm they fail because current behavior lacks the container contract. -- [ ] **Step 2 — Green:** Extend `scripts/internal/releaseworkflow` with the smallest container-policy rules needed to satisfy the failing tests. Reuse existing workflow YAML helpers and Linux target provenance instead of creating a second parser or duplicated policy framework. -- [ ] **Step 3 — Refactor:** Consolidate only genuinely shared Linux release target metadata if duplication is now stable and harmful; do not generalize unrelated release policy. -- [ ] **Step 4 — Verify:** Run `go test ./scripts/internal/releaseworkflow -count=1` and `go run ./scripts/cmd/releaseworkflow --workflow .github/workflows/release.yml`. Record both the initial Red evidence and final Green evidence in the Goal progress log. -- [ ] **Step 5 — Scope audit:** Confirm no production behavior, auth behavior, updater behavior, or registry credential has been introduced in this phase. - -## Phase Progress Log - -### Phase iteration 0 — plan created - -- **Status**: pending -- **Next step**: Step 1 — write and run the failing policy tests first. diff --git a/goals/docker-container-release/phases/phase-2.md b/goals/docker-container-release/phases/phase-2.md deleted file mode 100644 index cd157a09..00000000 --- a/goals/docker-container-release/phases/phase-2.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -phase_id: 2 -goal_id: docker-container-release -name: "Build the container runtime contract" -status: pending -criterion_id: C2 ---- - -# Phase 2: Build the container runtime contract - -## Objective - -Create the minimal production Docker packaging layer around the existing versioned Linux binary without creating a new compile/runtime product model. - -## Exit Criterion - -C2 is satisfied only when a real image build proves the exact release version, non-root execution, glibc runtime compatibility, expected home/state path, and `/work` working directory. - -## Dependencies - -- Phase 1 complete. -- Existing Linux production build and ugoira staticlib contract. -- Existing `internal/config/paths` behavior. - -## Plan - -- [ ] **Step 1 — Red:** Add `scripts/tests/containerrelease` tests defining the Dockerfile/package contract: immutable base digest, no Alpine/musl/scratch base, non-root final user, expected `HOME`, `/work`, `ENTRYPOINT`, OCI metadata inputs, and no embedded secret/state files. Run the focused tests and witness failure before creating the Dockerfile. -- [ ] **Step 2 — Green:** Add a minimal `Dockerfile` that copies a prebuilt versioned `pixiv` binary into a pinned Debian slim runtime, installs only required runtime material such as CA certificates, creates a dedicated non-root user, sets `HOME=/home/pixiv`, `WORKDIR /work`, and `ENTRYPOINT ["/usr/local/bin/pixiv"]`. -- [ ] **Step 3 — Context hygiene:** Add `.dockerignore` that excludes repository-only/build-noise content without excluding files required by the packaging contract. Do not add secret-specific guesses; rely on the minimal build context and existing repository secret rules. -- [ ] **Step 4 — Real smoke:** Build the image on native Linux and run assertions for `id -u != 0`, exact `pixiv --version`, and `pixiv config path` resolving under `/home/pixiv/.pixiv-cli/`. Also verify `/work` is the default working directory. -- [ ] **Step 5 — Functional boundary:** Verify the container still uses the same binary and existing CLI/MCP entrypoints. Do not introduce wrapper scripts that reinterpret CLI arguments or add container-only config environment variables. -- [ ] **Step 6 — Verify:** Re-run focused container tests and the relevant package/staticlib tests. Record image ID/digest and smoke outputs as evidence without recording credentials or local database contents. - -## Phase Progress Log - -### Phase iteration 0 — plan created - -- **Status**: pending -- **Next step**: Step 1 — define the failing Docker contract tests. diff --git a/goals/docker-container-release/phases/phase-3.md b/goals/docker-container-release/phases/phase-3.md deleted file mode 100644 index 69d7c2aa..00000000 --- a/goals/docker-container-release/phases/phase-3.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -phase_id: 3 -goal_id: docker-container-release -name: "Integrate native multi-arch build and GHCR publication" -status: pending -criterion_id: C3 ---- - -# Phase 3: Integrate native multi-arch build and GHCR publication - -## Objective - -Add native `linux/amd64` and `linux/arm64` container builds to the tagged release graph in parallel with native production builds, then publish a multi-arch GHCR image through a least-privilege publication boundary. - -## Exit Criterion - -C3 is satisfied only when both architectures build natively from the immutable tag, container build jobs hold no registry write permission, publication uses only verified image artifacts, and stable/prerelease tag behavior is proven by tests and workflow policy. - -## Dependencies - -- Phase 1 policy contract complete. -- Phase 2 runtime image contract complete. -- Existing `ubuntu-22.04` / `ubuntu-22.04-arm` Linux release provenance. - -## Plan - -- [ ] **Step 1 — Red:** Extend focused workflow tests for the concrete release graph: `build_container` starts after the shared quality gate and runs alongside `build_production`; exact native Linux runners/toolchains are required; `publish_container` is the only job with `packages: write`; exact-version tags are always published; `latest` is stable-only. Run and witness failure before editing the workflow. -- [ ] **Step 2 — Green/build:** Add a two-target `build_container` matrix using native `ubuntu-22.04` and `ubuntu-22.04-arm`. Each target checks out the immutable release tag, validates source, uses the audited Rust toolchain/staticlib path, builds a versioned Linux binary, applies the existing Linux ABI gate, builds the Docker image, and exports a transportable image artifact. It must not log in to a registry or receive `packages: write`. -- [ ] **Step 3 — Parallelism proof:** Ensure the release DAG makes `build_container` and `build_production` siblings after the required shared quality gate rather than serializing one behind the other. -- [ ] **Step 4 — Credential-free smoke workflow:** Add a narrowly triggered container smoke workflow for relevant PR/main changes. It builds both native architectures without registry credentials and runs the Phase 2 smoke assertions. Reuse full-SHA pinned actions and existing repository permission conventions. -- [ ] **Step 5 — Green/publish:** Add `publish_container` after the verified container artifacts and GitHub Release publication. Grant only `packages: write` (and the minimum read permission required), authenticate to GHCR with the workflow token, load/push the two architecture images, create the multi-arch manifest, and apply OCI source/revision/version/license labels. -- [ ] **Step 6 — Tag policy:** Publish `ghcr.io/flanchanxwo/pixiv-cli:vX.Y.Z` for every release. Advance `latest` only when the existing release channel classifier reports `stable`; prereleases must never move `latest`. -- [ ] **Step 7 — Recovery semantics:** Make publication rerunnable/idempotent for the same immutable tag where the registry permits it. Do not hide registry errors with retry loops or report a failed push as success. Document that GHCR publication failure leaves the workflow failed after GitHub Release publication and is repaired by rerunning the publication job/path. -- [ ] **Step 8 — Verify:** Run release policy/container tests and inspect both architecture artifacts in the smoke workflow. Confirm no QEMU setup, Docker Hub credentials, or new third-party GitHub Action dependency was added without explicit necessity and approval. - -## Phase Progress Log - -### Phase iteration 0 — plan created - -- **Status**: pending -- **Next step**: Step 1 — add failing release-graph and tag-policy tests. diff --git a/goals/docker-container-release/phases/phase-4.md b/goals/docker-container-release/phases/phase-4.md deleted file mode 100644 index 3cdedd1a..00000000 --- a/goals/docker-container-release/phases/phase-4.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -phase_id: 4 -goal_id: docker-container-release -name: "Document the supported Docker UX" -status: pending -criterion_id: C4 ---- - -# Phase 4: Document the supported Docker UX - -## Objective - -Make Docker a documented official installation/usage path while preserving the existing CLI, auth, state, MCP, and update contracts. - -## Exit Criterion - -C4 is satisfied only when English and Simplified Chinese documentation agree on Docker installation and operational boundaries and the repository documentation tests pass. - -## Dependencies - -- Phases 2 and 3 define the final image/tag/runtime behavior. -- `.agents/skills/pixiv-cli-docs/SKILL.md` documentation routing rules. - -## Plan - -- [ ] **Step 1 — Red where applicable:** Extend documentation fixtures/tests if the new official installation path needs a stable link/command contract. Run the focused documentation test before documentation changes when a testable contract is added. -- [ ] **Step 2 — User docs:** Update `README.md` first as the canonical English installation/quick-start entry, then synchronize `README.zh-CN.md`. Cover exact-version and `latest` pull semantics, persistent `~/.pixiv-cli` volume, `/work` bind mount for downloads, and image architecture support. -- [ ] **Step 3 — Auth docs:** Recommend stdin-based `pixiv auth import` with the persistent state volume for container use so refresh tokens do not need to enter image layers or CLI argv. Do not claim that `auth login` has new container-specific callback behavior. -- [ ] **Step 4 — MCP docs:** Show `docker run --rm -i ... mcp` as the stdio pattern. Preserve the rule that stdout belongs to MCP JSON-RPC and do not add a network MCP transport. -- [ ] **Step 5 — Upgrade docs:** State that container installations upgrade by pulling/redeploying a newer image. Do not claim `pixiv update` is container-aware or change updater semantics in this goal. -- [ ] **Step 6 — Maintainer docs:** Update `docs/en/maintainers/development.md` with container build/release verification and then synchronize `docs/zh-CN/maintainers/development.md`. Route rather than duplicate long release rules in multiple places. -- [ ] **Step 7 — Verify:** Run `go test ./scripts/tests/documentation -count=1` and `git diff --check`; inspect bilingual commands, paths, registry names, and security language for semantic parity. - -## Phase Progress Log - -### Phase iteration 0 — plan created - -- **Status**: pending -- **Next step**: Step 1 — determine which documentation behavior can be locked by a focused failing test before edits. diff --git a/goals/docker-container-release/phases/phase-5.md b/goals/docker-container-release/phases/phase-5.md deleted file mode 100644 index d08a6071..00000000 --- a/goals/docker-container-release/phases/phase-5.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -phase_id: 5 -goal_id: docker-container-release -name: "Run integrated verification and review" -status: pending -criterion_id: C5 ---- - -# Phase 5: Run integrated verification and review - -## Objective - -Prove the complete Docker release goal against the repository's actual gates and review the release/security boundary before declaring Goal Mode complete. - -## Exit Criterion - -C5 is satisfied only when all relevant focused and repository-wide checks pass with fresh evidence, the credential-free two-architecture container smoke run is green, and review finds no unresolved blocking issue in scope. - -## Dependencies - -- Phases 1–4 complete. - -## Plan - -- [ ] **Step 1 — Focused verification:** Run `go test ./scripts/internal/releaseworkflow -count=1`, `go test ./scripts/tests/containerrelease -count=1`, `go run ./scripts/cmd/releaseworkflow --workflow .github/workflows/release.yml`, and `go test ./scripts/tests/documentation -count=1`. -- [ ] **Step 2 — Package/regression verification:** Run `sh scripts/test-package-release.sh`, then `go test ./...`. Run any additional directly affected lint/build checks required by the files actually changed; do not substitute static checks for the real Docker smoke. -- [ ] **Step 3 — Real container evidence:** Confirm the latest credential-free container smoke workflow for the goal branch/commit is green on both native Linux architectures and that version/non-root/state-path assertions ran rather than skipped. -- [ ] **Step 4 — Diff hygiene:** Run `git diff --check` and confirm only files justified by this goal changed. Verify no generated image archives, local database, token, cache, or registry credential entered Git history. -- [ ] **Step 5 — Release boundary review:** Use the repository review checklist to inspect immutable tag binding, permissions, action SHA pinning, GHCR auth reachability, stable/prerelease tag semantics, glibc/native runner mapping, and error behavior. Fix blocking findings and rerun affected evidence. -- [ ] **Step 6 — Goal verifier:** Re-read every C1–C5 criterion against current repository state and evidence. Mark `COMPLETE` only when every criterion has direct current-state evidence; otherwise set `CONTINUE` or `BLOCKED` with the exact missing proof. - -## Phase Progress Log - -### Phase iteration 0 — plan created - -- **Status**: pending -- **Next step**: Wait until phases 1–4 are complete, then execute the full verification sequence. diff --git a/internal/architecture/architecture_test.go b/internal/architecture/architecture_test.go deleted file mode 100644 index efeeb8d3..00000000 --- a/internal/architecture/architecture_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package architecture_test - -import ( - "go/parser" - "go/token" - "os" - "path/filepath" - "strconv" - "strings" - "testing" -) - -const modulePath = "github.com/FlanChanXwO/pixiv-cli" - -func TestReverseSearchBoundaryExceptionIsDocumented(t *testing.T) { - repositoryRoot := findRepositoryRoot(t) - wants := map[string][]string{ - "AGENTS.md": { - "reverse-search is the only cross-boundary exception", - "internal/services/reversesearch/assembly", - "internal/services/reversesearch/saucenao", - "internal/services/reversesearch/ascii2d", - "internal/cli/commands", - "internal/mcpserver", - }, - "docs/en/maintainers/architecture.md": { - "### Reverse-search Facade exception", - "internal/services/reversesearch/assembly", - "internal/services/reversesearch/saucenao", - "internal/services/reversesearch/ascii2d", - "internal/cli/commands", - "internal/mcpserver", - }, - "docs/zh-CN/maintainers/architecture.md": { - "### reverse-search Facade 例外", - "internal/services/reversesearch/assembly", - "internal/services/reversesearch/saucenao", - "internal/services/reversesearch/ascii2d", - "internal/cli/commands", - "internal/mcpserver", - }, - } - for relativePath, phrases := range wants { - body, err := os.ReadFile(filepath.Join(repositoryRoot, relativePath)) - if err != nil { - t.Fatalf("read %s: %v", relativePath, err) - } - text := strings.ToLower(string(body)) - for _, phrase := range phrases { - if !strings.Contains(text, strings.ToLower(phrase)) { - t.Errorf("%s is missing architecture rule phrase %q", relativePath, phrase) - } - } - } -} - -func TestCLIAndMCPDoNotImportReverseSearchSubpackages(t *testing.T) { - repositoryRoot := findRepositoryRoot(t) - for _, relativeDir := range []string{"internal/cli/commands", "internal/mcpserver", "internal/shared/record"} { - directory := filepath.Join(repositoryRoot, relativeDir) - err := filepath.WalkDir(directory, func(path string, entry os.DirEntry, walkErr error) error { - if walkErr != nil { - return walkErr - } - if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { - return nil - } - file, err := parser.ParseFile(token.NewFileSet(), path, nil, parser.ImportsOnly) - if err != nil { - return err - } - for _, importSpec := range file.Imports { - importPath, err := strconv.Unquote(importSpec.Path.Value) - if err != nil { - return err - } - if strings.HasPrefix(importPath, modulePath+"/internal/services/reversesearch/") { - t.Errorf("%s imports reverse-search subpackage %q; only the top-level contract is allowed here", path, importPath) - } - } - return nil - }) - if err != nil { - t.Fatalf("scan %s: %v", relativeDir, err) - } - } -} - -func findRepositoryRoot(t *testing.T) string { - t.Helper() - directory, err := os.Getwd() - if err != nil { - t.Fatalf("get working directory: %v", err) - } - for { - if info, err := os.Stat(filepath.Join(directory, "go.mod")); err == nil && info.Mode().IsRegular() { - return directory - } - parent := filepath.Dir(directory) - if parent == directory { - t.Fatal("could not find repository root") - } - directory = parent - } -}