Skip to content

fix(cli): auto-delete binary on Windows, prune images, fix GoReleaser#590

Merged
Aureliolo merged 2 commits intomainfrom
fix/cli-uninstall-cleanup
Mar 19, 2026
Merged

fix(cli): auto-delete binary on Windows, prune images, fix GoReleaser#590
Aureliolo merged 2 commits intomainfrom
fix/cli-uninstall-cleanup

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

@Aureliolo Aureliolo commented Mar 19, 2026

Summary

  • synthorg uninstall on Windows now auto-deletes the binary after exit (spawns a detached cmd.exe that polls tasklist, then cleans up binary + empty dirs)
  • New step offers to remove all ghcr.io/aureliolo/synthorg-* container images, showing image list with sizes before confirmation
  • Fix GoReleaser ../LICENSE path resolution failure on v2.14.3 (first tag release exposed latent bug -- copy LICENSE into cli/ during CI instead)

Changes

  • cli/cmd/uninstall.go: Add confirmAndRemoveImages (lists + removes SynthOrg Docker images), scheduleWindowsCleanup (detached PID-polling cmd.exe for binary + dir cleanup), refactor confirmAndRemoveBinary to dispatch per-platform. Fix em-dashes in error messages.
  • cli/cmd/procattr_windows.go: windowsDetachedProcAttr returns CREATE_NEW_PROCESS_GROUP SysProcAttr.
  • cli/cmd/procattr_other.go: No-op stub for non-Windows (build tag !windows).
  • cli/.goreleaser.yml: Change src: ../LICENSE to src: LICENSE (relative to workdir).
  • .github/workflows/cli.yml: Copy LICENSE into cli/ before GoReleaser runs.
  • cli/.gitignore: Exclude the CI-only LICENSE copy.

Test plan

  • go build ./... clean
  • go vet ./... clean
  • golangci-lint run clean (noctx fixed)
  • go test ./... all pass
  • Pre-commit + pre-push hooks pass
  • Manual: synthorg uninstall on Windows -- verify binary auto-deleted after exit
  • Manual: synthorg uninstall -- verify image removal prompt appears and works
  • CI: GoReleaser release on next tag -- verify LICENSE included in archives

Closes #581

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced uninstall: lists local container images and can remove them during uninstall.
    • Improved CLI binary removal: automatic removal on Windows (deferred) and streamlined removal on Unix, with a clear manual-fallback option.
  • Bug Fixes

    • Ensured LICENSE is correctly included in CLI release archives and ignored locally in the CLI module.

Three uninstall gaps fixed:

1. Windows binary self-deletion: spawn a detached cmd.exe process that
   polls tasklist until the CLI exits, then deletes the binary and
   empty parent dirs (replaces manual PowerShell instruction)

2. Docker image pruning: new confirmAndRemoveImages step lists all
   ghcr.io/aureliolo/synthorg-* images with sizes, asks to remove,
   then runs docker rmi --force by ID

3. Fix em-dashes in error messages (non-ASCII punctuation)

Also fixes GoReleaser v2.14.3 archive failure:
- ../LICENSE path resolution broke (stat invalid argument)
- Changed to copy LICENSE into cli/ during CI, reference as LICENSE
- Added cli/.gitignore to exclude the CI-only copy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a1e99b07-37fc-4bf7-ab0d-9ba40bb7a7eb

📥 Commits

Reviewing files that changed from the base of the PR and between 5d377e8 and 2c027eb.

📒 Files selected for processing (1)
  • cli/cmd/uninstall.go
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

cli/**/*.go: Go CLI binary uses Cobra for command routing, charmbracelet libraries (huh, lipgloss) for interactive prompts and styled output, sigstore-go for cryptographic verification, go-containerregistry for Docker image operations
Cross-platform build matrix: linux/darwin/windows × amd64/arm64 via GoReleaser
Go code must pass golangci-lint run, go vet, and go test -race — vulnerability check via govulncheck
Use native testing.F fuzz functions (Fuzz*) for property-based testing of Go CLI packages

Files:

  • cli/cmd/uninstall.go
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Cross-platform build matrix: linux/darwin/windows × amd64/arm64 via GoReleaser
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Go CLI binary uses Cobra for command routing, charmbracelet libraries (huh, lipgloss) for interactive prompts and styled output, sigstore-go for cryptographic verification, go-containerregistry for Docker image operations
📚 Learning: 2026-03-19T13:34:44.305Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Go CLI binary uses Cobra for command routing, charmbracelet libraries (huh, lipgloss) for interactive prompts and styled output, sigstore-go for cryptographic verification, go-containerregistry for Docker image operations

Applied to files:

  • cli/cmd/uninstall.go
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/cmd/uninstall.go
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.

Applied to files:

  • cli/cmd/uninstall.go
📚 Learning: 2026-03-19T13:34:44.305Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Go code must pass `golangci-lint run`, `go vet`, and `go test -race` — vulnerability check via `govulncheck`

Applied to files:

  • cli/cmd/uninstall.go
🧬 Code graph analysis (1)
cli/cmd/uninstall.go (2)
cli/internal/docker/client.go (2)
  • Info (22-29)
  • RunCmd (110-119)
cli/internal/verify/identity.go (2)
  • RegistryHost (27-27)
  • ImageRepoPrefix (30-30)
🔇 Additional comments (8)
cli/cmd/uninstall.go (8)

3-20: LGTM!

Imports are appropriate for the new functionality: context for detached process spawning, os/exec for the Windows cleanup command, and verify for the image registry constants.


59-62: LGTM!

Appropriate placement after container shutdown and before data directory removal. The conditional check for Docker availability is correct.


123-192: LGTM!

The image removal implementation is well-structured:

  • Correctly uses info.DockerPath (the previous ComposeCmd[0] issue has been addressed)
  • Appropriate use of Docker filter syntax with glob pattern
  • User-friendly display with image sizes before confirmation
  • Non-blocking error handling with warnings aligns with the PR objectives

270-304: LGTM!

Clean refactoring that separates platform-specific concerns. The symlink resolution ensures the actual binary (not a symlink) is removed, and the early return pattern improves readability.


306-314: LGTM!

Simple and effective Unix binary removal with appropriate fallback instructions.


316-375: LGTM!

The Windows cleanup implementation is well-designed:

  • Temp .bat file approach correctly handles goto/labels (previous inline cmd /c issue resolved)
  • Proper batch syntax: %%%%i%%i for loop variables, %%~f0%~f0 for self-deletion
  • Uses context.Background() so parent context cancellation doesn't affect the cleanup process
  • timeout /t 1 is cleaner than the ping workaround
  • Graceful fallback to manual instructions on any failure

The 30-second polling window is reasonable for normal process exit times.


377-384: LGTM!

The fallback provides clear manual instructions with correct PowerShell single-quote escaping (''') and uses -LiteralPath to handle special characters in paths.


219-219: LGTM!

Punctuation normalized to ASCII dashes for consistent cross-platform terminal output.

Also applies to: 239-239, 260-260, 431-431


Walkthrough

Added CI step to copy the repository LICENSE into cli/ and updated cli/.gitignore and cli/.goreleaser.yml so archives include cli/LICENSE. Introduced platform-specific helpers: cli/cmd/procattr_windows.go and cli/cmd/procattr_other.go providing windowsDetachedProcAttr(). Expanded cli/cmd/uninstall.go to detect and optionally remove local SynthOrg container images, add Unix and Windows binary removal helpers, schedule Windows post-exit cleanup via a temporary .bat script with a manual-fallback path, and adjusted some uninstall message separators to --.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the three main changes: auto-deletion of CLI binary on Windows, image pruning during uninstall, and GoReleaser configuration fixes.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #581: implements Windows deferred binary cleanup via scheduled batch script, adds interactive container image removal during uninstall with docker CLI integration, updates GoReleaser configuration for correct LICENSE path handling, and includes build tag support for cross-platform compatibility.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #581 objectives: Windows binary cleanup, container image pruning functionality, GoReleaser LICENSE path fixes, and supporting build configuration files (.gitignore, workflow scripts). No extraneous modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 61.54% which is sufficient. The required threshold is 40.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/cli-uninstall-cleanup
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the synthorg uninstall experience by introducing automatic binary cleanup on Windows and providing an option to prune associated Docker container images. Additionally, it addresses a critical GoReleaser configuration bug to ensure smooth and correct inclusion of the license file in future releases, streamlining the overall maintenance and distribution process.

Highlights

  • Windows Uninstall Improvement: The synthorg uninstall command on Windows now automatically deletes the CLI binary after the current process exits. This is achieved by spawning a detached cmd.exe process that polls for the main process's termination before performing cleanup.
  • Container Image Pruning: The uninstall process now includes an optional step to remove all ghcr.io/aureliolo/synthorg-* container images from the local Docker cache. Users are prompted with a list of images and their sizes before confirmation.
  • GoReleaser Fix: Resolved an issue where GoReleaser failed to locate the LICENSE file during releases, particularly with v2.14.3. The fix involves copying the LICENSE file into the cli/ directory during CI workflows and updating the GoReleaser configuration to reference it correctly.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/cli.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds some excellent quality-of-life improvements to the uninstall command, particularly the automatic binary cleanup on Windows and the option to prune container images. The GoReleaser fix is also a necessary correction. My review focuses on a potential bug in the image pruning logic that could affect users with older docker-compose versions, and a suggestion to improve the readability of the new Windows cleanup script.

Comment on lines +131 to +134
listOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The docker images command is being executed using info.ComposeCmd[0], which could be docker-compose. However, docker-compose images (v1) does not support the --filter and --format arguments used here, which will cause this call to fail. This should be a direct docker images call to ensure it works correctly regardless of the Compose version detected.

Suggested change
listOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)
listOut, err := docker.RunCmd(ctx, "docker", "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)

Comment on lines +167 to +170
idsOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.ID}}",
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

For the same reason as the previous comment, getting image IDs should also use the docker command directly. The docker-compose images (v1) command does not support the required arguments, and this call will fail in environments where it is present.

Suggested change
idsOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.ID}}",
)
idsOut, err := docker.RunCmd(ctx, "docker", "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.ID}}",
)


_, _ = fmt.Fprintln(out, "Removing SynthOrg images...")
rmiArgs := append([]string{"rmi", "--force"}, ids...)
if _, rmiErr := docker.RunCmd(ctx, info.ComposeCmd[0], rmiArgs...); rmiErr != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The rmi command is a docker engine command. docker-compose v1 does not have a top-level rmi command. This call will fail if info.ComposeCmd[0] resolves to docker-compose. It should be an explicit call to docker rmi.

Suggested change
if _, rmiErr := docker.RunCmd(ctx, info.ComposeCmd[0], rmiArgs...); rmiErr != nil {
if _, rmiErr := docker.RunCmd(ctx, "docker", rmiArgs...); rmiErr != nil {

Comment on lines +331 to +345
script := fmt.Sprintf(
`for /L %%i in (1,1,30) do (`+
`tasklist /fi "PID eq %d" 2>nul | find "%d" >nul || goto :cleanup & `+
`ping -n 2 127.0.0.1 >nul`+
`) & goto :done`+
` & :cleanup`+
` & del /f /q "%s"`+
` & rmdir "%s" 2>nul`+
` & rmdir "%s" 2>nul`+
` & :done`,
pid, pid,
execPath,
binDir,
dataDir,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of ping for a delay is a classic technique, but using the timeout command is more explicit and clearer about the intent. timeout is a standard command in modern Windows versions (Vista and later), making it a safe and more readable choice for creating a 1-second delay. This suggestion also refactors the script to use standard double-quoted strings to avoid issues with backticks in code suggestions.

Suggested change
script := fmt.Sprintf(
`for /L %%i in (1,1,30) do (`+
`tasklist /fi "PID eq %d" 2>nul | find "%d" >nul || goto :cleanup & `+
`ping -n 2 127.0.0.1 >nul`+
`) & goto :done`+
` & :cleanup`+
` & del /f /q "%s"`+
` & rmdir "%s" 2>nul`+
` & rmdir "%s" 2>nul`+
` & :done`,
pid, pid,
execPath,
binDir,
dataDir,
)
script := fmt.Sprintf(
"for /L %%%%i in (1,1,30) do ("+
"tasklist /fi \"PID eq %d\" 2>nul | find \"%d\" >nul || goto :cleanup & "+
"timeout /t 1 /nobreak >nul"+
") & goto :done"+
" & :cleanup"+
" & del /f /q \"%s\""+
" & rmdir \"%s\" 2>nul"+
" & rmdir \"%s\" 2>nul"+
" & :done",
pid, pid,
execPath,
binDir,
dataDir,
)

coderabbitai[bot]
coderabbitai bot previously requested changes Mar 19, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cli/cmd/uninstall.go`:
- Around line 347-348: Replace the use of the parent's context when spawning the
detached cleanup process: instead of exec.CommandContext(cmd.Context(),
"cmd.exe", "/c", script) use exec.CommandContext(context.Background(),
"cmd.exe", "/c", script) so the child is not cancelled if the parent's context
is cancelled; keep c.SysProcAttr = windowsDetachedProcAttr() and the subsequent
Start()/Release() logic unchanged to ensure the process is detached and allowed
to outlive the parent.
- Around line 131-134: Replace uses of info.ComposeCmd[0] when invoking
docker.RunCmd for listing/removing images with info.DockerPath so the Docker CLI
(not docker-compose) is used; specifically update the docker.RunCmd calls that
pass info.ComposeCmd[0], e.g. the calls that build the "images" command with
"--filter reference="+imageRef and any subsequent docker.RunCmd invocations that
remove or inspect images, to use info.DockerPath instead while keeping the same
args (imageRef, "--format", etc.); apply the same change for the other
occurrences in uninstall.go where docker.RunCmd is called with
info.ComposeCmd[0] (the additional calls referenced in the review).
- Around line 331-345: The Windows uninstall command string built in
uninstall.go (the script variable that interpolates pid, execPath, binDir,
dataDir) incorrectly uses batch labels and goto (":cleanup", ":done", "goto
:cleanup") inside a single-line cmd /c which will fail; replace this approach by
either (A) rewriting the script to use conditional execution operators (&& and
||) and proper command grouping so the wait-and-clean logic doesn't rely on
labels/goto, or (B) generate a temporary .bat file containing the labeled block
and run that .bat from cmd.exe, ensuring you still pass pid, execPath, binDir,
dataDir into the temp file and delete it after execution; update the code that
builds the script variable and the exec invocation accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a5171a57-c46d-441c-a26e-b5727303da22

📥 Commits

Reviewing files that changed from the base of the PR and between bf58779 and 5d377e8.

📒 Files selected for processing (6)
  • .github/workflows/cli.yml
  • cli/.gitignore
  • cli/.goreleaser.yml
  • cli/cmd/procattr_other.go
  • cli/cmd/procattr_windows.go
  • cli/cmd/uninstall.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CLI Test (macos-latest)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
.github/workflows/*.yml

📄 CodeRabbit inference engine (CLAUDE.md)

.github/workflows/*.yml: GitHub Actions workflows must use SHA-pinned action versions (not @v or @main) for supply chain security
All commits must be GPG/SSH signed on main branch — enforced via branch protection

Files:

  • .github/workflows/cli.yml
cli/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

cli/**/*.go: Go CLI binary uses Cobra for command routing, charmbracelet libraries (huh, lipgloss) for interactive prompts and styled output, sigstore-go for cryptographic verification, go-containerregistry for Docker image operations
Cross-platform build matrix: linux/darwin/windows × amd64/arm64 via GoReleaser
Go code must pass golangci-lint run, go vet, and go test -race — vulnerability check via govulncheck
Use native testing.F fuzz functions (Fuzz*) for property-based testing of Go CLI packages

Files:

  • cli/cmd/procattr_other.go
  • cli/cmd/procattr_windows.go
  • cli/cmd/uninstall.go
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Cross-platform build matrix: linux/darwin/windows × amd64/arm64 via GoReleaser
📚 Learning: 2026-03-19T13:34:44.305Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Cross-platform build matrix: linux/darwin/windows × amd64/arm64 via GoReleaser

Applied to files:

  • cli/.gitignore
  • cli/.goreleaser.yml
  • .github/workflows/cli.yml
  • cli/cmd/procattr_other.go
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/.gitignore
  • .github/workflows/cli.yml
  • cli/cmd/procattr_other.go
  • cli/cmd/uninstall.go
📚 Learning: 2026-03-19T13:34:44.305Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to .github/workflows/release.yml : Release workflow must auto-create draft GitHub releases via Release Please, tag with semver, and set BSL Change Date to 3 years ahead

Applied to files:

  • .github/workflows/cli.yml
📚 Learning: 2026-03-19T13:34:44.305Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T13:34:44.305Z
Learning: Applies to cli/**/*.go : Go CLI binary uses Cobra for command routing, charmbracelet libraries (huh, lipgloss) for interactive prompts and styled output, sigstore-go for cryptographic verification, go-containerregistry for Docker image operations

Applied to files:

  • cli/cmd/uninstall.go
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.

Applied to files:

  • cli/cmd/uninstall.go
🧬 Code graph analysis (1)
cli/cmd/uninstall.go (2)
cli/internal/docker/client.go (2)
  • Info (22-29)
  • RunCmd (110-119)
cli/internal/verify/identity.go (2)
  • RegistryHost (27-27)
  • ImageRepoPrefix (30-30)
🔇 Additional comments (7)
cli/.gitignore (1)

1-2: LGTM!

The comment clearly explains why LICENSE is gitignored, and this properly prevents the CI-copied file from being accidentally committed to the repository.

cli/.goreleaser.yml (1)

34-37: LGTM!

The path change from ../LICENSE to LICENSE correctly aligns with the new CI workflow step that copies the root LICENSE into the cli/ directory before GoReleaser runs. This resolves the archive failure with GoReleaser v2.14.3 while keeping the same destination and timestamp metadata.

.github/workflows/cli.yml (1)

284-286: LGTM!

The LICENSE copy step is correctly positioned before GoReleaser execution and aligns with the corresponding changes in cli/.goreleaser.yml and cli/.gitignore. All actions in this workflow remain SHA-pinned, compliant with supply chain security guidelines.

cli/cmd/procattr_windows.go (1)

1-11: LGTM!

The Windows-specific implementation correctly uses CREATE_NEW_PROCESS_GROUP to detach the child process, allowing it to survive after the parent exits. The _windows.go filename suffix correctly constrains the build to Windows.

cli/cmd/procattr_other.go (1)

1-12: LGTM!

The build constraint //go:build !windows correctly gates this stub for non-Windows platforms. Returning nil is appropriate since exec.Cmd.SysProcAttr = nil is a no-op on Unix systems.

cli/cmd/uninstall.go (2)

268-302: LGTM!

The refactored function cleanly separates platform-specific logic with appropriate delegation to removeUnixBinary and scheduleWindowsCleanup. Symlink resolution ensures the actual binary is targeted.


304-312: LGTM!

Graceful degradation with clear manual instructions if removal fails. Returning nil on error is appropriate since the uninstall should complete even if binary removal requires manual intervention.

Comment on lines +131 to +134
listOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Bug: Using ComposeCmd[0] instead of DockerPath for docker image commands.

When Compose V1 is installed, info.ComposeCmd is ["docker-compose"], so ComposeCmd[0] would be "docker-compose". The command docker-compose images behaves differently from docker images—it lists images used by compose services, not local images by reference filter.

Use info.DockerPath instead, which always points to the Docker CLI binary.

🐛 Proposed fix
-	listOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
+	listOut, err := docker.RunCmd(ctx, info.DockerPath, "images",
 		"--filter", "reference="+imageRef+"*",
 		"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
 	)

Also apply the same fix at lines 167-170 and 183:

-	idsOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
+	idsOut, err := docker.RunCmd(ctx, info.DockerPath, "images",
-	if _, rmiErr := docker.RunCmd(ctx, info.ComposeCmd[0], rmiArgs...); rmiErr != nil {
+	if _, rmiErr := docker.RunCmd(ctx, info.DockerPath, rmiArgs...); rmiErr != nil {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
listOut, err := docker.RunCmd(ctx, info.ComposeCmd[0], "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)
listOut, err := docker.RunCmd(ctx, info.DockerPath, "images",
"--filter", "reference="+imageRef+"*",
"--format", "{{.Repository}}:{{.Tag}} ({{.Size}})",
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/cmd/uninstall.go` around lines 131 - 134, Replace uses of
info.ComposeCmd[0] when invoking docker.RunCmd for listing/removing images with
info.DockerPath so the Docker CLI (not docker-compose) is used; specifically
update the docker.RunCmd calls that pass info.ComposeCmd[0], e.g. the calls that
build the "images" command with "--filter reference="+imageRef and any
subsequent docker.RunCmd invocations that remove or inspect images, to use
info.DockerPath instead while keeping the same args (imageRef, "--format",
etc.); apply the same change for the other occurrences in uninstall.go where
docker.RunCmd is called with info.ComposeCmd[0] (the additional calls referenced
in the review).

Address review findings from CodeRabbit and Gemini:

- Replace broken goto/labels in cmd /c with temp .bat file approach
  (labels don't work in single-line cmd /c commands)
- Use info.DockerPath instead of info.ComposeCmd[0] for docker images
  and docker rmi (docker-compose doesn't support --filter/--format)
- Use context.Background() for detached cleanup process (parent
  context cancellation shouldn't kill the cleanup)
- Use timeout /t 1 instead of ping for delay
- Replace em-dashes with ASCII dashes in error messages
- .bat self-deletes via del "%%~f0" as final step

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo
Copy link
Copy Markdown
Owner Author

@coderabbitai review

Aureliolo added a commit that referenced this pull request Mar 19, 2026
## Summary

Reverts the Release Please commit for v0.3.7 (`bf58779`) because the CLI
GoReleaser step failed during the release workflow (LICENSE path
resolution error). The draft release and tag have been deleted.

**What this reverts:**
- `.github/.release-please-manifest.json`: 0.3.7 -> 0.3.6
- `.github/CHANGELOG.md`: removes 0.3.7 entry
- `pyproject.toml`: 0.3.7 -> 0.3.6
- `src/synthorg/__init__.py`: 0.3.7 -> 0.3.6

**What was already cleaned up:**
- Deleted draft GitHub Release for v0.3.7
- Deleted remote tag v0.3.7

**Next steps:**
1. Merge this PR (resets version to 0.3.6)
2. Merge #590 (fixes GoReleaser LICENSE path + uninstall improvements)
3. Release Please will auto-create a new release PR for 0.3.7 that
includes all fixes
4. Merging that release PR will re-trigger Docker + CLI workflows with
the GoReleaser fix in place

:robot: Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Version updated from 0.3.7 to 0.3.6 across configuration and version
metadata files.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo merged commit eb7c691 into main Mar 19, 2026
39 checks passed
@Aureliolo Aureliolo deleted the fix/cli-uninstall-cleanup branch March 19, 2026 14:16
Aureliolo added a commit that referenced this pull request Mar 19, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.7](v0.3.6...v0.3.7)
(2026-03-19)


### Features

* **engine:** implement Hybrid Plan + ReAct execution loop
([#582](#582))
([008147c](008147c))
* implement first-run setup wizard
([#584](#584))
([dfed931](dfed931))


### Bug Fixes

* **api:** address ZAP DAST scan findings
([#579](#579))
([ce9a3e0](ce9a3e0))
* **cli:** auto-delete binary on Windows, prune images, fix GoReleaser
([#590](#590))
([eb7c691](eb7c691))
* **cli:** regenerate compose and re-exec binary on update
([#576](#576))
([3f226eb](3f226eb))


### CI/CD

* add SBOM generation to Docker and CLI releases
([#580](#580))
([db459cf](db459cf))


### Maintenance

* **main:** release 0.3.7
([#583](#583))
([bf58779](bf58779))
* reset failed v0.3.7 release
([#591](#591))
([b69000d](b69000d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added engine Hybrid Plan + ReAct execution loop
  * Added first-run setup wizard

* **Bug Fixes**
  * Addressed ZAP DAST scan issues
  * Fixed CLI Windows/image/update issues

* **Maintenance**
  * Added SBOM generation for Docker/CLI releases
  * General maintenance updates

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Aureliolo added a commit that referenced this pull request Mar 19, 2026
## Summary

Two issues caused the v0.3.7 CLI release to fail:

1. **GoReleaser LICENSE path** -- fixed in #590 (already merged)
2. **Syft SBOM scan** -- syft tried to auto-detect `.tar.gz`/`.zip`
archives as container images and failed. Fix: prefix `$artifact` with
`file:` scheme so syft knows to scan as a local file archive

This PR:
- Reverts the Release Please v0.3.7 commit (`2e8e633`) to reset version
back to 0.3.6
- Fixes the syft scan command in `.goreleaser.yml`
- v0.3.7 draft release and tag already deleted

After merge, Release Please will auto-create a new 0.3.7 release PR with
both fixes in place.

## Changes

- **`.github/.release-please-manifest.json`**: 0.3.7 -> 0.3.6
- **`.github/CHANGELOG.md`**: remove 0.3.7 entry
- **`pyproject.toml`**: 0.3.7 -> 0.3.6
- **`src/synthorg/__init__.py`**: 0.3.7 -> 0.3.6
- **`cli/.goreleaser.yml`**: `scan $artifact` -> `scan file:$artifact`

:robot: Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Aureliolo added a commit that referenced this pull request Mar 19, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.7](v0.3.6...v0.3.7)
(2026-03-19)


### Features

* **engine:** implement Hybrid Plan + ReAct execution loop
([#582](#582))
([008147c](008147c))
* implement first-run setup wizard
([#584](#584))
([dfed931](dfed931))


### Bug Fixes

* **api:** address ZAP DAST scan findings
([#579](#579))
([ce9a3e0](ce9a3e0))
* **ci:** reset failed v0.3.7 release and fix syft SBOM scan
([#593](#593))
([d1508c2](d1508c2))
* **cli:** auto-delete binary on Windows, prune images, fix GoReleaser
([#590](#590))
([eb7c691](eb7c691))
* **cli:** regenerate compose and re-exec binary on update
([#576](#576))
([3f226eb](3f226eb))


### CI/CD

* add SBOM generation to Docker and CLI releases
([#580](#580))
([db459cf](db459cf))


---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Aureliolo added a commit that referenced this pull request Mar 19, 2026
## Summary

Syft cannot scan Go binary archives (`.tar.gz`/`.zip`) -- it tries to
interpret them as container images and fails. Since CLI binaries are
statically linked with `CGO_ENABLED=0`, there are no runtime
dependencies to catalog. Docker image SBOMs (generated by the Docker
workflow via syft on actual container images) remain unchanged.

This is the third v0.3.7 release attempt. Previous failures:
1. GoReleaser `../LICENSE` path resolution (fixed in #590)
2. Syft `file:$artifact` still failed on archive auto-detection (this PR
removes it)

## Changes

- **`cli/.goreleaser.yml`**: Remove `sboms:` stanza entirely
- **`.github/workflows/cli.yml`**: Remove `*.cdx.json` from upload glob,
remove `CLI_SBOM_DATA` HTML comment block
- **`.github/workflows/finalize-release.yml`**: Remove `CLI_SBOM`
extraction, simplify SBOM rendering to container-only
- **`CLAUDE.md`**: Remove CLI SBOM references from CI documentation
- Reverts Release Please v0.3.7 commit, tag + release already deleted

## Test plan

- [x] All pre-commit and pre-push hooks pass
- [x] GoReleaser config validates (no sboms stanza)
- [ ] Next v0.3.7 release: GoReleaser should complete without syft
errors

:robot: Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Aureliolo added a commit that referenced this pull request Mar 19, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.7](v0.3.6...v0.3.7)
(2026-03-19)


### Features

* **engine:** implement Hybrid Plan + ReAct execution loop
([#582](#582))
([008147c](008147c))
* implement first-run setup wizard
([#584](#584))
([dfed931](dfed931))


### Bug Fixes

* **api:** address ZAP DAST scan findings
([#579](#579))
([ce9a3e0](ce9a3e0))
* **ci:** remove CLI SBOM generation, reset failed v0.3.7
([#595](#595))
([d0f4992](d0f4992))
* **ci:** reset failed v0.3.7 release and fix syft SBOM scan
([#593](#593))
([d1508c2](d1508c2))
* **cli:** auto-delete binary on Windows, prune images, fix GoReleaser
([#590](#590))
([eb7c691](eb7c691))
* **cli:** regenerate compose and re-exec binary on update
([#576](#576))
([3f226eb](3f226eb))


### CI/CD

* add SBOM generation to Docker and CLI releases
([#580](#580))
([db459cf](db459cf))




---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(cli): clean up old container images and stale binaries after update

1 participant