Skip to content

refactor(plugin install): speed up plugin installation with native ZIP extraction - #2715

Closed
codewithchai0605 wants to merge 8 commits into
Acode-Foundation:mainfrom
codewithchai0605:feat/better-plugin-installer
Closed

refactor(plugin install): speed up plugin installation with native ZIP extraction#2715
codewithchai0605 wants to merge 8 commits into
Acode-Foundation:mainfrom
codewithchai0605:feat/better-plugin-installer

Conversation

@codewithchai0605

Copy link
Copy Markdown

Summary

This PR improves plugin installation performance, especially for plugins containing many small files such as file/folder icon providers.

The previous installer extracted archive entries in JavaScript with a concurrency limit of 2. This caused significant overhead from repeated JS-to-native filesystem calls.

This change moves archive extraction to native Android code and writes files directly from the ZIP archive.

Changes

  • Extract plugin ZIP archives natively using ZipFile.
  • Stream archive entries directly to a staging directory.
  • Activate updates through a directory swap, preserving the existing plugin if extraction fails.
  • Restore the previous plugin installation if an earlier update was interrupted during the swap.
  • Prevent concurrent installations of the same plugin.
  • Validate plugin IDs and archive paths to prevent path traversal.
  • Add archive size and entry-count limits.
  • Optimize extraction for many small files:
    • shared 64 KiB buffer
    • direct output streams
    • no per-entry canonical filesystem lookup

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR moves plugin ZIP extraction into the Android bridge and activates extracted contents through staging and backup directory swaps.

  • Adds native archive path validation, size limits, and direct streaming extraction.
  • Adds per-plugin installation exclusion and recovery of interrupted swaps.
  • Updates JavaScript cleanup to distinguish fresh installations from updates and recovered installations.

Confidence Score: 4/5

The PR should not merge until orphan recovery parses temporary directory ownership unambiguously, because valid plugin IDs can currently cause recovery to restore or delete the wrong directory.

The global recovery sweep interprets .install- and .backup- substrings inside otherwise valid plugin IDs as structural delimiters, allowing interrupted-install artifacts to be misclassified and mishandled.

Files Needing Attention: src/plugins/pluginContext/src/android/Tee.java

Important Files Changed

Filename Overview
src/lib/installPlugin.js Delegates extraction to the native bridge, tracks pre-extraction installation state, and removes failed fresh installations after activation.
src/plugins/pluginContext/src/android/Tee.java Implements native staged ZIP extraction and interrupted-swap recovery, but marker-substring parsing can assign temporary directories to the wrong plugin or type.

Sequence Diagram

sequenceDiagram
    participant JS as installPlugin.js
    participant Tee as Tee.extractPluginArchive
    participant FS as Plugin filesystem
    JS->>Tee: extract(archive, destination, manifest)
    Tee->>FS: Recover orphan backups
    Tee->>FS: Extract into .install-UUID
    alt Existing plugin
        Tee->>FS: Rename destination to .backup-UUID
    end
    Tee->>FS: Rename staging to destination
    Tee->>FS: Delete superseded backup
    Tee-->>JS: "success({ recovered })"
    JS->>JS: Load and initialize plugin
    alt Fresh plugin fails to load
        JS->>FS: Delete activated destination
    end
Loading

Reviews (4): Last reviewed commit: "fix: restore cross-plugin backups during..." | Re-trigger Greptile

Comment thread src/lib/installPlugin.js
Comment thread src/plugins/pluginContext/src/android/Tee.java
@UnschooledGamer UnschooledGamer added community and removed enhancement New feature or request labels Aug 14, 2026
…ulation

installPlugin.js: Move pluginWasInstalled check to after extractPluginArchive so that native recovery (restoreInterruptedInstall) has already run before the flag is recorded. Previously, a recovered update could be misclassified as a fresh install, causing the error handler to delete the restored directory.

Tee.java: Expand restoreInterruptedInstall to also clean up orphaned .install-* staging directories alongside .backup-* directories, preventing indefinite storage accumulation from interrupted extractions.
@codewithchai0605

Copy link
Copy Markdown
Author

@greptile-apps review

Comment thread src/lib/installPlugin.js Outdated
Comment thread src/plugins/pluginContext/src/android/Tee.java Outdated
@codewithchai0605 codewithchai0605 changed the title speed up plugin installation with native ZIP extraction refactor(plugin install): speed up plugin installation with native ZIP extraction Aug 16, 2026
- installPlugin.js: Move pluginWasInstalled check back to before extraction to restore correct fresh-install cleanup, overriding it with the native recovered flag from extractPluginArchive when native backup recovery is detected.

- Tee.java: Make restoreInterruptedInstall return a boolean to indicate backup recovery to the JS layer. Broaden orphan cleanup to remove abandoned staging and backup directories for ANY plugin, skipping only active extractions. This fixes indefinite staging dir accumulation without requiring the user to re-download the same plugin.
@codewithchai0605

Copy link
Copy Markdown
Author

@greptile-apps review

Comment thread src/plugins/pluginContext/src/android/Tee.java

@RohitKushvaha01 RohitKushvaha01 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why did you change Tee.java? It's for security related stuff this doesn't make any sense put that code in System.java

Tee.java: The global sweep for orphaned staging and backup directories could permanently delete another plugin's only recoverable backup if its own update was interrupted. This refactors the sweep to first identify and restore the newest backup for any plugin missing its destination directory, and strictly protects any remaining inactive backups whose destinations are missing.
@codewithchai0605

Copy link
Copy Markdown
Author

@greptile-apps review

Comment on lines +379 to +381
int typeIdx = name.lastIndexOf(".install-");
boolean isBackup = typeIdx < 0;
if (isBackup) typeIdx = name.lastIndexOf(".backup-");

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.

P1 Marker substrings corrupt orphan recovery

When a valid plugin ID contains .install- or .backup-, the orphan sweep interprets that part of the ID as a structural delimiter. A backup can consequently be classified as staging and lose its preservation guard, or staging contents can be restored under an unrelated plugin name, causing recoverable plugin data to be deleted or activated at the wrong destination.

Knowledge Base Used: Plugin System

@github-project-automation github-project-automation Bot moved this from Backlog to Done in The Code Board - Acode Aug 19, 2026
@codewithchai0605
codewithchai0605 deleted the feat/better-plugin-installer branch August 19, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants