Skip to content

fix(desktop): add Dock icon and fix undeletable failed clusters#4896

Open
devantler wants to merge 3 commits into
mainfrom
claude/busy-banach-7e30b6
Open

fix(desktop): add Dock icon and fix undeletable failed clusters#4896
devantler wants to merge 3 commits into
mainfrom
claude/busy-banach-7e30b6

Conversation

@devantler
Copy link
Copy Markdown
Contributor

Fixes two issues reported in the KSail desktop app.

1. Missing Dock icon

Root cause: the KSail.app bundle never carried an icon — the Info.plist had no CFBundleIconFile key, and neither make-macos-app.sh nor the desktop GoReleaser config placed an .icns in Contents/Resources. macOS therefore showed the generic application icon in the Dock and Finder.

Fix:

  • New desktop/scripts/make-icns.sh generates AppIcon.icns from a square source PNG via sips + iconutil (macOS-only — fine, the .app is only built on macOS).
  • New desktop/resources/icon.png (the KSail logo; the only square icon in the repo — the docs logos are wide wordmarks).
  • make-info-plist.sh now emits CFBundleIconFile = AppIcon.
  • Wired into both the local make-macos-app.sh build and the release GoReleaser post-hook + archive, so local and Homebrew-cask bundles both ship the icon.

No Go change needed. Verified that assembling the bundle yields a valid 1024px AppIcon.icns at Contents/Resources/AppIcon.icns with the matching plist key.

Note for reviewers: the source icon is 445×445 (reused from the VS Code extension); the 512/1024 slots are slight upscales. Swap in a larger master later if desired. Existing installs may show a cached empty icon until a fresh install or killall Dock.

2. Failed clusters can't be deleted

Root cause (reproduced against the live uiserver backend): a cluster left in the Failed phase by a failed create could never be removed from the UI. DELETE returned 204, but the async job then called provisioner.Delete, which returned ErrClusterNotFound (a failed create produces no deletable cluster), so the entry was re-pinned Failed. The only escapes were restarting the app or ksail cluster delete --name <name>.

Fix: runDelete in pkg/cli/clusterapi/local_service.go is now idempotent — it probes Exists first and treats "already gone" as success so the tracked entry clears. Genuine deletion failures still surface as Failed. This also benefits ksail ui, which shares the same backend.

Tests: added TestDeleteClearsFailedClusterWithNoUnderlyingCluster (the regression) and TestDeleteKeepsClusterFailedWhenDeletionErrors (guards against masking real failures). Confirmed the regression test fails without the fix (error="cluster not found", matching the live repro). The test fake's Delete now mirrors real provisioners (returns ErrClusterNotFound for an absent cluster).

Validation

go build ./..., go test -race ./pkg/cli/clusterapi/..., golangci-lint run, and shellcheck on the scripts all pass. Reproduced and fixed against real Kind/K3d clusters via ksail ui + the REST API; all test clusters cleaned up.

Out of scope (separate, confirmed gap)

Deleting a CLI-created cluster (one with KSail mirror-registry containers) via the desktop removes the node but orphans the <name>-docker.io / -ghcr.io registry containers — the CLI delete cleans these up, the desktop path doesn't. Happy to address in a follow-up.

🤖 Generated with Claude Code

devantler and others added 2 commits May 26, 2026 23:31
The KSail.app bundle never carried an icon: the Info.plist had no
CFBundleIconFile key and neither make-macos-app.sh nor the desktop
GoReleaser config placed an .icns in Contents/Resources, so macOS
showed the generic application icon in the Dock and Finder.

Add make-icns.sh, which generates AppIcon.icns from a square source PNG
(desktop/resources/icon.png, the KSail logo) using sips + iconutil. This
is macOS-only, which is fine because the .app bundle is only ever built
on macOS. Wire it into the Info.plist (CFBundleIconFile=AppIcon), the
local make-macos-app.sh build, and the release GoReleaser post-hook +
archive so both local and Homebrew-cask bundles ship the icon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…oved

A cluster left in the Failed phase by a failed create could never be
removed from the web UI / desktop app. Deleting it returned 204 but the
background job then called provisioner.Delete, which returned
ErrClusterNotFound (the failed create produced no deletable cluster), so
the entry was re-pinned Failed. The row could only be cleared by
restarting the app or falling back to `ksail cluster delete --name`.

Make runDelete idempotent: probe Exists first and treat "already gone"
as success so the tracked entry is cleared. Genuine deletion errors
still surface as Failed. Adds regression tests covering both paths
(verified the new test fails without the fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 26, 2026 21:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes two desktop-app issues: (1) missing macOS Dock/Finder icon for the KSail.app bundle, and (2) inability to delete UI-tracked clusters that are left in Failed with no underlying cluster to delete (desktop app and ksail ui share this backend).

Changes:

  • Make local-cluster deletion effectively idempotent by probing Provisioner.Exists before calling Delete, so “already gone” clears the tracked entry.
  • Add regression tests ensuring failed/no-underlying clusters can be deleted, while real delete failures still surface as Failed.
  • Add macOS icon generation (.icns) and wire it into both local .app assembly and the desktop GoReleaser pipeline, plus emit CFBundleIconFile in the bundle plist.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/cli/clusterapi/local_service.go Updates async delete workflow to clear UI entries when the underlying cluster does not exist.
pkg/cli/clusterapi/local_service_test.go Expands the fake provisioner behavior and adds regression/guard tests for delete semantics.
desktop/scripts/make-macos-app.sh Generates and embeds AppIcon.icns into the local .app bundle during assembly.
desktop/scripts/make-info-plist.sh Adds CFBundleIconFile so macOS uses the embedded icon.
desktop/scripts/make-icns.sh New script to generate AppIcon.icns from a source PNG via sips/iconutil.
.goreleaser.desktop.yaml Adds icon generation hook and includes the .icns in the archived .app bundle.

Comment thread pkg/cli/clusterapi/local_service.go Outdated
Comment thread pkg/cli/clusterapi/local_service_test.go
@devantler devantler marked this pull request as ready for review May 26, 2026 21:35
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

MegaLinter analysis: Error

❌ COPYPASTE / jscpd - 7 errors
Clone found (typescript):
 - vsce/src/mcp/schemaClient.ts [325:18 - 332:33] (7 lines, 56 tokens)
   vsce/src/mcp/schemaClient.ts [309:20 - 316:26]

Clone found (typescript):
 - vsce/src/ksail/clusters.ts [381:34 - 390:46] (9 lines, 59 tokens)
   vsce/src/ksail/clusters.ts [361:35 - 370:47]

Clone found (typescript):
 - vsce/src/ksail/clusters.ts [406:44 - 415:46] (9 lines, 61 tokens)
   vsce/src/ksail/clusters.ts [341:33 - 350:48]

Clone found (typescript):
 - vsce/src/ksail/clusters.ts [426:36 - 435:48] (9 lines, 59 tokens)
   vsce/src/ksail/clusters.ts [361:35 - 370:47]

Clone found (typescript):
 - vsce/src/commands/prompts.ts [658:29 - 669:3] (11 lines, 55 tokens)
   vsce/src/commands/prompts.ts [201:8 - 211:3]

Clone found (go):
 - pkg/svc/tenant/argocd.go [400:61 - 413:37] (13 lines, 99 tokens)
   pkg/fsutil/configmanager/talos/manager.go [228:64 - 241:37]

Clone found (typescript):
 - vsce/src/extension.ts [162:46 - 173:18] (11 lines, 59 tokens)
   vsce/src/extension.ts [140:39 - 151:5]

┌────────────┬────────────────┬─────────────┬──────────────┬──────────────┬──────────────────┬───────────────────┐
│ Format     │ Files analyzed │ Total lines │ Total tokens │ Clones found │ Duplicated lines │ Duplicated tokens │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ toml       │ 6              │ 64          │ 277          │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ go         │ 590            │ 97664       │ 640505       │ 1            │ 13 (0.01%)       │ 99 (0.02%)        │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ markdown   │ 7              │ 302         │ 638          │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ typescript │ 23             │ 4084        │ 22492        │ 6            │ 56 (1.37%)       │ 349 (1.55%)       │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ javascript │ 15             │ 1108        │ 9310         │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ tsx        │ 12             │ 1781        │ 14802        │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ css        │ 2              │ 45          │ 148          │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ smarty     │ 1              │ 63          │ 1039         │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ bash       │ 3              │ 101         │ 444          │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ Total:     │ 659            │ 105212      │ 689655       │ 7            │ 69 (0.07%)       │ 448 (0.06%)       │
└────────────┴────────────────┴─────────────┴──────────────┴──────────────┴──────────────────┴───────────────────┘
Found 7 clones.
HTML report saved to megalinter-reports/copy-paste/html/
ERROR: jscpd found too many duplicates (0.07%) over threshold (0%)
Error: ERROR: jscpd found too many duplicates (0.07%) over threshold (0%)
    at ThresholdReporter.report (/node-deps/node_modules/@jscpd/finder/dist/index.js:615:13)
    at /node-deps/node_modules/@jscpd/finder/dist/index.js:109:18
    at Array.forEach (<anonymous>)
    at /node-deps/node_modules/@jscpd/finder/dist/index.js:108:22
    at async /node-deps/node_modules/jscpd/dist/bin/jscpd.js:9:5
❌ REPOSITORY / osv-scanner - 1 error
Scanning dir .
Starting filesystem walk for root: /
Scanned docs/package-lock.json file and found 549 packages
Scanned desktop/go.mod file and found 401 packages
Scanned web/ui/package-lock.json file and found 187 packages
Scanned go.mod file and found 965 packages
Scanned vsce/package-lock.json file and found 592 packages
End status: 308 dirs visited, 2031 inodes visited, 5 Extract calls, 171.763387ms elapsed, 171.763527ms wall time
Filtered 1 local/unscannable package/s from the scan.
Failed to run code analysis (govulncheck) on 'desktop/go.mod' because govulncheck: loading packages: 
There are errors with the provided package patterns:

-: # github.com/webview/webview_go
# [pkg-config --cflags  -- gtk+-3.0 webkit2gtk-4.0]
Package gtk+-3.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `gtk+-3.0.pc'
to the PKG_CONFIG_PATH environment variable
Package 'gtk+-3.0' not found
Package 'webkit2gtk-4.0' not found
/go/pkg/mod/github.com/webview/webview_go@v0.0.0-20240831120633-6173450d4dd6/webview.go:26:8: could not import C (no metadata for C)
desktop/main.go:64:42: cannot use webview.HintNone (constant unknown with invalid type) as webview.Hint value in argument to view.SetSize

For details on package patterns, see https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.

(the Go toolchain is required)
❌ ACTION / zizmor - 1 error
INFO zizmor: 🌈 zizmor v1.25.0
fatal: no audit was performed
'artipacked' audit failed on file://.github/workflows/cd.yaml

Caused by:
    0: error in 'artipacked' audit
    1: couldn't list tags for actions/checkout
    2: request error while accessing GitHub API
    3: HTTP status client error (401 Unauthorized) for url (https://github.com/actions/checkout.git/git-upload-pack)


[ZizmorLinter] Zizmor failed to reach the GitHub API.
To allow zizmor to use GITHUB_TOKEN, add the following to your .mega-linter.yml:
ACTION_ZIZMOR_UNSECURED_ENV_VARIABLES:
  - GITHUB_TOKEN

✅ Linters with no issues

actionlint, bash-exec, git_diff, hadolint, jsonlint, lychee, markdown-table-formatter, markdownlint, prettier, prettier, shellcheck, shfmt (1 fix), stylelint, syft, trivy-sbom, trufflehog, v8r, v8r, yamllint

Notices

📣 MegaLinter 9.5.0 is out! Discover the new features and security recommendations in the release announcement. (Skip this info by defining SECURITY_SUGGESTIONS: false)

See detailed reports in MegaLinter artifacts

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

Address review feedback: replace the Exists pre-check in runDelete with
unconditional Delete + errors.Is(err, clustererr.ErrClusterNotFound). The
pre-check duplicated the provisioners' own existence check and left a
TOCTOU window (Exists could return true and the cluster vanish before
Delete, returning ErrClusterNotFound and re-pinning the job Failed).
Handling the not-found error directly is race-free and covers the failed
-create, deleted-out-of-band, and TOCTOU cases alike.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ksail-bot ksail-bot Bot enabled auto-merge May 26, 2026 21:39
@github-code-quality
Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: Go

Go / code-coverage/go

The overall coverage in the branch is 55%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File e442b4e +/-
pkg/cli/cmd/wor...load/gen/gen.go 96%
pkg/notify/progress.go 94%
pkg/svc/image/exporter.go 92%
pkg/fsutil/conf...alos/configs.go 86%
pkg/cli/cmd/cipher/cipher.go 79%
internal/contro...r_controller.go 79%
pkg/fsutil/gene...os/generator.go 78%
pkg/cli/ui/chat/model.go 70%
pkg/cli/cmd/cluster/cluster.go 55%
pkg/cli/cmd/wor...oad/workload.go 52%

Code Coverage is in Public Preview. Learn more and provide us with your feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🫴 Ready

Development

Successfully merging this pull request may close these issues.

2 participants