Skip to content

ADFA-2498 Plumbing for plugin-artifacts.zip#792

Merged
hal-eisen-adfa merged 2 commits intostagefrom
ADFA-2498-Update-BundledAssetsInstaller-for-plugin-artifacts
Jan 7, 2026
Merged

ADFA-2498 Plumbing for plugin-artifacts.zip#792
hal-eisen-adfa merged 2 commits intostagefrom
ADFA-2498-Update-BundledAssetsInstaller-for-plugin-artifacts

Conversation

@hal-eisen-adfa
Copy link
Collaborator

Add creation and upload of plugins-artifacts.zip to generate_assets.yml

Finish removing SSH from release.yml

Tweak .gitignore to fix overly broad handling of "assets"

Add bundlePluginArtifactsForRelease to Gradle

Set up download URL for plugin-artifacts.zip.br

Add how to handle plugin-artifacts.zip to BundledAssetsInstaller.kt

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Release Notes - Plugin Artifacts Infrastructure

Changes Overview

  • Workflows
    • Added creation of plugin artifacts and V7/Llama assets in .github/workflows/generate_assets.yml (new "Create Release Assets" step).
    • Added conditional upload step in generate_assets.yml to SCP assets to a remote server when SCP_HOST is set ("Upload Release Assets to Server").
    • Removed SSH key setup and SSH cleanup steps from .github/workflows/release.yml (SSH usage removed).
  • Repository config
    • Removed the assets/ ignore rule from .gitignore so assets can be tracked.
  • Build system
    • Added Gradle task bundlePluginArtifactsForRelease (depends on createPluginArtifactsZip).
    • Task writes assets/release/common/data/common/plugin-artifacts.zip.br when brotli available, otherwise writes uncompressed plugin-artifacts.zip.
    • assembleV8Release and assembleV7Release now depend on bundlePluginArtifactsForRelease.
    • Added release asset entry for plugin-artifacts.zip.br (download URL configured).
  • Runtime/installers
    • Added AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP constant.
    • BundledAssetsInstaller and SplitAssetsInstaller updated to extract plugin-artifacts.zip (handles brotli-compressed ZIP, extracts entries with path-traversal checks and per-entry logging).

Risks & Best Practice Concerns

  • Binary assets in Git: Removing assets/ from .gitignore may cause large binary assets to be committed, inflating repository size and slowing clones.
  • Inconsistent auth model: SSH key setup was removed from release.yml while generate_assets.yml adds an SCP upload path; authentication strategy across workflows is unclear and may cause failures.
  • Hardcoded values:
  • Upload/error handling:
    • SCP upload step only warns/skips when SCP_HOST is unset or assets missing; lacks robust failure handling, retries, or rollback semantics on upload errors.
    • No integrity verification after brotli compression (no checksum/validation step).
  • Security considerations:
    • Extraction implements path-traversal checks, but reviewers should validate correctness and ensure all edge cases are handled (symlinks, unusual ZIP entries).
    • Commit message references a security fix for pluginEntry.name — reviewers should verify that name handling and sanitization are complete and consistent.
  • Workflow hygiene:
    • Removing SSH setup without documenting alternate credentials or secrets management may break CI/CD for downstream consumers; migration documentation is missing.

Recommended Action Items for Review

  • Confirm decision and process for tracking binary assets in git; consider storing large assets externally (CDN, artifact storage) or using Git LFS.
  • Clarify and unify authentication for uploads (SCP vs SSH keys) and document required secrets/configuration.
  • Replace the hardcoded size and URL with computed values or configuration; add tests/guardrails if sizes are assumed.
  • Add post-compression integrity checks (e.g., checksum) and robust error handling/retries for uploads.
  • Audit the pluginEntry.name fix and all extraction/security logic (symlink handling, ZIP entry edge cases).
  • Add documentation for how to produce, upload, and consume plugin-artifacts.zip in CI and release processes.

Walkthrough

Adds bundling and distribution of plugin-artifact release assets: new Gradle task to produce compressed plugin-artifacts, GitHub Actions steps to create and optionally SCP-upload release assets, removes SSH setup/cleanup in release workflow, and app code to extract the new plugin-artifacts ZIP during installation.

Changes

Cohort / File(s) Summary
CI/CD Workflow Updates
.github/workflows/generate_assets.yml, .github/workflows/release.yml
generate_assets.yml: adds "Create Release Assets" Gradle step and conditional "Upload Release Assets to Server" (SCP) step. release.yml: removes "Set up SSH key" from download_assets and removes "Cleanup ssh" from build_apk.
Repository Ignore Rules
.gitignore
Removed the assets/ ignore entry so assets can be tracked in the repo.
Gradle Build Configuration
app/build.gradle.kts
New task bundlePluginArtifactsForRelease that depends on createPluginArtifactsZip, writes to assets/release/.../plugin-artifacts.zip.br (brotli if available), and wires this task into assembleV7Release/assembleV8Release; adds the compressed plugin artifact to the release asset catalog.
Bundled Assets Extraction (app)
app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt, app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt, app/src/main/java/com/itsaky/androidide/assets/AssetsInstallationHelper
Added handling for PLUGIN_ARTIFACTS_ZIP: brotli-compressed ZIP extraction into plugin directory with path-traversal safety checks; updated expected size mapping and imports (ZipInputStream) and ensured safe target-path normalization/validation during extraction.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Actions
    participant Gradle as Gradle (runner)
    participant Repo as Repository (assets/)
    participant Server as Remote Server (SCP) 
    participant App as App Installer (runtime)

    GH->>Gradle: run bundlePluginArtifactsForRelease (and V7/V8 bundles)
    Gradle-->>Repo: write assets/release/.../plugin-artifacts.zip.br
    alt SCP_HOST set and assets exist
        GH->>Server: scp upload assets/release/* -> /remote/path
        Server-->>GH: upload OK
    else no SCP or missing assets
        GH-->>GH: emit warning, skip upload
    end
    Note right of App: At runtime
    App->>Repo: read bundled asset PLUGIN_ARTIFACTS_ZIP
    App->>App: brotli-decompress -> ZipInputStream -> extract files (with path checks)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • Daniel-ADFA
  • jomen-adfa

Poem

🐰 In burrows of code I quietly hop,

I bundle plugin treats on a tiny hop.
Compressed and shipped, no keys to misplace,
Assets snug in release, tucked in their space.
A carrot for CI — hop, build, and race! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly references the ticket number and the main change: adding plumbing for plugin-artifacts.zip handling across the codebase.
Description check ✅ Passed The description is directly related to the changeset, covering all major modifications including workflow updates, Gradle configuration, asset handling, and security fixes.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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: 2

🤖 Fix all issues with AI agents
In @app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt:
- Around line 146-148: The code creates pluginDir from
Environment.PLUGIN_API_JAR.parentFile and calls pluginDir.mkdirs() without
checking the return value; update the BundledAssetsInstaller (where pluginDir is
computed from Environment.PLUGIN_API_JAR) to capture the boolean result of
pluginDir.mkdirs() and throw or log a clear IllegalStateException if it returns
false (and the directory does not already exist/is not a directory), so failures
to create the directory are detected and handled.
- Around line 157-161: The code writing ZIP entries uses pluginEntry.name
directly which allows path traversal; change the extraction to sanitize and
validate names: for each ZipEntry (pluginEntry) obtain a normalized relative
path (e.g., Paths.get(pluginEntry.name).normalize()), reject entries that are
absolute or that contain ".." or that resolve outside the intended pluginDir by
resolving pluginDir.resolve(normalizedRelative) and verifying the resulting
targetPath.toRealPath().startsWith(pluginDir.toRealPath()) (or compare
normalized paths), create parent directories as needed, handle directory entries
separately, and only open output streams for regular files; replace the direct
pluginDir.resolve(pluginEntry.name) and pluginZip.copyTo(output) usage with this
safe resolution + check before writing.
🧹 Nitpick comments (1)
.github/workflows/generate_assets.yml (1)

162-168: Task invocation is correct but slightly redundant.

The step calls bundleV7LlamaAssets and bundleV8LlamaAssets, which depend on assembleV7Assets and assembleV8Assets respectively. Since these were already invoked in the "Assemble Assets" step (line 156), Gradle will mark them as UP-TO-DATE and skip re-execution. This redundancy is safe but results in a slightly longer task resolution phase.

The critical part—invoking bundlePluginArtifactsForRelease—is correct and necessary.

🔎 Optional optimization

If you want to eliminate the redundancy, you could call only the new task:

       - name: Create Release Assets
         run: |
-          flox activate -d flox/base -- ./gradlew :app:bundleV7LlamaAssets :app:bundleV8LlamaAssets :app:bundlePluginArtifactsForRelease --no-daemon \
+          flox activate -d flox/base -- ./gradlew :app:bundlePluginArtifactsForRelease --no-daemon \
             -Dorg.gradle.jvmargs="-Xmx10g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED" \
             -Dandroid.aapt2.daemonHeapSize=4096M \
             -Dorg.gradle.workers.max=1 \
             -Dorg.gradle.parallel=false

However, explicitly listing all three tasks makes the workflow's intent clearer and documents what assets are being bundled, which may be preferable from a maintainability perspective.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56633f4 and a5d1904.

📒 Files selected for processing (5)
  • .github/workflows/generate_assets.yml
  • .github/workflows/release.yml
  • .gitignore
  • app/build.gradle.kts
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
💤 Files with no reviewable changes (2)
  • .gitignore
  • .github/workflows/release.yml
🔇 Additional comments (7)
app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt (2)

24-24: LGTM!

The import is necessary for handling ZIP stream extraction in the new plugin artifacts logic.


181-181: The constant AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP is properly defined in AssetsInstallationHelper.kt at line 60 with value "plugin-artifacts.zip" and follows the established naming convention. The asset size mapping at line 181 is correct.

app/build.gradle.kts (4)

679-735: LGTM! Task follows established patterns.

The bundlePluginArtifactsForRelease task implementation correctly mirrors the existing bundleLlamaAssets pattern:

  • Proper dependency on createPluginArtifactsZip
  • Robust Brotli availability check with graceful fallback
  • Appropriate error handling for missing source files
  • Clean file management (ensures only compressed or uncompressed version exists)

780-780: Dependency correctly added.

The addition of bundlePluginArtifactsForRelease as a dependency ensures plugin artifacts are properly bundled for V8 release builds.


797-797: Dependency correctly added.

The addition of bundlePluginArtifactsForRelease as a dependency ensures plugin artifacts are properly bundled for V7 release builds.


994-995: Asset entry correctly configured.

The plugin-artifacts.zip.br asset is properly added to the release assets list with correct paths and URL. The entry follows the established pattern for release assets.

Note: Ensure that the corresponding .md5 checksum file is generated and uploaded to the server alongside the asset, consistent with how other release assets are handled (referenced in assetsFileChecksum function at lines 1074-1103).

.github/workflows/generate_assets.yml (1)

170-186: LGTM! Proper error handling and guards.

The upload step correctly handles missing configuration and missing directories:

  • Checks for SCP_HOST before attempting upload
  • Verifies assets/release directory exists
  • Uses exit 0 to allow the workflow to continue gracefully if conditions aren't met
  • SCP command properly uploads the release assets to the target server

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt (1)

116-118: Consider checking mkdirs() result.

While unlikely to fail, verifying the result of mkdirs() would catch directory creation failures explicitly.

🔎 Proposed improvement
                                val pluginDir = Environment.PLUGIN_API_JAR.parentFile
                                    ?: throw IllegalStateException("Plugin API parent directory is null")
-                               pluginDir.mkdirs()
+                               if (!pluginDir.exists() && !pluginDir.mkdirs()) {
+                                   throw IllegalStateException("Failed to create plugin directory: ${pluginDir.absolutePath}")
+                               }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5d1904 and fd2f459.

📒 Files selected for processing (2)
  • app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt
  • app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build Universal APK
🔇 Additional comments (4)
app/src/main/java/com/itsaky/androidide/assets/SplitAssetsInstaller.kt (1)

119-131: Excellent path traversal protection.

The security implementation correctly prevents path traversal attacks by:

  • Normalizing the base directory path
  • Resolving each entry against the base
  • Validating the resolved path stays within bounds

This approach effectively mitigates the risk of malicious ZIP entries escaping the target directory.

app/src/main/java/com/itsaky/androidide/assets/BundledAssetsInstaller.kt (3)

24-24: LGTM!

Import addition is appropriate for the new ZIP extraction functionality.


158-164: Path traversal protection correctly implemented.

The security check properly prevents malicious ZIP entries from escaping the target directory by normalizing paths and validating with startsWith(). This addresses the critical vulnerability from the previous review.


190-190: Expected size value added for plugin artifacts.

The expected size 86442L has been added for AssetsInstallationHelper.PLUGIN_ARTIFACTS_ZIP in the expectedSize() function at line 190. This value (~86 KB) represents the expected compressed size of the plugin artifacts asset and is used for progress tracking during installation. The value appears reasonable compared to other bundled assets, and the addition properly integrates with the existing asset size mapping in the when-expression.

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