Skip to content

fix: updater#146

Merged
jsonmaki merged 3 commits intoGUIfrom
2.8-dev
Feb 15, 2026
Merged

fix: updater#146
jsonmaki merged 3 commits intoGUIfrom
2.8-dev

Conversation

@jasoneri
Copy link
Owner

@jasoneri jasoneri commented Feb 15, 2026

Description

Related Issues

Checklist:

  • Have you checked to ensure there aren't other open Pull Requests for the same update/change?
  • Have you linted your code locally prior to submission?
  • Have you successfully ran app with your changes locally?

Summary by Sourcery

Introduce a native cross-platform updater and integrate it into the application update flow.

New Features:

  • Add a Rust-based installer binary with both CLI and GUI modes to handle ComicGUISpider updates and restarts.
  • Normalize and handle semantic and pre-release version strings when installing updates.

Enhancements:

  • Refactor the Python update logic to use either the bundled installer executable on Windows or direct uv commands with logging on other platforms.
  • Package the new installer executable alongside the existing Windows runtime artifacts.

Tests:

  • Add unit tests for version normalization and install-argument construction in the Rust installer.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 15, 2026

Reviewer's Guide

Replaces the previous Python-based self-update script with a dedicated native installer binary (with optional GUI) that orchestrates uv-based updates, adds robust version handling and logging, and ensures the Windows packer includes the installer executable.

Sequence diagram for the new update process using installer.exe

sequenceDiagram
    actor User
    participant Manager as GUI_manager
    participant Installer as installer.exe
    participant Uv as uv_exc
    participant NewApp as restarted_CGS

    User->>Manager: click_update_to_version(ver)
    Manager->>Manager: compute_index_url_and_log_path
    alt windows_and_installer_exists
        Manager->>Installer: spawn_with_args(version, uv_exc, index_url, parent_pid, uv_tool_dirs)
        Note over Installer: main
        Installer->>Installer: parse_CliArgs
        Installer->>Installer: InstallerConfig.from_args
        Installer->>Installer: wait_for_parent_exit(parent_pid)
        Installer->>Installer: cleanup_stale_dirs
        alt no_gui_flag
            Installer->>Installer: run_update_cli
            Installer->>Uv: tool_install_ComicGUISpider
            Uv-->>Installer: exit_code
        else gui_mode
            Installer->>Installer: run_gui
            Installer->>Installer: spawn_update_worker
            Installer->>Uv: tool_install_ComicGUISpider
            Uv-->>Installer: progress_output_and_exit_code
        end
        alt install_success
            Installer->>Installer: restart_cgs
            Installer->>Uv: tool_run_cgs
            Uv-->>NewApp: start_new_process
        end
    else non_windows_or_no_installer
        Manager->>Uv: spawn_shell_with_uv_tool_install_command
        Uv-->>Manager: install_logs_written
    end
    Manager->>Manager: close_gui
Loading

Class diagram for the new native installer components

classDiagram
    class CliArgs {
        +String uv_exc
        +String version
        +String index_url
        +u32 parent_pid
        +String uv_tool_dir
        +String uv_tool_bin_dir
        +bool no_gui
    }

    class InstallerConfig {
        +PathBuf uv_exc
        +String version
        +String index_url
        +PathBuf uv_tool_dir
        +PathBuf uv_tool_bin_dir
        +from_args(args~CliArgs~) InstallerConfig
        +log_path() PathBuf
        +install_args() Vec~String~
    }

    class InstallerEvent {
        <<enum>>
        Progress
        Finished
        Fatal
    }

    class UpdaterApp {
        -String version
        -String status
        -u8 progress
        -Receiver~InstallerEvent~ rx
        -Arc~AtomicI32~ exit_code
        -Option~Instant~ close_at
        +new(version String, rx Receiver~InstallerEvent~, exit_code Arc~AtomicI32~) UpdaterApp
        -drain_events() void
        +update(ctx Context, frame Frame) void
    }

    class VersionModule {
        +normalize_version(version String) (String, bool)
        +build_install_args(version String, index_url String) Vec~String~
    }

    class ProcessModule {
        +cleanup_stale_dirs(config InstallerConfig) Result~(),io::Error~
        +wait_for_parent_exit(parent_pid u32, timeout_ms u32) void
        +run_update_cli(config InstallerConfig) i32
        +spawn_update_worker(config InstallerConfig, tx Sender~InstallerEvent~) JoinHandle~()~
        +restart_cgs(config InstallerConfig) Result~(),io::Error~
    }

    CliArgs --> InstallerConfig : used_to_create
    InstallerConfig --> VersionModule : uses
    InstallerConfig --> ProcessModule : passed_into
    InstallerEvent --> UpdaterApp : drives_state
    VersionModule <.. ProcessModule : uses_functions
Loading

File-Level Changes

Change Details Files
Switch updater flow to use a native installer executable and fall back to direct uv CLI commands when the installer is unavailable.
  • Replace dynamic Python updater script generation with logic that prefers launching an installer.exe binary when present on Windows.
  • Pass uv executable path, target version, index URL, parent PID, and UV_TOOL_* environment variables as command-line arguments to the installer.
  • Add non-installer fallback paths that call uv tool install directly and capture logs into cgs_update.log for both Windows and POSIX systems.
  • Ensure updater always closes the main GUI after spawning the update process.
GUI/manager/__init__.py
Ensure build artifacts include the native installer in the Windows runtime zip so the GUI can launch it for updates.
  • Extend preset zip packaging list to include installer.exe alongside the main executable and runtime artifacts.
  • Keep packaging behavior for non-preset builds unchanged.
deploy/packer.py
Introduce a Rust-based cross-platform installer/updater binary with GUI and CLI modes, logging, progress reporting, and robust version normalization.
  • Define CLI argument parsing for uv path, target version, index URL, parent PID, UV tool directories, and a no-gui flag.
  • Implement InstallerConfig to derive configuration from CLI args and environment, compute install arguments, and resolve log locations.
  • Add cleanup of stale comicguispider_old directories and optional waiting on the parent process PID before starting updates.
  • Implement CLI update mode that runs uv tool install with proper environment, logs output, and restarts ComicGUISpider on success.
  • Implement a threaded GUI updater using eframe/egui that displays progress, status, and auto-closes on success.
  • Stream uv stdout/stderr, parse them with regexes to emit structured progress events (resolve, download, install phases), and write all lines to a log file.
  • Normalize and interpret PEP 440 versions (including v-prefixes and pre-release tags) and build uv install arguments accordingly, with tests for version handling.
  • Add a Windows-specific build script to embed an application manifest (UTF-8, DPI awareness, asInvoker), and configure Cargo metadata and release profile for the installer crate.
tools/installer/Cargo.toml
tools/installer/build.rs
tools/installer/src/main.rs
tools/installer/src/args.rs
tools/installer/src/process.rs
tools/installer/src/gui.rs
tools/installer/src/version.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In GUI/manager/__init__.py::to_update, using shlex.quote when building PowerShell and cmd command strings on Windows is likely to produce incorrectly escaped paths/arguments since shlex is POSIX-oriented; consider using subprocess.list2cmdline or manual quoting appropriate for cmd/PowerShell instead.
  • In tools/installer/src/process.rs::open_log, the fallback expect("cannot open any log file") will hard-crash the installer if both log paths fail; if logging is non-critical, consider gracefully degrading (e.g., using stderr only) instead of panicking.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `GUI/manager/__init__.py::to_update`, using `shlex.quote` when building PowerShell and `cmd` command strings on Windows is likely to produce incorrectly escaped paths/arguments since `shlex` is POSIX-oriented; consider using `subprocess.list2cmdline` or manual quoting appropriate for `cmd`/PowerShell instead.
- In `tools/installer/src/process.rs::open_log`, the fallback `expect("cannot open any log file")` will hard-crash the installer if both log paths fail; if logging is non-critical, consider gracefully degrading (e.g., using `stderr` only) instead of panicking.

## Individual Comments

### Comment 1
<location> `GUI/manager/__init__.py:194` </location>
<code_context>
             subprocess.Popen(args, creationflags=subprocess.CREATE_NEW_CONSOLE, env=env)
         else:
-            subprocess.Popen(args, start_new_session=True, env=env)
+            cmd = f"{uv_exc} tool install ComicGUISpider=={ver} --force --index-url {index_url}"
+            if os.name == "nt":
+                subprocess.Popen(["cmd", "/c", "start", "", "powershell", "-NoProfile", "-Command",
</code_context>

<issue_to_address>
**issue (bug_risk):** Command construction and quoting are fragile on Windows and may misbehave with spaces/special characters.

`cmd` is a flat f-string combining `uv_exc`, `ver`, and `index_url` and then embedded into a PowerShell `-Command` string. This introduces several risks on Windows:
- Spaces or special characters in `uv_exc`, `index_url`, or `log` can break the command.
- `shlex.quote()` is POSIX-focused and doesn't match PowerShell quoting semantics.
- Passing everything as one `-Command` string means characters like `&` or `?` in `index_url` can be interpreted by PowerShell.

Please restructure this to avoid stringly-typed command building: either call `uv_exc` directly with an argument list (skipping PowerShell), or build a PowerShell `Start-Process` with `-ArgumentList` where each argument is properly escaped for PowerShell rather than using `shlex.quote()`.
</issue_to_address>

### Comment 2
<location> `tools/installer/Cargo.toml:4` </location>
<code_context>
+[package]
+name = "cgs-installer"
+version = "2.8.6"
+edition = "2024"
+
+[[bin]]
</code_context>

<issue_to_address>
**issue (bug_risk):** Using `edition = "2024"` will currently fail to compile on stable Rust.

Currently only `2015`, `2018`, and `2021` are stable editions; `edition = "2024"` will make builds fail on standard stable toolchains. Unless you need unreleased 2024-only features (and are pinning to a matching nightly/preview toolchain), consider using `"2021"` instead, or clearly document and/or gate the requirement for a 2024-capable toolchain.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 15, 2026

Deploying comicguispider-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: db80701
Status: ✅  Deploy successful!
Preview URL: https://bb8eb79e.comicguispider-docs.pages.dev
Branch Preview URL: https://2-8-dev.comicguispider-docs.pages.dev

View logs

@jasoneri jasoneri requested a review from jsonmaki February 15, 2026 09:32
@jsonmaki jsonmaki merged commit 3ef9fe6 into GUI Feb 15, 2026
8 checks passed
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.

2 participants