Skip to content

feat(launchers/helper-go): Linux + Windows support#104

Open
nmgarza5 wants to merge 11 commits into
mainfrom
feat/launchers-cross-platform
Open

feat(launchers/helper-go): Linux + Windows support#104
nmgarza5 wants to merge 11 commits into
mainfrom
feat/launchers-cross-platform

Conversation

@nmgarza5
Copy link
Copy Markdown
Collaborator

Description

Cross-platform install for the launcher. macOS chain unchanged; Linux + Windows added via filename-suffixed build-tag modules + per-OS build scripts + per-OS backend routes.

  • internal/install/install_common.go — pure RenderLinuxDesktopFile / RenderWindowsRegistryCommands (testable from any platform)
  • install_linux.go — writes ~/.local/share/applications/agentwiki-launcher.desktop, runs xdg-mime + update-desktop-database
  • install_windows.goreg add HKCU\Software\Classes\agentwiki via reg.exe (no extra Go deps)
  • install_darwin.go (renamed) — same behavior, func renamed to Install for cross-OS dispatch
  • scripts/build-linux.sh — amd64+arm64 tarballs (binary + install.sh → ~/.local/bin)
  • scripts/build-windows.sh — amd64 zip (.exe + install.bat → %LOCALAPPDATA%). Unsigned for v1; SmartScreen warning documented. Authenticode is a follow-up.
  • Workflow: 3 parallel jobs (darwin keeps signing chain, linux/windows on ubuntu-latest cross-compile). Outputs darwin-/linux-/windows-artifact-name.
  • docker-build-push.yml + nightly-build.yml: pull all 3, verify each before docker build
  • Backend: /installer/{mac,linux,windows}; /installer/app kept as mac alias
  • FE InstallHelperPane: UA platform detect → per-OS download + install copy

Codex review pass 1 caught .desktop Exec quoting + no-fstring rule violations + xdg-mime stderr fallback. Fixed in same commit.

How Has This Been Tested?

nmgarza5 added 3 commits May 21, 2026 15:55
Cross-platform install for the agent-wiki helper. macOS chain stays
as-is; Linux + Windows added via build-tag-suffixed install_<os>.go
modules + per-OS build scripts + per-OS backend routes.

- internal/install: install_common.go (pure render helpers, testable
  from any platform) + install_{darwin,linux,windows}.go selected via
  filename suffix. macOS uses osacompile + LaunchServices (unchanged).
  Linux writes a freedesktop .desktop file + runs xdg-mime. Windows
  writes HKCU\Software\Classes\agentwiki via reg add (no extra deps).
- scripts/build-linux.sh + build-windows.sh: cross-compile + package
  each as a tarball/zip containing the binary + install.sh/install.bat
  that drops it in ~/.local/bin or %LOCALAPPDATA% and runs the
  install subcommand.
- Windows is unsigned for v1 — SmartScreen warning is documented in
  the README + FE install copy. Authenticode is a follow-up.
- CI workflow refactored from 1 job to 3 parallel jobs (darwin keeps
  the AWS-OIDC + signing chain; linux + windows run on ubuntu-latest
  with the Go cross-compiler, no secrets). workflow_call now outputs
  darwin-/linux-/windows-artifact-name.
- docker-build-push + nightly-build pull all 3 artifacts into
  backend/static/installers/ and verify each file before docker build.
- Backend: /installer/mac, /installer/linux?arch=amd64|arm64,
  /installer/windows. /installer/app stays as a back-compat mac alias.
- FE InstallHelperPane: UA-based platform detect → per-OS download
  button + install copy; unknown platform shows all 3.
freedesktop spec §6.5 says field codes (e.g. %u) inside quoted args
have unspecified behavior. Revert codex's well-meaning quote-add and
note the constraint in the helper's doc.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 21, 2026

Greptile Summary

Extends the Go launcher to macOS/Linux/Windows by adding build-tag-selected Install() implementations, per-OS build scripts, three new backend routes (/installer/{mac,linux,windows}), and a UA-aware frontend pane that serves the right download per detected platform.

  • install_linux.go writes a freedesktop .desktop file and calls xdg-mime; install_windows.go runs reg add under HKCU\Software\Classes\agentwiki; the macOS path is unchanged aside from a function rename. Pure rendering helpers in install_common.go keep the logic testable cross-platform.
  • The CI workflow splits into three parallel jobs (darwin / linux / windows); callers (docker-build-push.yml, nightly-build.yml) download all three artifacts and bake them into the backend image. The backend now serves per-platform routes with a back-compat /installer/app alias preserved.

Confidence Score: 4/5

The cross-platform wiring is structurally sound; the one defect worth fixing before merge is in the Windows install path.

The Windows Install() function silently drops a UserHomeDir error rather than returning it, meaning the .agentwiki config directory may never be created on affected machines and the launcher will fail later with no clear indication of the root cause. The Linux counterpart handles this correctly. Everything else — the CI workflow split, the new backend routes, the frontend UA detection, the build scripts, and the test coverage — looks correct.

packages/agentwiki-launcher-go/internal/install/install_windows.go — the UserHomeDir error handling; frontend/src/components/agents/InstallHelperPane.tsx — minor button variant inconsistency in the unknown-platform fallback.

Important Files Changed

Filename Overview
packages/agentwiki-launcher-go/internal/install/install_windows.go New Windows URL-scheme registration via reg.exe; silently swallows UserHomeDir error on the .agentwiki dir creation step, diverging from the Linux counterpart that propagates it.
packages/agentwiki-launcher-go/internal/install/install_linux.go New Linux URL-scheme registration via freedesktop .desktop file + xdg-mime; errors properly propagated, non-fatal xdg-tool failures correctly degraded to stderr warnings.
packages/agentwiki-launcher-go/internal/install/install_common.go Pure helpers for rendering the Linux .desktop template and Windows reg-add command slices; no side effects, well-tested in install_test.go.
packages/agentwiki-launcher-go/internal/install/install_darwin.go Renamed from darwin.go; function renamed from InstallDarwin to Install for cross-OS dispatch; behavior unchanged.
packages/agentwiki-launcher-go/internal/install/install_test.go New unit tests for RenderLinuxDesktopFile and RenderWindowsRegistryCommands; runs on any platform, good coverage of key substrings and command shape.
frontend/src/components/agents/InstallHelperPane.tsx UA platform detection via useEffect avoids SSR mismatch; unknown-platform fallback shows all three OS buttons but Linux/Windows lack variant="action", creating visual inconsistency.
backend/app/api/installer.py Adds /installer/mac, /installer/linux?arch=, /installer/windows routes via a shared _stream helper; /installer/app kept as back-compat alias; f-string removed from shell script body to avoid accidental interpolation.
backend/tests/test_installer_api.py Comprehensive new tests for all three platform routes including missing-file 503, arch validation 404, and alias back-compat; follows existing test conventions.
.github/workflows/release-agentwiki-launcher-go.yml Splits single release job into three parallel darwin/linux/windows jobs; darwin keeps macOS runner + OIDC signing chain, linux/windows cross-compile on ubuntu-latest with no secrets; outputs renamed accordingly.
.github/workflows/docker-build-push.yml Downloads all three platform artifacts before Docker build; verification step checks all four expected filenames.
.github/workflows/nightly-build.yml Mirrors docker-build-push.yml changes; three download steps correctly gated behind matrix.component == 'backend'.
packages/agentwiki-launcher-go/scripts/build-linux.sh Cross-compiles amd64 + arm64, bundles binary + install.sh into per-arch tarballs; install.sh correctly invokes the binary's own install subcommand.
packages/agentwiki-launcher-go/scripts/build-windows.sh Cross-compiles amd64 Windows exe, bundles with install.bat; falls back to Python's zipfile if zip CLI is unavailable; SmartScreen limitation documented.
packages/agentwiki-launcher-go/cmd/agentwiki-launcher/main.go Drops the runtime.GOOS darwin-only guard and calls install.Install(self) directly; now correctly dispatches to the OS-specific implementation selected at compile time.
packages/agentwiki-launcher-go/Makefile Adds release, release-linux, release-windows targets delegating to the build scripts; .PHONY updated accordingly.
packages/agentwiki-launcher-go/README.md Updated to document cross-platform install behavior, new make targets, and per-OS distribution table.

Sequence Diagram

sequenceDiagram
    participant FE as InstallHelperPane
    participant UA as navigator.userAgent
    participant BE as Backend /installer/…
    participant FS as static/installers/

    FE->>UA: detectPlatform()
    UA-->>FE: "mac" | "linux" | "windows" | "unknown"

    alt mac
        FE->>BE: GET /installer/mac
        BE->>FS: AgentWikiLauncher.zip
        FS-->>BE: file stream
        BE-->>FE: application/zip
    else linux
        FE->>BE: "GET /installer/linux?arch=amd64"
        BE->>FS: agentwiki-launcher-linux-amd64.tar.gz
        FS-->>BE: file stream
        BE-->>FE: application/gzip
    else windows
        FE->>BE: GET /installer/windows
        BE->>FS: agentwiki-launcher-windows-amd64.zip
        FS-->>BE: file stream
        BE-->>FE: application/zip
    end

    Note over FE: User runs install.sh / install.bat / drags .app
    FE->>FE: launcher install subcommand
    Note over FE: agentwiki:// URL scheme registered
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/agentwiki-launcher-go/internal/install/install_windows.go:33-37
Silent error swallow on `UserHomeDir` — the outer `if` condition eats any failure, so if the home directory can't be resolved the `.agentwiki` config dir is silently skipped. The launcher will later fail to write its endpoint config with no indication of why. The Linux counterpart returns the error unconditionally; Windows should do the same.

```suggestion
	home, err := os.UserHomeDir()
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Join(home, ".agentwiki"), 0o755); err != nil {
		return err
	}
```

### Issue 2 of 2
frontend/src/components/agents/InstallHelperPane.tsx:131-142
In the `unknown`-platform fallback block, only the macOS button carries `variant="action"`. The Linux and Windows buttons render with the default (secondary) style, making all three look like different levels of priority on SSR/first-paint where the platform hasn't been resolved yet. They should share the same variant.

```suggestion
              <Button
                size="md"
                variant="action"
                onClick={() => download("/api/installer/linux?arch=amd64")}
              >
                Download for Linux (amd64)
              </Button>
              <Button
                size="md"
                variant="action"
                onClick={() => download("/api/installer/windows")}
              >
                Download for Windows
              </Button>
```

Reviews (1): Last reviewed commit: "fix(launchers/helper-go): unquote launch..." | Re-trigger Greptile

Comment on lines +33 to +37
if home, err := os.UserHomeDir(); err == nil && home != "" {
if err := os.MkdirAll(filepath.Join(home, ".agentwiki"), 0o755); err != nil {
return err
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Silent error swallow on UserHomeDir — the outer if condition eats any failure, so if the home directory can't be resolved the .agentwiki config dir is silently skipped. The launcher will later fail to write its endpoint config with no indication of why. The Linux counterpart returns the error unconditionally; Windows should do the same.

Suggested change
if home, err := os.UserHomeDir(); err == nil && home != "" {
if err := os.MkdirAll(filepath.Join(home, ".agentwiki"), 0o755); err != nil {
return err
}
}
home, err := os.UserHomeDir()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Join(home, ".agentwiki"), 0o755); err != nil {
return err
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentwiki-launcher-go/internal/install/install_windows.go
Line: 33-37

Comment:
Silent error swallow on `UserHomeDir` — the outer `if` condition eats any failure, so if the home directory can't be resolved the `.agentwiki` config dir is silently skipped. The launcher will later fail to write its endpoint config with no indication of why. The Linux counterpart returns the error unconditionally; Windows should do the same.

```suggestion
	home, err := os.UserHomeDir()
	if err != nil {
		return err
	}
	if err := os.MkdirAll(filepath.Join(home, ".agentwiki"), 0o755); err != nil {
		return err
	}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +131 to +142
<Button
size="md"
onClick={() => download("/api/installer/linux?arch=amd64")}
>
Download for Linux (amd64)
</Button>
<Button
size="md"
onClick={() => download("/api/installer/windows")}
>
Download for Windows
</Button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 In the unknown-platform fallback block, only the macOS button carries variant="action". The Linux and Windows buttons render with the default (secondary) style, making all three look like different levels of priority on SSR/first-paint where the platform hasn't been resolved yet. They should share the same variant.

Suggested change
<Button
size="md"
onClick={() => download("/api/installer/linux?arch=amd64")}
>
Download for Linux (amd64)
</Button>
<Button
size="md"
onClick={() => download("/api/installer/windows")}
>
Download for Windows
</Button>
<Button
size="md"
variant="action"
onClick={() => download("/api/installer/linux?arch=amd64")}
>
Download for Linux (amd64)
</Button>
<Button
size="md"
variant="action"
onClick={() => download("/api/installer/windows")}
>
Download for Windows
</Button>
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/components/agents/InstallHelperPane.tsx
Line: 131-142

Comment:
In the `unknown`-platform fallback block, only the macOS button carries `variant="action"`. The Linux and Windows buttons render with the default (secondary) style, making all three look like different levels of priority on SSR/first-paint where the platform hasn't been resolved yet. They should share the same variant.

```suggestion
              <Button
                size="md"
                variant="action"
                onClick={() => download("/api/installer/linux?arch=amd64")}
              >
                Download for Linux (amd64)
              </Button>
              <Button
                size="md"
                variant="action"
                onClick={() => download("/api/installer/windows")}
              >
                Download for Windows
              </Button>
```

How can I resolve this? If you propose a fix, please make it concise.

nmgarza5 added 8 commits May 21, 2026 16:02
… variant in unknown-platform fallback

Greptile #104:
- P1 install_windows.go — match Linux: fail loudly on UserHomeDir error
- P2 InstallHelperPane.tsx — Linux/Windows fallback buttons need variant="action" to match macOS
Tightens the cross-platform packaging from "zip + installer script" to
single-file downloads where possible, and ports the rest of the
dispatch flow (Pin/Switch dialogs, terminal spawn) so the new packages
actually work end-to-end on Linux + Windows — not just install.

Packaging
- Windows: single .exe linked with -H windowsgui (no console flash on
  every agentwiki:// dispatch). Drops install.bat + zip wrapper. User
  downloads .exe, double-clicks, SmartScreen → "More info" → "Run
  anyway", a MessageBox confirms install succeeded.
- Linux: AppImage (amd64) is the default — chmod +x, double-click,
  done. install_linux.go picks up $APPIMAGE so the .desktop entry
  written to ~/.local/share/applications points at the AppImage file,
  not the ephemeral FUSE mount. Tarball stays as fallback for arm64
  and distros that prefer manual install.

Dispatch flow (the install was the easy part)
- internal/dialog package: ConfirmPin / ConfirmSwitch with per-OS
  build-tag impls — osascript (mac), zenity/kdialog (linux), mshta
  MsgBox (windows). Pure helpers (escapeAppleScript, vbQuote) in
  dialog_common.go so tests run on every OS.
- internal/terminal package: terminal_<os>.go per build tag. Linux
  picks $TERMINAL / x-terminal-emulator / gnome-terminal / konsole /
  xfce4-terminal / mate-terminal / tilix / xterm in that order, falls
  back to headless bash if none found. Windows writes a .bat wrapper
  and opens `cmd /c start cmd /k wrapper.bat` so the window stays
  alive for the CLI session.
- Windows error visibility: -H windowsgui detaches stderr from any
  console; main.go now tees errors to ~/.agentwiki/stub.log so silent
  GUI failures are diagnosable.

Routes + FE
- /installer/linux accepts format=appimage (default) | tar.gz with
  arch=amd64|arm64.
- /installer/windows now serves the .exe (application/octet-stream).
- FE InstallHelperPane: AppImage download link with chmod+x guidance;
  .exe link with SmartScreen guidance.

CI
- linux job installs appimagetool (--appimage-extract trick since GH
  runners have no FUSE), builds tarballs + AppImage, uploads both.
- windows job uploads .exe (no zip).
- docker-build-push + nightly verify the 5 expected files.
AppImage still requires `chmod +x` once before first run — annoying for
mac-parity UX. Add .deb (Debian / Ubuntu / Mint) + .rpm (Fedora / RHEL
/ openSUSE) so double-click goes through Software Center / GNOME
Software / KDE Discover and the URL handler is registered via postinst.
Zero terminal, zero chmod.

- scripts/build-deb.sh — dpkg-deb pack with /usr/bin + /usr/share/applications
  layout, postinst+postrm refresh desktop database. xdg-utils dep.
- scripts/build-rpm.sh — rpmbuild with matching layout + %post/%postun.
  Requires Linux (macOS brew rpm refuses x86_64 cross-build).
- Makefile: release-deb + release-rpm targets.
- CI: apt-get install rpm, run both scripts, upload artifacts alongside
  AppImage + tarballs.
- docker-build-push + nightly: verify .deb + .rpm files present too.
- backend installer.py: /installer/linux?format=deb (NEW default),
  format=rpm, format=appimage, format=tar.gz. Versioned filenames
  resolved via glob at request time.
- FE InstallHelperPane: default Linux download = .deb; instructions list
  the rpm / appimage / tar.gz fallbacks.
rpmbuild on Fedora / RHEL inserts a dist tag (.el9, .fc40) between the
release number and arch, producing filenames like
agentwiki-launcher-0.9.0-1.el9.x86_64.rpm. The previous glob
`agentwiki-launcher-*-1.x86_64.rpm` required the literal sequence
`1.x86_64`, missing every dist-tagged build. CI rpmbuild on ubuntu
happens to omit the dist tag, which is why local + GH-artifact paths
worked but the rpm-picks-newest-version test (which simulates real
distro outputs) failed.

Glob is now `agentwiki-launcher-*-1*.x86_64.rpm` — matches both
plain and dist-tagged releases. Verified locally:
  matched: agentwiki-launcher-0.10.0-1.fc40.x86_64.rpm
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.

1 participant