Skip to content

Conversation

@rainxchzed
Copy link
Owner

@rainxchzed rainxchzed commented Jan 10, 2026

Summary by CodeRabbit

  • New Features

    • Added comprehensive Flatpak packaging and distribution support for Linux, including binary and source workflows to build, package, export, and run the app.
  • Chores

    • Added app metadata, desktop and appdata descriptors for Flatpak distribution.
    • Added idempotent automated Flatpak build/export/run steps for more reliable releases.
    • Exposed build-time configuration (version/client ID) and adjusted platform-specific dependency placements.

✏️ Tip: You can customize this high-level summary in your review settings.

This commit introduces the necessary configurations and scripts to package the application as a Flatpak for Linux distributions.

Key changes include:
*   **Gradle Tasks**: New tasks (`packageFlatpak`, `exportFlatpak`, `runFlatpak`, and source-based variants) are added to `build.gradle.kts` to automate the Flatpak build and bundling process.
*   **Flatpak Manifests**: Added `manifest.yml` for binary-based builds and `zed.rainxch.githubstore.yml` for source-based builds, defining the application's dependencies and build steps.
*   **Desktop Integration**: Included a `.desktop` entry file for application launchers, an AppStream metadata file (`.appdata.xml`) for software centers, and a new application icon.
This commit refactors the Gradle tasks responsible for building Flatpak bundles (`buildFlatpakBinary`, `exportFlatpakBinary`, `buildFlatpakSource`, `exportFlatpakSource`) to improve their reliability and portability.

Key changes include:
*   Using absolute paths for build and project directories to prevent issues when running Gradle from different locations.
*   Implementing a clean-and-recreate step for the Flatpak build directories to avoid potential file permission errors during subsequent builds.
*   Adding a conditional copy for `generated-sources.json` in the source-based build, allowing the task to run successfully even if the file does not exist.
This commit refactors the Flatpak-related Gradle tasks (`packageFlatpak`, `exportFlatpak`, `runFlatpak`, and their source-based variants) to be of type `Exec`.

This change simplifies the task definitions by moving setup logic into `doFirst` blocks and using `workingDir` and `commandLine` directly on the task, which improves readability and aligns with Gradle's best practices for task configuration.
This commit refactors the Flatpak packaging tasks in `composeApp/build.gradle.kts` to improve structure and address Gradle configuration cache issues.

Key changes include:
*   Extracting file copying logic from `packageFlatpak` and `packageFlatpakSource` tasks into separate `Copy` tasks (`copyFlatpakBinary`, `copyFlatpakResources`, etc.).
*   Using lazy configuration for task properties like `workingDir` to align with modern Gradle practices.
*   Replacing direct file operations within `doFirst` blocks with declarative `Copy` tasks, ensuring better task dependency management and cacheability.
*   Simplifying variable access and path handling within the tasks.
Refactored the Flatpak build tasks in `composeApp/build.gradle.kts` to improve readability and maintainability.

Key changes include:
- Extracted frequently used paths and values into local `val` variables (e.g., `appImageOutput`, `resourcesDir`, `buildDir`, `repoDir`).
- Simplified the `doFirst` block in `copyFlatpakBinary` and `copyFlatpakSourceResources` to use `outputs.files.singleFile` for deleting the target directory.
- In `manifest.yml`, consolidated the `sources` to copy the entire build directory (`path: .`) while excluding the manifest files, simplifying file management.
- Added installation of the `appdata.xml` file to the manifest to ensure metainfo is included in the Flatpak build.
This commit introduces two main changes to the Flatpak configuration:

*   Adds the `--disable-rofiles-fuse` flag to the `flatpak-builder` command in the Gradle build script. This addresses potential build or runtime issues related to filesystem permissions.
*   Updates the `appdata.xml` file to conform to modern standards by moving the project license, adding a metadata license, and including developer and content rating information.
The `zed.rainxch.githubstore.desktop` file has been updated:
*   The `Version` is set to `1.0`.
*   The `Comment` is changed to "Discover and install apps from GitHub releases".
*   The `Categories` are simplified to just `Development`.

Additionally, the application icon (`app_icon.png`) has been updated.
This commit updates the Flatpak manifest to:
- Enable Inter-Process Communication (IPC) by adding `--share=ipc`.
- Set the default X11 display by adding the environment variable `--env=DISPLAY=:0`.
Removes unnecessary socket and display environment variable settings from the Flatpak manifest. Sets `GDK_BACKEND=x11` to ensure compatibility.
This commit refines the Flatpak configuration for better integration and metadata.

**Key Changes:**

*   **Permissions:** Added `xdg-download` for file system access, `ipc` for GUI compatibility, and set `GDK_BACKEND=x11,wayland` for broader display server support.
*   **Build Paths:** Corrected build artifact paths from `build/` to `composeApp/build/`.
*   **Metadata (`appdata.xml`):**
    *   Expanded the application description and features list.
    *   Added keywords, categories, and various project URLs (VCS, contribute, donation).
    *   Improved the release notes for version 1.5.1 with more detail.
    *   Renamed the installed file to `*.metainfo.xml` to follow modern standards.
*   **Manifest (`manifest.yml`):** Mirrored the permission updates from the main Flatpak file.
The `gradlew` script requires execute permissions to run. This change adds the necessary `chmod +x` command to the Flatpak build process to ensure the script can be executed, preventing potential build failures.
This introduces a new Gradle task, `resizeFlatpakIcon`, that automatically resizes the application icon to 512x512 for the Flatpak package.

The task uses ImageMagick's `convert` command to perform the resizing. It includes a check to ensure the task only runs if ImageMagick is installed and available in the system's PATH. The `copyFlatpakResources` task is updated to depend on this new resizing task.
The `resizeFlatpakIcon` custom task, which was responsible for resizing the application icon for Flatpak builds using ImageMagick, has been removed. The `copyFlatpakResources` task's dependency on it has also been removed.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 10, 2026

Walkthrough

Adds Flatpak packaging workflows and resources: Gradle automation (constants and tasks) to copy resources, package, export, and run binary- and source-based Flatpaks, BuildConfig generation hook, dependency adjustment, and Flatpak metadata files (manifests, desktop, appdata).

Changes

Cohort / File(s) Summary
Gradle Build Configuration
composeApp/build.gradle.kts
New top-level constants (appId, appName, flatpakDir, flatpakSourceDir); copy tasks for binary and source artifacts; Exec tasks to package/export/run Flatpaks; BuildConfig.kt generation ordering; moved kotlinx.coroutinesSwing from jvmMain to androidMain; conditional handling for generated sources.
Flatpak Manifests & Metadata
composeApp/src/jvmMain/resources/flatpak/manifest.yml, .../zed.rainxch.githubstore.yml, .../zed.rainxch.githubstore.appdata.xml, .../zed.rainxch.githubstore.desktop
Added Flatpak manifest(s) and app metadata including runtime/SDK, finish-args/permissions, module build steps (install binary/libs/icons/desktop/appdata), source settings/exclusions, and an appdata XML and desktop entry (release v1.5.1).

Sequence Diagram(s)

sequenceDiagram
  actor Developer
  participant Gradle as Gradle build
  participant FS as FileSystem (flatpakDir)
  participant Flatpak as Flatpak tooling
  participant Runtime as Sandboxed runtime

  Developer->>Gradle: run package/export/run task
  Gradle->>FS: copy resources, AppImage, generated sources (if present)
  Gradle->>Flatpak: invoke flatpak-builder / build-export / install / run
  Flatpak->>FS: produce repo/export artifacts
  Flatpak->>Runtime: install and launch sandboxed app
  Runtime-->>Developer: app started
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hop through manifests, scripts in paw,
copy icons, binaries, and a little more,
Gradle drums, Flatpak doors swing wide,
sandboxed carrots tucked inside,
a rabbit cheers — the app takes stride! 🎩✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Pub flatpak' is too vague and generic to clearly convey the changeset scope. Use a more descriptive title that specifies the main change, such as 'Add Flatpak packaging support with automated build tasks' or 'Implement Flatpak manifest and build configuration'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
composeApp/build.gradle.kts (1)

77-133: Move kotlinx.coroutinesSwing from commonMain to jvmMain—it breaks Android builds.

kotlinx-coroutines-swing is a JVM/Swing desktop–only module requiring javax.swing and other Java Desktop APIs unavailable on Android. Placing it in commonMain exposes it to the Android target, causing build failures.

Fix
 commonMain.dependencies {
     ...
-    implementation(libs.kotlinx.coroutinesSwing)
 }
 ...
 jvmMain {
     kotlin.srcDir(layout.buildDirectory.dir("generated/buildconfig/jvm"))
     dependencies {
         implementation(compose.desktop.currentOs)
         // Koin core
         implementation(libs.koin.core)
         // Ktor client for JVM Desktop
         implementation(libs.ktor.client.core)
         implementation(libs.ktor.client.java)
         implementation(libs.ktor.client.content.negotiation)
         implementation(libs.ktor.serialization.kotlinx.json)
         implementation(libs.kotlinx.serialization.json)
+        implementation(libs.kotlinx.coroutinesSwing)
     }
 }
🤖 Fix all issues with AI agents
In @composeApp/build.gradle.kts:
- Around line 341-411: The Flatpak manifest filename is inconsistent:
copyFlatpakSourceResources copies zed.rainxch.githubstore.yml but
packageFlatpakSource and runFlatpakSource call "$appId.yml"; update
copyFlatpakSourceResources to rename the copied manifest to "$appId.yml" (e.g.,
use from(resourcesDir) { include("zed.rainxch.githubstore.yml");
rename("zed.rainxch.githubstore.yml", "$appId.yml") } or equivalent) so
packageFlatpakSource and runFlatpakSource (which reference appId and
"$appId.yml") find the manifest, or alternatively change the commandLine
invocations in packageFlatpakSource and runFlatpakSource to use
"zed.rainxch.githubstore.yml" consistently.
- Around line 264-340: packageFlatpak fails because it doesn't depend on the
binary copy and the icon isn't being copied; update the packageFlatpak task to
dependOn(copyFlatpakBinary) in addition to copyFlatpakResources, ensure
copyFlatpakBinary places the app binaries and libs under the Flatpak build
layout (build/${appId}/bin and build/${appId}/lib) and/or add a small
copyFlatpakIcon task (or include app_icon.png in copyFlatpakResources' from(...)
includes) so app_icon.png is present in the flatpak build dir, and expose those
outputs as task inputs (e.g., inputs.dir(flatpakDir.map{...})) so Gradle's
up-to-date checks see the produced files.

In @composeApp/src/jvmMain/resources/flatpak/manifest.yml:
- Around line 1-32: The sdk field currently uses an invalid double-slash syntax
("org.freedesktop.Sdk//25.08"); edit the manifest to set sdk to
"org.freedesktop.Sdk" (remove the "//25.08") and keep the runtime-version field
as '25.08' so Flatpak recognizes the SDK and version separately; update the sdk
value in the manifest entry where sdk is defined.

In @composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml:
- Around line 1-56: Run flatpak-builder-tools/gradle/flatpak-gradle-generator.py
against your project to produce the generated-sources.json list of Maven/Gradle
artifacts, then paste that generated list into the manifest's sources: section
in place of the TODO so each dependency entry (with url, sha256, dest and
dest-filename) is vendored; keep the manifest's ./gradlew --offline build
command only after confirming all required entries from generated-sources.json
are present, or remove --offline if you cannot vendor everything.
🧹 Nitpick comments (1)
composeApp/build.gradle.kts (1)

269-411: Gradle configuration cache: use lazy evaluation for Providers in Exec tasks and avoid eager resolution in Copy tasks.

  • --repo=${repoDir.get().absolutePath} in packageFlatpak and packageFlatpakSource tasks calls Provider.get() at configuration time. Move the commandLine setup into a doFirst {} block or use map() to keep the configuration cache friendly.
  • outputs.files.singleFile in copyFlatpakBinary and copyFlatpakSourceResources eagerly resolves the destination and breaks laziness. Instead, use the known destination directly: flatpakDir.get().asFile.resolve("build") or flatpakSourceDir.get().asFile.resolve("build"), or configure into() lazily with a closure.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1741327 and 065a84b.

⛔ Files ignored due to path filters (2)
  • composeApp/logo/app_icon.png is excluded by !**/*.png
  • composeApp/src/jvmMain/resources/flatpak/app_icon.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • composeApp/build.gradle.kts
  • composeApp/src/jvmMain/resources/flatpak/manifest.yml
  • composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.appdata.xml
  • composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.desktop
  • composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml
🔇 Additional comments (2)
composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.appdata.xml (1)

1-69: Align installed filename between the two Flatpak manifests and add screenshots for AppStream metadata.

Your two manifests (manifest.yml and zed.rainxch.githubstore.yml) install the metadata file under different names:

  • manifest.yml uses zed.rainxch.githubstore.appdata.xml
  • zed.rainxch.githubstore.yml uses zed.rainxch.githubstore.metainfo.xml

Pick one convention (.metainfo.xml is the current AppStream recommendation) and update both manifests consistently. Additionally, add screenshots to the appdata file and include the corresponding image files in the flatpak directory—Flathub and AppStream consumers expect GUI apps to provide screenshots for preview purposes.

composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.desktop (1)

1-10: Verify the desktop entry WM_CLASS configuration—it may not match the actual window class at runtime.

The Exec=GitHub-Store parameter is correct and properly matches the executable name defined in both manifest.yml and build.gradle.kts. However, the StartupWMClass=GitHub-Store declaration has a potential naming mismatch:

  • The application window sets title = "Github Store" (space-separated) in main.kt, while StartupWMClass=GitHub-Store uses a hyphenated form. Compose Desktop may derive the actual WM_CLASS from the window title or application configuration, potentially causing taskbar/dock grouping issues with duplicate icons.

The %U parameter is appropriately used for URI handling. To confirm the exact WM_CLASS at runtime, launch the app and check with xprop WM_CLASS (X11) or equivalent Wayland tooling.

Comment on lines 341 to 411
// Source-based tasks for Flathub submission
val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")

from(resourcesDir) {
include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
}
into(flatpakSourceDir.map { it.asFile.resolve("build") })

doFirst {
val targetDir = outputs.files.singleFile
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
targetDir.mkdirs()
}
}

val copyGeneratedSources = tasks.register<Copy>("copyGeneratedSources") {
dependsOn(copyFlatpakSourceResources)

val sourcesFile = layout.projectDirectory.file("generated-sources.json")

from(sourcesFile)
into(flatpakSourceDir.map { it.asFile.resolve("build") })

onlyIf {
sourcesFile.asFile.exists()
}
}

tasks.register<Exec>("packageFlatpakSource") {
dependsOn(copyGeneratedSources)
group = "build"
description = "Build Flatpak for Linux desktop (source-based for submission)"

val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
val repoDir = flatpakSourceDir.map { it.asFile.resolve("repo") }

workingDir(buildDir)
commandLine(
"flatpak-builder",
"--install-deps-from=flathub",
"--repo=${repoDir.get().absolutePath}",
"--force-clean",
"build/${appId}",
"$appId.yml"
)
}

tasks.register<Exec>("exportFlatpakSource") {
dependsOn("packageFlatpakSource")
group = "build"
description = "Export Flatpak bundle (source-based)"

val versionName = appVersionName

workingDir(flatpakSourceDir)
commandLine("flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, "master")
}

tasks.register<Exec>("runFlatpakSource") {
dependsOn("packageFlatpakSource")
group = "run"
description = "Run the Flatpak locally (source-based)"

val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }

workingDir(buildDir)
commandLine("flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Source-based tasks reference the wrong manifest filename ($appId.yml doesn’t exist in that build dir).

copyFlatpakSourceResources copies zed.rainxch.githubstore.yml, but packageFlatpakSource/runFlatpakSource use $appId.yml. That should be consistent (either rename on copy, or invoke the copied filename).

Proposed fix (rename on copy to reuse $appId.yml)
 val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
   val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")

   from(resourcesDir) {
     include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
   }
   into(flatpakSourceDir.map { it.asFile.resolve("build") })
+
+  rename("zed.rainxch.githubstore.yml", "$appId.yml")

   doFirst {
     val targetDir = outputs.files.singleFile
     if (targetDir.exists()) {
       targetDir.deleteRecursively()
     }
     targetDir.mkdirs()
   }
 }
🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 341 - 411, The Flatpak manifest
filename is inconsistent: copyFlatpakSourceResources copies
zed.rainxch.githubstore.yml but packageFlatpakSource and runFlatpakSource call
"$appId.yml"; update copyFlatpakSourceResources to rename the copied manifest to
"$appId.yml" (e.g., use from(resourcesDir) {
include("zed.rainxch.githubstore.yml"); rename("zed.rainxch.githubstore.yml",
"$appId.yml") } or equivalent) so packageFlatpakSource and runFlatpakSource
(which reference appId and "$appId.yml") find the manifest, or alternatively
change the commandLine invocations in packageFlatpakSource and runFlatpakSource
to use "zed.rainxch.githubstore.yml" consistently.

Comment on lines +1 to +32
app-id: zed.rainxch.githubstore
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk//25.08
command: GitHub-Store
finish-args:
- --share=network
- --share=ipc
- --socket=x11
- --socket=wayland
- --device=dri
- --filesystem=xdg-download
- --env=GDK_BACKEND=x11,wayland
modules:
- name: githubstore
buildsystem: simple
build-commands:
- mkdir -p /app/bin /app/lib
- install -Dm755 bin/GitHub-Store /app/bin/GitHub-Store
- cp -r lib/* /app/lib/
- mkdir -p /app/share/applications
- install -D zed.rainxch.githubstore.desktop /app/share/applications/
- mkdir -p /app/share/icons/hicolor/512x512/apps/
- install -D app_icon.png /app/share/icons/hicolor/512x512/apps/zed.rainxch.githubstore.png
- mkdir -p /app/share/metainfo/
- install -D zed.rainxch.githubstore.appdata.xml /app/share/metainfo/zed.rainxch.githubstore.appdata.xml
sources:
- type: dir
path: .
skip:
- zed.rainxch.githubstore.yml
- manifest.yml No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

❓ Verification inconclusive

Flatpak manifest format sdk field syntax runtime-version


Short answer — in a Flatpak manifest (JSON or YAML) you set:

  • sdk: a string naming the development runtime (e.g. "org.freedesktop.Sdk", "org.gnome.Sdk", "org.kde.Sdk").
  • runtime-version: a string naming the runtime version to build against (e.g. "19.08", "3.26", "41", "6.8"); if omitted it defaults to "master".

Example (JSON):
{
"app-id": "org.example.App",
"runtime": "org.freedesktop.Platform",
"runtime-version": "21.08",
"sdk": "org.freedesktop.Sdk",
"command": "app"
}

These fields are top-level manifest keys used by flatpak-builder to choose the platform and SDK for build and packaging. [1][2]

Sources:
[1] Flatpak Builder command reference / manifest docs.
[2] Flatpak manifests guide (examples and explanation).


AppStream metainfo.xml vs appdata.xml naming convention


Short answer

  • Use .appdata.xml for desktop applications (type="desktop" or "desktop-application"); use .metainfo.xml for other component types (addons, fonts, codecs, firmware, etc.). The file basename should match the inside the and files go in /usr/share/metainfo (legacy /usr/share/appdata is supported only for compatibility). [1][2][3]

Details / rationale (brief)

  • AppStream's spec permits either suffix for desktop-application for legacy reasons, but conventions and packaging guidelines prefer .appdata.xml for applications and .metainfo.xml for everything else to avoid name collisions and to make intent clear. [1][2]
  • Validate files with appstream tools (appstreamcli / appstream-util) and install them in the package that provides the .desktop file for GUI apps. [1][3]

Sources
[1] AppStream (freedesktop) — Metadata / Desktop Applications.
[2] KDE TechBase — MetaInfo (explains .appdata.xml vs .metainfo.xml).
[3] Debian / Fedora packaging guidelines (installation location and package placement).


Script executed:

# Check if there are any other Flatpak manifests in the repo for reference
find . -name "*.yml" -o -name "*.yaml" | grep -i flatpak | head -20

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

./composeApp/src/jvmMain/resources/flatpak/manifest.yml
./composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml

Script executed:

# Look at the Gradle build config to understand the manifest renaming behavior
find . -name "build.gradle*" -o -name "*.gradle" | head -20

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

./build.gradle.kts
./composeApp/build.gradle.kts

Script executed:

# Check the build.gradle.kts to understand manifest naming/handling
cat -n composeApp/build.gradle.kts | head -100

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	import org.jetbrains.compose.desktop.application.dsl.TargetFormat
     2	import org.jetbrains.kotlin.gradle.dsl.JvmTarget
     3	import java.util.Properties
     4	
     5	plugins {
     6	    alias(libs.plugins.kotlinMultiplatform)
     7	    alias(libs.plugins.androidApplication)
     8	    alias(libs.plugins.composeMultiplatform)
     9	    alias(libs.plugins.composeCompiler)
    10	    alias(libs.plugins.composeHotReload)
    11	    alias(libs.plugins.kotlinSerialization)
    12	    alias(libs.plugins.kotest)
    13	    alias(libs.plugins.ksp)
    14	    alias(libs.plugins.androidx.room)
    15	}
    16	
    17	val appVersionName = "1.5.1"
    18	val appVersionCode = 10
    19	
    20	val localProps = Properties().apply {
    21	    val file = rootProject.file("local.properties")
    22	    if (file.exists()) file.inputStream().use { this.load(it) }
    23	}
    24	val localGithubClientId =
    25	    (localProps.getProperty("GITHUB_CLIENT_ID") ?: "Ov23linTY28VFpFjFiI9").trim()
    26	
    27	// Generate BuildConfig for JVM (Configuration Cache Compatible)
    28	val generateJvmBuildConfig = tasks.register("generateJvmBuildConfig") {
    29	    val outputDir = layout.buildDirectory.dir("generated/buildconfig/jvm")
    30	    val clientId = localGithubClientId
    31	    val versionName = appVersionName
    32	
    33	    outputs.dir(outputDir)
    34	
    35	    doLast {
    36	        val file = outputDir.get().asFile.resolve("zed/rainxch/githubstore/BuildConfig.kt")
    37	        file.parentFile.mkdirs()
    38	        file.writeText(
    39	            """
    40	            package zed.rainxch.githubstore
    41	            
    42	            object BuildConfig {
    43	                const val GITHUB_CLIENT_ID = "$clientId"
    44	                const val VERSION_NAME = "$versionName"
    45	            }
    46	        """.trimIndent()
    47	        )
    48	    }
    49	}
    50	
    51	kotlin {
    52	    androidTarget {
    53	        compilerOptions {
    54	            jvmTarget.set(JvmTarget.JVM_21)
    55	        }
    56	    }
    57	
    58	    jvm()
    59	
    60	    sourceSets {
    61	        androidMain.dependencies {
    62	            implementation(compose.preview)
    63	            implementation(libs.androidx.activity.compose)
    64	            // Koin DI
    65	            implementation(libs.koin.android)
    66	            implementation(libs.koin.androidx.compose)
    67	            // Ktor client for Android
    68	            implementation(libs.ktor.client.core)
    69	            implementation(libs.ktor.client.okhttp)
    70	            implementation(libs.ktor.client.content.negotiation)
    71	            implementation(libs.ktor.serialization.kotlinx.json)
    72	            implementation(libs.kotlinx.serialization.json)
    73	            // Secure storage (direct coordinate to avoid catalog accessor mismatch)
    74	            implementation(libs.androidx.security.crypto)
    75	            implementation(libs.core.splashscreen)
    76	        }
    77	        commonMain.dependencies {
    78	            implementation(compose.runtime)
    79	            implementation(compose.foundation)
    80	            implementation(libs.material3)
    81	            implementation(compose.materialIconsExtended)
    82	            implementation(compose.ui)
    83	            implementation(compose.components.resources)
    84	            implementation(compose.components.uiToolingPreview)
    85	            implementation(libs.androidx.lifecycle.viewmodelCompose)
    86	            implementation(libs.androidx.lifecycle.runtimeCompose)
    87	
    88	            // Koin DI
    89	            implementation(libs.koin.core)
    90	            implementation(libs.koin.compose)
    91	            implementation(libs.koin.compose.viewmodel)
    92	
    93	            // HTTP and serialization
    94	            implementation(libs.ktor.client.core)
    95	            implementation(libs.ktor.client.content.negotiation)
    96	            implementation(libs.ktor.serialization.kotlinx.json)
    97	            implementation(libs.kotlinx.serialization.json)
    98	            implementation(libs.kotlinx.coroutines.core)
    99	
   100	            // Logging

Script executed:

# Also check the root build.gradle.kts
cat -n build.gradle.kts | head -100

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	plugins {
     2	    alias(libs.plugins.androidApplication) apply false
     3	    alias(libs.plugins.androidLibrary) apply false
     4	    alias(libs.plugins.composeHotReload) apply false
     5	    alias(libs.plugins.composeMultiplatform) apply false
     6	    alias(libs.plugins.composeCompiler) apply false
     7	    alias(libs.plugins.kotlinMultiplatform) apply false
     8	}

Script executed:

# Let's look at the flatpak directory structure
find composeApp/src/jvmMain/resources/flatpak -type f

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

composeApp/src/jvmMain/resources/flatpak/app_icon.png
composeApp/src/jvmMain/resources/flatpak/manifest.yml
composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.appdata.xml
composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.desktop
composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml

Script executed:

# Check if there's any documentation or comments about the manifest renaming
grep -r "manifest\|appId" composeApp/src/jvmMain/resources/flatpak/ 2>/dev/null

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

composeApp/src/jvmMain/resources/flatpak/manifest.yml:          - manifest.yml

Script executed:

# Check the appdata.xml file to see what component type is declared
cat composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.appdata.xml

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
    <id>zed.rainxch.githubstore</id>
    <metadata_license>CC0-1.0</metadata_license>
    <project_license>Apache-2.0</project_license>
    <name>GitHub Store</name>
    <summary>A cross-platform app store for GitHub releases</summary>
    <description>
        <p>GitHub Store is a cross-platform "play store" for GitHub releases. It discovers repositories that ship real installable binaries and lets you install, track, and update them across platforms from one place.</p>
        <p>Key Features:</p>
        <ul>
            <li>Smart discovery with Trending, Recently Updated, and New sections</li>
            <li>Only shows repositories with installable assets (APK, EXE, DMG, AppImage, DEB, RPM, etc.)</li>
            <li>Platform-aware filtering - automatically detects and shows installers for your OS</li>
            <li>Always installs from the latest published release with changelog preview</li>
            <li>Rich app details with README rendering, statistics, and developer info</li>
            <li>GitHub OAuth login for higher API rate limits</li>
            <li>Material 3 design with dynamic color support and dark mode</li>
            <li>Cross-platform support for Android and Desktop (Windows, macOS, Linux)</li>
        </ul>
        <p>Built with Kotlin Multiplatform and Compose Multiplatform for a consistent experience across all platforms.</p>
    </description>
    <developer id="zed.rainxch">
        <name>rainxchzed</name>
    </developer>
    <url type="homepage">https://github.com/rainxchzed/Github-Store</url>
    <url type="bugtracker">https://github.com/rainxchzed/Github-Store/issues</url>
    <url type="vcs-browser">https://github.com/rainxchzed/Github-Store</url>
    <url type="contribute">https://github.com/rainxchzed/Github-Store/blob/main/CONTRIBUTING.md</url>
    <url type="donation">https://github.com/sponsors/rainxchzed</url>
    <launchable type="desktop-id">zed.rainxch.githubstore.desktop</launchable>
    <provides>
        <binary>GitHub-Store</binary>
    </provides>
    <categories>
        <category>Development</category>
        <category>Utility</category>
    </categories>
    <keywords>
        <keyword>github</keyword>
        <keyword>releases</keyword>
        <keyword>app store</keyword>
        <keyword>package manager</keyword>
        <keyword>installer</keyword>
    </keywords>
    <content_rating type="oars-1.1">
        <content_attribute id="social-info">moderate</content_attribute>
    </content_rating>
    <releases>
        <release version="1.5.1" date="2026-01-10">
            <description>
                <p>A quality-of-life update focused on smoother navigation, smarter syncing, and richer localization.</p>
                <p>Highlights:</p>
                <ul>
                    <li>New liquid bottom navigation bar for a more fluid and modern experience</li>
                    <li>Favorites and feedback features on the details screen</li>
                    <li>Smarter data migration and improved system sync for better state alignment</li>
                    <li>New language support: Korean, Bengali, and Italian</li>
                    <li>Improved translations and localized theme options</li>
                    <li>Home screen refactored for better maintainability</li>
                    <li>Enhanced search experience with polished input behavior</li>
                    <li>Authentication improvements for more reliable sign-in</li>
                    <li>Fixed Android update issues</li>
                </ul>
            </description>
            <url>https://github.com/rainxchzed/Github-Store/releases/tag/1.5.1</url>
        </release>
    </releases>
</component>

Script executed:

# Check the desktop file as well for context
cat composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.desktop

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

[Desktop Entry]
Version=1.0
Type=Application
Categories=Development;
Terminal=false
Exec=GitHub-Store %U
Name=GitHub Store
Comment=Discover and install apps from GitHub releases
Icon=zed.rainxch.githubstore
StartupWMClass=GitHub-Store

Script executed:

# Check the source-based manifest to understand the two-manifest approach
cat composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

app-id: zed.rainxch.githubstore
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
sdk-extensions:
  - org.freedesktop.Sdk.Extension.openjdk21
command: GitHub-Store
finish-args:
  # Network access for GitHub API
  - --share=network
  # Required for X11/Wayland GUI applications
  - --share=ipc
  # Display server access - supports both X11 and Wayland
  - --socket=x11
  - --socket=wayland
  # GPU acceleration
  - --device=dri
  # File system access for downloads
  - --filesystem=xdg-download
  # Ensures compatibility across different display configurations
  - --env=GDK_BACKEND=x11,wayland
modules:
  - name: githubstore
    buildsystem: simple
    build-options:
      append-path: /usr/lib/sdk/openjdk21/bin
      env:
        JAVA_HOME: /usr/lib/sdk/openjdk21
    sources:
      - type: git
        url: https://github.com/rainxchzed/Github-Store.git
        tag: 1.5.1
      # TODO: Paste the contents of generated-sources.json here as a YAML list
      # Generate it using: flatpak-builder-tools/gradle/flatpak-gradle-generator.py
      # Example format:
      # - type: file
      #   url: https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar
      #   sha256: abc123...
      #   dest: gradle-cache/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/2.1.0/
      #   dest-filename: kotlin-stdlib-2.1.0.jar
      - type: file
        path: zed.rainxch.githubstore.desktop
      - type: file
        path: app_icon.png
      - type: file
        path: zed.rainxch.githubstore.appdata.xml
    build-commands:
      - chmod +x gradlew
      - export GRADLE_USER_HOME=$PWD/.gradle
      - ./gradlew --offline --no-daemon packageAppImage
      - mkdir -p /app/bin /app/lib
      - cp -r composeApp/build/compose/binaries/main/app/GitHub-Store/bin/* /app/bin/
      - cp -r composeApp/build/compose/binaries/main/app/GitHub-Store/lib/* /app/lib/
      - install -D zed.rainxch.githubstore.desktop /app/share/applications/zed.rainxch.githubstore.desktop
      - install -D app_icon.png /app/share/icons/hicolor/512x512/apps/zed.rainxch.githubstore.png
      - install -Dm644 zed.rainxch.githubstore.appdata.xml /app/share/metainfo/zed.rainxch.githubstore.metainfo.xml

Script executed:

# Check if there's any Gradle task or configuration that renames manifest files
grep -r "manifest" composeApp/build.gradle.kts | head -20

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

        include("manifest.yml", "*.desktop", "*.xml")
    rename("manifest.yml", "$appId.yml")

Script executed:

# Search the entire codebase for any manifest renaming logic
rg "manifest\.(yml|yaml)" --type gradle --type kotlin -A 3 -B 3

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

composeApp/build.gradle.kts-    val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
composeApp/build.gradle.kts-
composeApp/build.gradle.kts-    from(resourcesDir) {
composeApp/build.gradle.kts:        include("manifest.yml", "*.desktop", "*.xml")
composeApp/build.gradle.kts-    }
composeApp/build.gradle.kts-    into(flatpakDir.map { it.asFile.resolve("build") })
composeApp/build.gradle.kts-
composeApp/build.gradle.kts:    rename("manifest.yml", "$appId.yml")
composeApp/build.gradle.kts-}
composeApp/build.gradle.kts-
composeApp/build.gradle.kts-tasks.register<Exec>("packageFlatpak") {

Remove the double-slash from the sdk field: org.freedesktop.Sdk//25.08org.freedesktop.Sdk.

Flatpak manifests use separate fields for sdk (the SDK name) and runtime-version (the version). The syntax org.freedesktop.Sdk//25.08 is invalid and will cause flatpak-builder to reject the manifest.

🤖 Prompt for AI Agents
In @composeApp/src/jvmMain/resources/flatpak/manifest.yml around lines 1 - 32,
The sdk field currently uses an invalid double-slash syntax
("org.freedesktop.Sdk//25.08"); edit the manifest to set sdk to
"org.freedesktop.Sdk" (remove the "//25.08") and keep the runtime-version field
as '25.08' so Flatpak recognizes the SDK and version separately; update the sdk
value in the manifest entry where sdk is defined.

Comment on lines +1 to +56
app-id: zed.rainxch.githubstore
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk21
command: GitHub-Store
finish-args:
# Network access for GitHub API
- --share=network
# Required for X11/Wayland GUI applications
- --share=ipc
# Display server access - supports both X11 and Wayland
- --socket=x11
- --socket=wayland
# GPU acceleration
- --device=dri
# File system access for downloads
- --filesystem=xdg-download
# Ensures compatibility across different display configurations
- --env=GDK_BACKEND=x11,wayland
modules:
- name: githubstore
buildsystem: simple
build-options:
append-path: /usr/lib/sdk/openjdk21/bin
env:
JAVA_HOME: /usr/lib/sdk/openjdk21
sources:
- type: git
url: https://github.com/rainxchzed/Github-Store.git
tag: 1.5.1
# TODO: Paste the contents of generated-sources.json here as a YAML list
# Generate it using: flatpak-builder-tools/gradle/flatpak-gradle-generator.py
# Example format:
# - type: file
# url: https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.1.0/kotlin-stdlib-2.1.0.jar
# sha256: abc123...
# dest: gradle-cache/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/2.1.0/
# dest-filename: kotlin-stdlib-2.1.0.jar
- type: file
path: zed.rainxch.githubstore.desktop
- type: file
path: app_icon.png
- type: file
path: zed.rainxch.githubstore.appdata.xml
build-commands:
- chmod +x gradlew
- export GRADLE_USER_HOME=$PWD/.gradle
- ./gradlew --offline --no-daemon packageAppImage
- mkdir -p /app/bin /app/lib
- cp -r composeApp/build/compose/binaries/main/app/GitHub-Store/bin/* /app/bin/
- cp -r composeApp/build/compose/binaries/main/app/GitHub-Store/lib/* /app/lib/
- install -D zed.rainxch.githubstore.desktop /app/share/applications/zed.rainxch.githubstore.desktop
- install -D app_icon.png /app/share/icons/hicolor/512x512/apps/zed.rainxch.githubstore.png
- install -Dm644 zed.rainxch.githubstore.appdata.xml /app/share/metainfo/zed.rainxch.githubstore.metainfo.xml No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

org.freedesktop.Platform runtime version 25.08

💡 Result:

org.freedesktop.Platform 25.08 is the Freedesktop (Flatpak) runtime from the Freedesktop SDK 25.08 release (major release for 2025). It was published as part of Freedesktop SDK 25.08 (release announced Sept 3, 2025) and introduces several breaking/behavior changes (e.g., openh264/ffmpeg-full replaced by codecs-extra, removal/replacements of some drivers and packages). [1][2][3]

Quick actions:

  • Install runtime (example): flatpak install org.freedesktop.Platform//25.08. [1]
  • See full SDK NEWS/release notes for detailed changes and migration guidance. [1][3]

Sources:
[1] Flathub Discourse — “Freedesktop SDK 25.08.0 released!” (announcement).
[2] Flatpak docs — “Available Runtimes” (freedesktop runtime description).
[3] Freedesktop SDK — Release notes / NEWS for 25.08.


🌐 Web query:

Flathub org.freedesktop.Platform 25.08 support

💡 Result:

Short answer: Yes — the Freedesktop org.freedesktop.Platform 25.08 runtime is available on Flathub and is a supported/current Freedesktop runtime. Install with flatpak (example):

  • flatpak install flathub org.freedesktop.Platform//25.08
  • flatpak install flathub org.freedesktop.Sdk//25.08

Sources: Flathub runtime pages and release notes, and Flatpak runtime documentation. [1][2][3]

References

  • Flathub org.freedesktop.Platform / Sdk pages (release notes). [1]
  • Freedesktop SDK app page on Flathub. [2]
  • Flatpak “Available Runtimes” (release schedule / support policy). [3]

🌐 Web query:

Flatpak gradle offline build vendored dependencies

💡 Result:

Short answer: you must vendor all Maven/Gradle artifacts into your Flatpak manifest (or into a local repo included as a source) and make Gradle use only that local repo / its cache during the build. Flatpak builds disallow network access, so fetching from Maven Central at build time is not permitted — either generate Flatpak "sources" for each dependency or include a pre-built local Maven/Gradle cache and point your build to it. [1][2][3][4]

Recommended approaches (concise):

  • Use the flatpak-gradle-generator plugin to generate a Flatpak sources file (it harvests your Gradle dependencies and produces sources entries you can include in the manifest). Add the generated sources to the manifest so flatpak-builder downloads/copies them before build. [1]

  • Vendor a local Maven repository inside the manifest:

    • Produce a directory containing all required artifacts/POMs (a local Maven repo or a flatDir).
    • Add that directory as a module/source in the manifest (type=file/archive or path), and in build.gradle declare a repository using file:// or layout path to that directory (or maven { url uri("$projectDir/...") }). This makes Gradle resolve from the vendored repo offline. See Flatpak module-sources and Gradle local repo docs. [2][4]
  • Alternatively, pre-populate Gradle’s user cache (.gradle) as a source:

    • Build once externally to populate ~/.gradle/caches, tar that cache, add it as a source in the manifest, extract it to /run/build/.gradle (or $HOME/.gradle) in the build commands, and run Gradle with --offline. Note: plugins resolved via the plugins {} block may need explicit buildscript/classpath declarations so they resolve from the cache. Use Gradle’s --offline flag. [2][4]

Practical notes / gotchas:

  • Flatpak disallows network downloads during build; everything must be in the manifest/sources. [2][3]
  • The plugins { } DSL can still try to reach plugins.gradle.org; convert those plugin declarations to explicit buildscript classpath dependencies or vendor the plugin JARs so they resolve offline. [4]
  • Test by running gradle --offline in a clean build environment identical to the Flatpak build sandbox to ensure no remote access is required. [4]

Useful references:

  • flatpak-gradle-generator plugin (generates Flatpak sources from Gradle). [1]
  • Flatpak module sources / how sources are staged for builds. [2]
  • Flathub/Flatpak guidance: builds must be offline; either vendor deps or generate sources. [3]
  • Gradle docs on offline mode, caching, and local Maven repositories. [4]

If you want, tell me which build setup you have (single-module Kotlin/Java, Android, or multimodule) and I’ll give an exact manifest + Gradle repo snippet and a short workflow to produce the vendored sources.


Build will fail on Flathub: --offline requires the generated-sources.json TODO to be completed and embedded as vendored sources.

The manifest runs ./gradlew --offline ... but the generated-sources.json content (Maven URLs + sha256 hashes) has not yet been generated and added to sources:. Flatpak disallows network access during builds, so without vendored Maven/Gradle artifacts listed in the manifest, the offline flag will cause the build to fail in the Flathub sandbox.

Complete the TODO using flatpak-builder-tools/gradle/flatpak-gradle-generator.py to extract all Gradle dependencies, then paste the generated sources list into the manifest before submission.

🤖 Prompt for AI Agents
In @composeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.yml around
lines 1 - 56, Run flatpak-builder-tools/gradle/flatpak-gradle-generator.py
against your project to produce the generated-sources.json list of Maven/Gradle
artifacts, then paste that generated list into the manifest's sources: section
in place of the TODO so each dependency entry (with url, sha256, dest and
dest-filename) is vendored; keep the manifest's ./gradlew --offline build
command only after confirming all required entries from generated-sources.json
are present, or remove --offline if you cannot vendor everything.

Adds `app_icon.png` to the list of files copied for the Flatpak build.

Also establishes a dependency for the `copyFlatpakResources` task on `copyFlatpakBinary` to ensure the binary is always copied before the resources.
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: 7

🤖 Fix all issues with AI agents
In @composeApp/build.gradle.kts:
- Around line 270-285: The doFirst cleanup in the copyFlatpakBinary task is
using outputs.files.singleFile which may not reference the intended target;
update the cleanup to resolve and operate on the explicit flatpakDir target
instead (use flatpakDir.map { it.asFile.resolve("build") } or equivalent to get
the File) and call deleteRecursively()/mkdirs() on that File; keep
appImageOutput and the Copy inputs unchanged and ensure you only clear the
resolved flatpak build directory before copying.
- Around line 131-132: The kotlinx.coroutinesSwing dependency is incorrectly
added to commonMain (implementation(libs.kotlinx.coroutinesSwing)), which is
JVM-only and will break Android/other targets; move this dependency out of
commonMain.dependencies and add it to jvmMain.dependencies instead (alongside
the other JVM-specific deps in the jvmMain block) so only the JVM target pulls
in kotlinx.coroutinesSwing.
- Around line 300-318: The packageFlatpak Exec task is creating a nested path by
setting workingDir to buildDir and still passing "build/${appId}" to
flatpak-builder; update the commandLine argument to reference the appId
directory relative to the workingDir (replace "build/${appId}" with just
"${appId}") so the builder runs in buildDir and looks for the appId manifest
there; keep the task name packageFlatpak and the variables buildDir, repoDir,
appId and the dependsOn copyFlatpakResources unchanged.
- Around line 403-412: In the runFlatpakSource Exec task, fix the nested build
directory path and avoid the hardcoded executable name: compute buildDir from
flatpakSourceDir (used by workingDir) and use that resolved buildDir in the
commandLine arguments so you don’t prepend an extra "build/" (adjust the second
argument that currently reads "build/${appId}" to reference the same
buildDir/manifest location), and replace the hardcoded "GitHub-Store" with the
actual command id defined in your Flatpak manifest (derive or inject the correct
command name rather than hardcoding it); update the task registered as
"runFlatpakSource", the flatpakSourceDir/buildDir usage, and the commandLine
call accordingly.
- Around line 373-390: The packageFlatpakSource task is invoking flatpak-builder
with a nested "build/${appId}" path even though workingDir is already set to
buildDir; update the commandLine invocation in the packageFlatpakSource task to
use the simple appId directory (i.e., "${appId}") instead of "build/${appId}" so
the build is run relative to workingDir, keeping repoDir and other flags
unchanged and leaving the --disable-rofiles-fuse flag as-is (it is intentionally
absent here).
- Around line 331-340: The runFlatpak task sets workingDir to the
already-resolved buildDir (flatpakDir/build), but the commandLine currently uses
"build/${appId}" which produces a double-nested path; update the commandLine
invocation in the tasks.register<Exec>("runFlatpak") block to use just the appId
variable (i.e., replace "build/${appId}" with appId) so the path resolves to
flatpakDir/build/<appId> and keep the rest (appId.yml and "GitHub-Store")
unchanged.
🧹 Nitpick comments (2)
composeApp/build.gradle.kts (2)

320-329: Consider parameterizing the Flatpak branch name.

The branch is hardcoded as "master". For Flathub submissions or versioned releases, you may want to use "stable" or a version-specific branch instead.

♻️ Optional improvement
+val flatpakBranch = "stable"  // or "master" for development builds
+
 tasks.register<Exec>("exportFlatpak") {
     dependsOn("packageFlatpak")
     group = "build"
     description = "Export Flatpak bundle (binary-based)"
 
     val versionName = appVersionName
 
     workingDir(flatpakDir)
-    commandLine("flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, "master")
+    commandLine("flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, flatpakBranch)
 }

392-401: Consider parameterizing the Flatpak branch name.

Same as with exportFlatpak: the branch is hardcoded as "master". For consistency and flexibility, consider using a shared branch parameter.

♻️ Optional improvement

Define the branch once at the top level (as suggested in the previous comment) and reuse it:

 tasks.register<Exec>("exportFlatpakSource") {
     dependsOn("packageFlatpakSource")
     group = "build"
     description = "Export Flatpak bundle (source-based)"
 
     val versionName = appVersionName
 
     workingDir(flatpakSourceDir)
-    commandLine("flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, "master")
+    commandLine("flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, flatpakBranch)
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 065a84b and 1a972b6.

📒 Files selected for processing (1)
  • composeApp/build.gradle.kts
🔇 Additional comments (2)
composeApp/build.gradle.kts (2)

360-371: LGTM!

The conditional copying using onlyIf is a good pattern for handling optional generated sources. The task correctly checks for file existence before attempting the copy.


287-298: No changes needed. The icon file naming correctly follows Flatpak conventions. The source file app_icon.png is referenced in the manifest, which explicitly installs it with the proper app ID name (zed.rainxch.githubstore.png) via the install command. The copyFlatpakResources task appropriately copies the generic source name, and the Flatpak manifest build process handles the final naming convention.

Comment on lines +131 to +132

implementation(libs.kotlinx.coroutinesSwing)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: JVM-specific dependency in commonMain will break Android builds.

kotlinx.coroutinesSwing requires the Swing library, which is JVM-specific and not available on Android or other platforms. Adding this to commonMain.dependencies will cause compilation failures for non-JVM targets.

🐛 Proposed fix

Move this dependency back to jvmMain.dependencies:

         // Room
         implementation(libs.androidx.room.runtime)
         implementation(libs.sqlite.bundled)
-
-        implementation(libs.kotlinx.coroutinesSwing)
     }

And add it to jvmMain.dependencies:

jvmMain {
    kotlin.srcDir(layout.buildDirectory.dir("generated/buildconfig/jvm"))

    dependencies {
        implementation(compose.desktop.currentOs)
        // Koin core
        implementation(libs.koin.core)
        // Ktor client for JVM Desktop
        implementation(libs.ktor.client.core)
        implementation(libs.ktor.client.java)
        implementation(libs.ktor.client.content.negotiation)
        implementation(libs.ktor.serialization.kotlinx.json)
        implementation(libs.kotlinx.serialization.json)
        implementation(libs.kotlinx.coroutinesSwing)
    }
}
🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 131 - 132, The
kotlinx.coroutinesSwing dependency is incorrectly added to commonMain
(implementation(libs.kotlinx.coroutinesSwing)), which is JVM-only and will break
Android/other targets; move this dependency out of commonMain.dependencies and
add it to jvmMain.dependencies instead (alongside the other JVM-specific deps in
the jvmMain block) so only the JVM target pulls in kotlinx.coroutinesSwing.

Comment on lines +343 to +358
val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")

from(resourcesDir) {
include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
}
into(flatpakSourceDir.map { it.asFile.resolve("build") })

doFirst {
val targetDir = outputs.files.singleFile
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
targetDir.mkdirs()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix directory cleanup to target the correct path.

The same issue as in copyFlatpakBinary: the doFirst block uses outputs.files.singleFile which may not reliably reference the intended target directory.

🔧 Proposed fix
 val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
     val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
 
     from(resourcesDir) {
         include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
     }
     into(flatpakSourceDir.map { it.asFile.resolve("build") })
 
     doFirst {
-        val targetDir = outputs.files.singleFile
+        val targetDir = flatpakSourceDir.get().asFile.resolve("build")
         if (targetDir.exists()) {
             targetDir.deleteRecursively()
         }
         targetDir.mkdirs()
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
from(resourcesDir) {
include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
}
into(flatpakSourceDir.map { it.asFile.resolve("build") })
doFirst {
val targetDir = outputs.files.singleFile
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
targetDir.mkdirs()
}
}
val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
from(resourcesDir) {
include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
}
into(flatpakSourceDir.map { it.asFile.resolve("build") })
doFirst {
val targetDir = flatpakSourceDir.get().asFile.resolve("build")
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
targetDir.mkdirs()
}
}

Replaced `flatpak` and `flatpak-builder` with their absolute paths (`/usr/bin/flatpak` and `/usr/bin/flatpak-builder`) in Gradle tasks. This ensures the commands are found correctly, especially in environments where they are not in the default system PATH.
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: 8

🤖 Fix all issues with AI agents
In @composeApp/build.gradle.kts:
- Around line 331-340: The runFlatpak Exec task currently passes a hard-coded
"GitHub-Store" as the Flatpak command which can mismatch the manifest; update
the commandLine call in the tasks.register<Exec>("runFlatpak") block to use the
project property/variable appName instead of the literal "GitHub-Store" (so
commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}",
"${appId}.yml", appName)); ensure appName is in scope or access it the same way
other variables (like appId/flatpakDir) are referenced.
- Around line 392-401: The exportFlatpakSource task
(tasks.register<Exec>("exportFlatpakSource")) hardcodes the flatpak binary path
and the "master" branch; make the tool path and branch configurable by: read an
env var or Gradle property (e.g., System.getenv("FLATPAK_CMD") or
project.findProperty("flatpakCmd")) and fall back to resolving the executable
via runtime lookup (e.g., "which flatpak" or default "flatpak"), and similarly
read a branch property/env var (e.g., FLATPAK_BRANCH or project property
"flatpakBranch") defaulting to "master" or "main" as appropriate; then update
the commandLine invocation in exportFlatpakSource (and mirror the same change in
exportFlatpak) to use the resolved flatpak command and the configurable branch
variable.
- Around line 403-412: The tasks.register<Exec>("runFlatpakSource") (and the
analogous runFlatpak task) currently hardcode the flatpak-builder binary path
and the hardcoded "GitHub-Store" app name; change the commandLine to call
"flatpak-builder" (so it is resolved via PATH) and replace the literal
"GitHub-Store" with the existing appName variable so the command becomes
portable and consistent with the rest of the build.
- Around line 131-133: The dependency
implementation(libs.kotlinx.coroutinesSwing) is currently declared under
commonMain (commonMain.dependencies) but kotlinx-coroutines-swing is JVM-only;
move that dependency entry from the commonMain.dependencies block into the
jvmMain.dependencies block so it is only added for the JVM target (look for the
implementation(libs.kotlinx.coroutinesSwing) line and the commonMain / jvmMain
dependency blocks and relocate the line accordingly).
- Around line 300-318: The packageFlatpak task currently hard-codes
"/usr/bin/flatpak-builder" and calls repoDir.get() at configuration time,
breaking portability and the Gradle configuration cache; modify the
tasks.register<Exec>("packageFlatpak") so command resolution and provider
dereferencing happen in an action (e.g., doFirst { ... }) — call the executable
as "flatpak-builder" (relying on PATH) and use repoDir.get().absolutePath and
buildDir.get() only inside doFirst (or use repoDir.get().asFile.absolutePath and
buildDir.get().asFile) to avoid Provider.get() during configuration and ensure
the commandLine uses "flatpak-builder", "--repo=${repoDirPath}",
"--force-clean", "--disable-rofiles-fuse", "build/${appId}", "$appId.yml".
🧹 Nitpick comments (3)
composeApp/build.gradle.kts (3)

264-267: Deduplicate IDs/names to avoid drift across Android/Desktop/Flatpak

You now have val appId / val appName, but android.defaultConfig.applicationId and compose.desktop.nativeDistributions.packageName still use literals. Consider moving these constants earlier and reusing them to keep packaging identifiers aligned.


343-358: copyFlatpakSourceResources: consider renaming the manifest to $appId.yml for symmetry

Right now it copies zed.rainxch.githubstore.yml and later references $appId.yml (which happens to match today). Adding an explicit rename(...) here makes future appId changes safer.


360-371: copyGeneratedSources: optional file copy is fine; consider failing fast if it’s required

If generated-sources.json is required for the source-based manifest, consider making packageFlatpakSource fail with a clear message when it’s missing, rather than silently proceeding.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a972b6 and 6b06857.

📒 Files selected for processing (1)
  • composeApp/build.gradle.kts
🔇 Additional comments (1)
composeApp/build.gradle.kts (1)

287-298: copyFlatpakResources: good structure; consider using constants consistently

Looks good overall; minor nit: you hardcode includes and later hardcode the run command name—consider using appName everywhere you need the command string so renames don’t silently break.

Comment on lines +131 to 133

implementation(libs.kotlinx.coroutinesSwing)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the build.gradle.kts file around the flagged lines
find . -name "build.gradle.kts" -path "*/composeApp/*" | head -5

Repository: rainxchzed/Github-Store

Length of output: 96


🏁 Script executed:

# Read the file to see the context around lines 131-133 and the source sets
cat -n composeApp/build.gradle.kts | sed -n '120,145p'

Repository: rainxchzed/Github-Store

Length of output: 1071


🏁 Script executed:

# Get the full picture of source sets and all dependencies sections
cat -n composeApp/build.gradle.kts | grep -A 5 -B 2 "commonMain\|jvmMain\|dependencies"

Repository: rainxchzed/Github-Store

Length of output: 2883


🏁 Script executed:

# Check the full commonMain.dependencies block to see all dependencies
cat -n composeApp/build.gradle.kts | sed -n '77,133p'

Repository: rainxchzed/Github-Store

Length of output: 2670


🏁 Script executed:

# Check what targets are configured in the kotlin block
cat -n composeApp/build.gradle.kts | grep -A 20 "kotlin {" | head -30

Repository: rainxchzed/Github-Store

Length of output: 890


🏁 Script executed:

# Look for the libs.kotlinx.coroutinesSwing definition in gradle catalog
find . -name "*.toml" -o -name "libs.gradle" | xargs grep -l "coroutinesSwing" 2>/dev/null || echo "Checking for version catalog..."
find . -name "libs.versions.toml" | head -1 | xargs cat 2>/dev/null | grep -A 2 -B 2 "coroutinesSwing"

Repository: rainxchzed/Github-Store

Length of output: 638


🌐 Web query:

kotlinx-coroutines-swing library JVM Swing-only compatibility

💡 Result:

Short answer: kotlinx-coroutines-swing is a JVM-only module that provides a Swing (EDT) MainCoroutineDispatcher (Dispatchers.Swing / Main on JVM). If you need Dispatchers.Main for Swing on the JVM (including desktop Kotlin/JVM or the JVM target of a multiplatform project) you must add the org.jetbrains.kotlinx:kotlinx-coroutines-swing artifact to your runtime dependencies. [1][2]

Sources:

  • kotlinx-coroutines-swing package (documents Dispatchers.Swing / jvm). [1]
  • Dispatchers.Main docs (notes that on JVM Main maps to Android/JavaFX/Swing and that you must add the corresponding artifact at runtime). [2]

Move kotlinx.coroutinesSwing from commonMain to jvmMain

kotlinx-coroutines-swing is JVM-only (provides Dispatchers.Swing for desktop/Swing applications). Placing it in commonMain.dependencies makes it available to all targets including androidTarget, where it has no meaning and cannot be used. Move it to jvmMain.dependencies.

Fix
         commonMain.dependencies {
             // ... other dependencies
-            implementation(libs.kotlinx.coroutinesSwing)
         }
         jvmMain {
             kotlin.srcDir(layout.buildDirectory.dir("generated/buildconfig/jvm"))
 
             dependencies {
                 implementation(compose.desktop.currentOs)
+                implementation(libs.kotlinx.coroutinesSwing)
                 // Koin core
🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 131 - 133, The dependency
implementation(libs.kotlinx.coroutinesSwing) is currently declared under
commonMain (commonMain.dependencies) but kotlinx-coroutines-swing is JVM-only;
move that dependency entry from the commonMain.dependencies block into the
jvmMain.dependencies block so it is only added for the JVM target (look for the
implementation(libs.kotlinx.coroutinesSwing) line and the commonMain / jvmMain
dependency blocks and relocate the line accordingly).

Comment on lines +270 to +285
val copyFlatpakBinary = tasks.register<Copy>("copyFlatpakBinary") {
dependsOn("packageAppImage")

val appImageOutput = layout.buildDirectory.dir("compose/binaries/main/app/$appName")

from(appImageOutput)
into(flatpakDir.map { it.asFile.resolve("build") })

doFirst {
val targetDir = outputs.files.singleFile
if (targetDir.exists()) {
targetDir.deleteRecursively()
}
targetDir.mkdirs()
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd "build.gradle.kts" | head -5

Repository: rainxchzed/Github-Store

Length of output: 111


🏁 Script executed:

git ls-files | grep -E "composeApp.*build\.gradle\.kts"

Repository: rainxchzed/Github-Store

Length of output: 94


🏁 Script executed:

head -300 composeApp/build.gradle.kts | tail -50

Repository: rainxchzed/Github-Store

Length of output: 1594


🏁 Script executed:

rg "packageAppImage|register.*AppImage" composeApp/build.gradle.kts -B 2 -A 5

Repository: rainxchzed/Github-Store

Length of output: 407


🏁 Script executed:

rg "OperatingSystem|isLinux|isWindows|isMac" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 49


🏁 Script executed:

rg "outputs\.files\.singleFile" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 164


🏁 Script executed:

head -50 composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 1652


🏁 Script executed:

rg "import|onlyIf" composeApp/build.gradle.kts | head -20

Repository: rainxchzed/Github-Store

Length of output: 222


🏁 Script executed:

rg "packageAppImage" . -A 3 -B 1 --type kotlin

Repository: rainxchzed/Github-Store

Length of output: 408


🏁 Script executed:

rg "onlyIf.*Linux|onlyIf.*Windows|onlyIf.*platform" composeApp/build.gradle.kts -B 2 -A 2

Repository: rainxchzed/Github-Store

Length of output: 49


🏁 Script executed:

grep -n "onlyIf" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 83


🏁 Script executed:

rg "System.getProperty|os\.name" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 49


🏁 Script executed:

cat -n composeApp/build.gradle.kts | sed -n '200,350p'

Repository: rainxchzed/Github-Store

Length of output: 5541


🏁 Script executed:

cat -n composeApp/build.gradle.kts | sed -n '360,380p'

Repository: rainxchzed/Github-Store

Length of output: 878


🏁 Script executed:

rg "System\.getenv|System\.getProperty" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 49


🌐 Web query:

Gradle Copy task outputs.files.singleFile fragile alternative

💡 Result:

Short answer: don’t rely on outputs.files.singleFile for a Copy task. declare a concrete output (OutputFile or outputs.file with a Provider) or make the Copy produce a known filename and register that exact file as the task’s output.

Why: Copy’s outputs.files is an approximation (and can be a directory or multiple files), so singleFile is brittle. Gradle docs recommend explicitly declaring outputs or using doNotTrackState for weird dirs. [1][2]

Two concise patterns:

  1. If you can pick a stable output path (recommended)
  • Make the Copy write a single known file (rename or into a unique dir) and declare that file as the task output.
    Groovy example:
    task myCopy(type: Copy) {
    from 'src/x/foo.zip'
    into "$buildDir/out"
    rename { 'app.zip' }
    }
    tasks.myCopy.outputs.file("$buildDir/out/app.zip")

Kotlin DSL:
val myCopy = tasks.register("myCopy") {
from(layout.projectDirectory.file("src/x/foo.zip"))
into(layout.buildDirectory.dir("out"))
rename { "app.zip" }
}
myCopy.configure { outputs.file(layout.buildDirectory.file("out/app.zip")) }

  1. If you can’t know the filename at configuration time, expose a Provider/explicit output property from a custom task (annotate with @OutputFile or return Provider) or compute and register the exact output file via a Provider so Gradle knows the single output without calling .singleFile.
  • Implement a small custom task class with an @OutputFile property (or register outputs.file(someProvider)) and set that provider from the copy spec.

References:

  • Gradle “Working with files” / copy behavior and outputs explanation. [1]
  • Copy task DSL and outputs/file APIs. [2]

If you want, tell me which Gradle version and show your current Copy task and I’ll give the exact code to make it robust.


🌐 Web query:

Gradle org.gradle.internal.os.OperatingSystem internal API alternative

💡 Result:

Use Gradle's public OperatingSystem types (not the internal org.gradle.internal.*). Options:

  • org.gradle.platform.OperatingSystem (public enum, since Gradle 7.6). [1]
  • org.gradle.nativeplatform.platform.OperatingSystem (public interface used by native-platform APIs). [2]
  • Or, if you just need a simple runtime check, use Java System properties (System.getProperty("os.name")) and parse it yourself. [3]

Do not depend on org.gradle.internal.* — the Gradle public-API docs mark internal packages unsupported. [4]

Sources:
[1] Gradle Javadoc — org.gradle.platform.OperatingSystem.
[2] Gradle Javadoc — org.gradle.nativeplatform.platform.OperatingSystem.
[3] Typical approach: System.getProperty("os.name") (Java SE).
[4] Gradle Public APIs documentation.


copyFlatpakBinary: add Linux guard (using public API) and safer cleanup target

This task depends on packageAppImage and assumes a Linux environment (Flatpak is Linux-only), but has no platform guard. Additionally, using outputs.files.singleFile is fragile; prefer the explicit destination directory.

The proposed fix is on the right track for the cleanup logic, but uses org.gradle.internal.os.OperatingSystem which is an unsupported internal API. Use a public alternative instead (e.g., System.getProperty("os.name") or Gradle's public OperatingSystem from org.gradle.platform if Gradle 7.6+).

Revised fix
 val copyFlatpakBinary = tasks.register<Copy>("copyFlatpakBinary") {
+    onlyIf { System.getProperty("os.name").lowercase().contains("linux") }
     dependsOn("packageAppImage")
 
+    val targetDir = flatpakDir.map { it.asFile.resolve("build") }
-    val appImageOutput = layout.buildDirectory.dir("compose/binaries/main/app/$appName")
+    val appImageOutput = layout.buildDirectory.dir("compose/binaries/main/app/$appName")
 
     from(appImageOutput)
-    into(flatpakDir.map { it.asFile.resolve("build") })
+    into(targetDir)
 
     doFirst {
-        val targetDir = outputs.files.singleFile
-        if (targetDir.exists()) {
-            targetDir.deleteRecursively()
+        val dir = targetDir.get()
+        if (dir.exists()) {
+            dir.deleteRecursively()
         }
-        targetDir.mkdirs()
+        dir.mkdirs()
     }
 }

Comment on lines +300 to +318
tasks.register<Exec>("packageFlatpak") {
dependsOn(copyFlatpakResources)
group = "build"
description = "Build Flatpak for Linux desktop (binary-based)"

val buildDir = flatpakDir.map { it.asFile.resolve("build") }
val repoDir = flatpakDir.map { it.asFile.resolve("repo") }

workingDir(buildDir)
commandLine(
"/usr/bin/flatpak-builder",
"--install-deps-from=flathub",
"--repo=${repoDir.get().absolutePath}",
"--force-clean",
"--disable-rofiles-fuse",
"build/${appId}",
"$appId.yml"
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, locate and read the file to confirm the code exists
find . -name "build.gradle.kts" -path "*/composeApp/*" | head -5

Repository: rainxchzed/Github-Store

Length of output: 96


🏁 Script executed:

# Read the specific lines in the build.gradle.kts file
if [ -f "composeApp/build.gradle.kts" ]; then
  wc -l composeApp/build.gradle.kts
  sed -n '295,320p' composeApp/build.gradle.kts
fi

Repository: rainxchzed/Github-Store

Length of output: 813


🏁 Script executed:

# Search for all Exec task registrations to understand the pattern used
rg 'tasks\.register<Exec>' composeApp/build.gradle.kts -A 15

Repository: rainxchzed/Github-Store

Length of output: 2822


🌐 Web query:

Gradle Provider.get() configuration time caching performance impact

💡 Result:

Short answer: calling Provider.get() during configuration forces eager resolution (and often task/object realization), which increases configuration time and can prevent or reduce the benefit of the configuration cache. Prefer lazy operations (map/flatMap, Property, providers, tasks.named/configureEach) and avoid get() except inside task execution or when you truly need the concrete value at configuration time. [1][2][3]

Why:

  • Provider.get() forces the provider to compute its value immediately (realizing deferred work). That moves potentially expensive work into the configuration phase. [2][5]
  • The configuration phase cost matters for overall build time and for the configuration-cache feature (the cache stores configuration results; expensive work at configuration time still must run on a cache miss and may make caching less effective). [3][1]
  • Using get() can also cause task instances to be created/configured earlier than necessary. Task creation/configuration increases configuration-phase work. Use task avoidance APIs (register/named/configureEach) to avoid that. [2][5]

Practical recommendations:

  • Replace provider.get() with provider.map { ... } or provider.flatMap { ... } when you only need to transform or propagate a value lazily. [5]
  • Use Property, Provider, and the task configuration-avoidance APIs (tasks.register / tasks.named / configureEach) so work runs at execution time or only for tasks that will run. [2][5]
  • If you must observe a provider in configuration, try getOrNull, or limit the scope and ensure the work is cheap; prefer moving heavy work into task actions. [3]
  • Measure with Gradle Profiler and enable configuration cache to see real impact on your project. [1][3]

Sources:

  • Gradle user guide — Avoiding unnecessary task configuration / configuration avoidance. [2]
  • Gradle docs — Lazy vs eager evaluation and performance guidance. [5][3]
  • Gradle blog / configuration cache overview and status. [1][6]

References:
[1] Gradle — Introducing Configuration Caching.
[2] Gradle — Avoiding Unnecessary Task Configuration (task configuration avoidance).
[3] Gradle — Improve the Performance of Gradle Builds (configuration cache & avoid expensive configuration).
[5] Gradle — Lazy vs Eager Evaluation (Provider / Property guidance).
[6] Gradle Blog — Road to configuration cache / state updates.

(If you want, I can search your build for provider.get() usages and show where to change them.)


🌐 Web query:

Gradle doFirst() lazy task configuration best practices

💡 Result:

Short best-practices for using doFirst()/lazy task configuration in Gradle

  • Prefer task registration (tasks.register / tasks.named) and TaskProvider over creating task instances; register keeps configuration deferred until needed. [1]
  • Use configureEach / named / configureWhen-needed instead of withType/all/iterating tasks, which eagerly configures tasks. [1][2]
  • Keep heavy work out of the configuration phase: don’t call provider.get(), resolve configurations, read files, run processes, or perform network I/O during configuration. Defer those to the execution phase (e.g., inside doFirst/doLast or @TaskAction). [2][3]
  • doFirst/doLast add execution-phase actions — they run when the task executes, but adding them happens at configuration time; ensure the action body itself is lazy (use Provider/Property/RegularFileProperty etc. inside the action). Avoid resolving providers inside build script configuration. [4][2]
  • Use Gradle’s lazy property types (Property, DirectoryProperty, ConfigurableFileCollection, Provider) for task inputs/outputs so Gradle can wire dependencies and delay evaluation. Avoid using raw File/String/collection APIs that force eager evaluation. [2][3]
  • For task ordering and dependencies prefer declaring relationships with TaskProvider (dependsOn(builtBy using providers)) so you don’t force instantiation; be mindful that strong relationships (dependsOn/finalizedBy) will cause referenced tasks to be created if they must run. [1]
  • Aim for configuration-cache compatibility: avoid non-deterministic side-effects and runtime execs during configuration; prefer execution-time execs or the providers API only when you intentionally want configuration-time inputs. Test with --configuration-cache. [1][6]

Key docs:

  • Task configuration avoidance / register vs create. [1]
  • Lazy configuration & lazy property types. [2]
  • Best practices for tasks (file collections, inputs/outputs). [3]
  • doFirst/doLast behavior and task actions. [4]

Sources: [1] Gradle — Avoiding Unnecessary Task Configuration; [2] Gradle — Configuring Tasks Lazily / Lazy properties; [3] Gradle — Best Practices for Tasks; [4] Gradle — Creating and Registering Tasks.


🌐 Web query:

flatpak-builder /usr/bin PATH portability Linux distributions

💡 Result:

Short answer: flatpak-builder sets PATH to /app/bin:/usr/bin during build, but both build- and run-time /usr/bin refer to the Flatpak runtime’s /usr (not the host distro’s /usr). Don’t hardcode /usr/bin if you want cross-distro portability — install/ship binaries into /app (or the runtime), rely on PATH (/app/bin first), or call host tools explicitly with flatpak-spawn --host (or provide a wrapper). Sources and details below.

Key points and recommendations

  • Build environment PATH: flatpak-builder default PATH is /app/bin:/usr/bin — /app is the app install prefix; /usr is the mounted runtime. So tools found at /usr/bin are from the runtime/sdk, not the host distro. [1]
  • Runtime sandbox: sandboxed apps also see PATH=/app/bin:/usr/bin and a bind-mounted runtime /usr; host /usr is not exposed at /usr. Host filesystem entries (when allowed) are mounted under /run/host (e.g. /run/host/usr) and many core dirs are blacklisted. Relying on host /usr/bin breaks portability and sandboxing. [2][3]
  • If you need to run a host binary: use flatpak-spawn --host (runs unsandboxed on host) or ship the tool inside the Flatpak (install into /app/bin) or provide a wrapper that uses host-spawn-style helpers. Do not assume /usr/bin on the host is available inside the sandbox. [4][5]

Short checklist to make builds/apps portable across distros

  • Put your app’s executables and runtime libs under /app (use manifest modules to install there). That makes the app self-contained across distributions. [1]
  • Avoid hardcoded absolute paths to /usr/bin; use executable names (rely on PATH) or use /app/bin paths for bundled tools. [1][2]
  • If you must invoke host tools, explicitly call flatpak-spawn --host and document that behaviour; consider bundling alternatives if you need predictable behaviour across distros. [4]

Sources
[1] flatpak-builder command reference — Build environment and PATH.
[2] flatpak sandbox wiki / Sandbox environment (PATH and mounts).
[3] Flatpak sandbox-permissions docs — host mounts and blacklisted dirs (/usr is provided by runtime; host files appear under /run/host).
[4] flatpak-spawn man page / docs (use --host to run on host).
[5] community notes (e.g. Q&A) illustrating /usr/bin host vs runtime behavior.


packageFlatpak: hard-coded /usr/bin/flatpak-builder breaks portability; repoDir.get() at configuration time blocks config-cache

Hard-coding /usr/bin/flatpak-builder assumes the tool is installed in that exact location, but it may be in PATH only or elsewhere depending on the distribution. Inside Flatpak builds, /usr/bin refers to the runtime's tools, not the host's. Calling Provider.get() during configuration forces eager resolution and prevents Gradle's configuration cache from being effective.

Move command resolution to doFirst {} and call the executable by name to rely on PATH:

Fix
 tasks.register<Exec>("packageFlatpak") {
     dependsOn(copyFlatpakResources)
+    onlyIf { org.gradle.internal.os.OperatingSystem.current().isLinux }
     workingDir(buildDir)
-    commandLine(
-        "/usr/bin/flatpak-builder",
-        "--install-deps-from=flathub",
-        "--repo=${repoDir.get().absolutePath}",
-        "--force-clean",
-        "--disable-rofiles-fuse",
-        "build/${appId}",
-        "$appId.yml"
-    )
+    doFirst {
+        commandLine(
+            "flatpak-builder",
+            "--install-deps-from=flathub",
+            "--repo=${repoDir.get().absolutePath}",
+            "--force-clean",
+            "--disable-rofiles-fuse",
+            "build/${appId}",
+            "$appId.yml"
+        )
+    }
 }
🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 300 - 318, The packageFlatpak task
currently hard-codes "/usr/bin/flatpak-builder" and calls repoDir.get() at
configuration time, breaking portability and the Gradle configuration cache;
modify the tasks.register<Exec>("packageFlatpak") so command resolution and
provider dereferencing happen in an action (e.g., doFirst { ... }) — call the
executable as "flatpak-builder" (relying on PATH) and use
repoDir.get().absolutePath and buildDir.get() only inside doFirst (or use
repoDir.get().asFile.absolutePath and buildDir.get().asFile) to avoid
Provider.get() during configuration and ensure the commandLine uses
"flatpak-builder", "--repo=${repoDirPath}", "--force-clean",
"--disable-rofiles-fuse", "build/${appId}", "$appId.yml".

Comment on lines +320 to +329
tasks.register<Exec>("exportFlatpak") {
dependsOn("packageFlatpak")
group = "build"
description = "Export Flatpak bundle (binary-based)"

val versionName = appVersionName

workingDir(flatpakDir)
commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, "master")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Search for flatpak references in the build configuration
rg -i "flatpak" composeApp/build.gradle.kts -B 2 -A 2

Repository: rainxchzed/Github-Store

Length of output: 3999


🏁 Script executed:

# Search for all flatpak-related configuration in the repository
fd -type f -name "*flatpak*" -o -name "*.flatpak*"

Repository: rainxchzed/Github-Store

Length of output: 237


🏁 Script executed:

# Check if /usr/bin/flatpak is used elsewhere
rg "/usr/bin/flatpak" --type kotlin --type gradle

Repository: rainxchzed/Github-Store

Length of output: 751


🏁 Script executed:

# Look at the context around the exportFlatpak task and related tasks
sed -n '280,340p' composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 1834


🏁 Script executed:

# Look for Flatpak manifest files in resources
find . -name "*.yml" -o -name "*.yaml" | grep -i flatpak | head -20

Repository: rainxchzed/Github-Store

Length of output: 193


🏁 Script executed:

# Check the Flatpak manifest to understand branch configuration
cat src/jvmMain/resources/flatpak/manifest.yml 2>/dev/null || cat src/jvmMain/resources/flatpak/*.yml 2>/dev/null | head -100

Repository: rainxchzed/Github-Store

Length of output: 49


🏁 Script executed:

# Check if there's any documentation or README about Flatpak configuration
rg -i "branch|master|stable" src/jvmMain/resources/flatpak/ -A 2 -B 2

Repository: rainxchzed/Github-Store

Length of output: 137


🏁 Script executed:

# Find the actual path to the manifest files
fd -name "manifest.yml" -o -name "zed.rainxch.githubstore.yml"

Repository: rainxchzed/Github-Store

Length of output: 297


🏁 Script executed:

# Read the manifest files
cat ./composeApp/src/jvmMain/resources/flatpak/manifest.yml

Repository: rainxchzed/Github-Store

Length of output: 1127


🏁 Script executed:

# Check if there are any references to branch or "stable" in the project
rg -i "branch|stable" --max-count 20

Repository: rainxchzed/Github-Store

Length of output: 10379


exportFlatpak: hardcoded tool paths affect portability and config-cache support

The /usr/bin/flatpak path is hardcoded and not configurable. This pattern is systemic across all Flatpak tasks (packageFlatpak, exportFlatpak, runFlatpak, and their source variants) using both /usr/bin/flatpak and /usr/bin/flatpak-builder. Consider making these paths configurable to improve portability and support Gradle's configuration cache.

The "master" parameter is the standard Flatpak repository branch (created by flatpak-builder), not a git branch. No changes needed here.

Comment on lines +331 to +340
tasks.register<Exec>("runFlatpak") {
dependsOn("packageFlatpak")
group = "run"
description = "Run the Flatpak locally (binary-based)"

val buildDir = flatpakDir.map { it.asFile.resolve("build") }

workingDir(buildDir)
commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

runFlatpak: use appName instead of hard-coded "GitHub-Store"

If the Flatpak manifest’s command is expected to match appName, keep it consistent and avoid a silent mismatch later.

Proposed fix
-    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
+    commandLine("flatpak-builder", "--run", "build/${appId}", "$appId.yml", appName)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tasks.register<Exec>("runFlatpak") {
dependsOn("packageFlatpak")
group = "run"
description = "Run the Flatpak locally (binary-based)"
val buildDir = flatpakDir.map { it.asFile.resolve("build") }
workingDir(buildDir)
commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
}
tasks.register<Exec>("runFlatpak") {
dependsOn("packageFlatpak")
group = "run"
description = "Run the Flatpak locally (binary-based)"
val buildDir = flatpakDir.map { it.asFile.resolve("build") }
workingDir(buildDir)
commandLine("flatpak-builder", "--run", "build/${appId}", "$appId.yml", appName)
}
🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 331 - 340, The runFlatpak Exec task
currently passes a hard-coded "GitHub-Store" as the Flatpak command which can
mismatch the manifest; update the commandLine call in the
tasks.register<Exec>("runFlatpak") block to use the project property/variable
appName instead of the literal "GitHub-Store" (so
commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}",
"${appId}.yml", appName)); ensure appName is in scope or access it the same way
other variables (like appId/flatpakDir) are referenced.

Comment on lines +373 to +390
tasks.register<Exec>("packageFlatpakSource") {
dependsOn(copyGeneratedSources)
group = "build"
description = "Build Flatpak for Linux desktop (source-based for submission)"

val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
val repoDir = flatpakSourceDir.map { it.asFile.resolve("repo") }

workingDir(buildDir)
commandLine(
"/usr/bin/flatpak-builder",
"--install-deps-from=flathub",
"--repo=${repoDir.get().absolutePath}",
"--force-clean",
"build/${appId}",
"$appId.yml"
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

❓ Verification inconclusive

Script executed:

cd composeApp && wc -l build.gradle.kts

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

412 build.gradle.kts

Script executed:

cd composeApp && sed -n '300,450p' build.gradle.kts | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	tasks.register<Exec>("packageFlatpak") {
     2	    dependsOn(copyFlatpakResources)
     3	    group = "build"
     4	    description = "Build Flatpak for Linux desktop (binary-based)"
     5	
     6	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
     7	    val repoDir = flatpakDir.map { it.asFile.resolve("repo") }
     8	
     9	    workingDir(buildDir)
    10	    commandLine(
    11	        "/usr/bin/flatpak-builder",
    12	        "--install-deps-from=flathub",
    13	        "--repo=${repoDir.get().absolutePath}",
    14	        "--force-clean",
    15	        "--disable-rofiles-fuse",
    16	        "build/${appId}",
    17	        "$appId.yml"
    18	    )
    19	}
    20	
    21	tasks.register<Exec>("exportFlatpak") {
    22	    dependsOn("packageFlatpak")
    23	    group = "build"
    24	    description = "Export Flatpak bundle (binary-based)"
    25	
    26	    val versionName = appVersionName
    27	
    28	    workingDir(flatpakDir)
    29	    commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, "master")
    30	}
    31	
    32	tasks.register<Exec>("runFlatpak") {
    33	    dependsOn("packageFlatpak")
    34	    group = "run"
    35	    description = "Run the Flatpak locally (binary-based)"
    36	
    37	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
    38	
    39	    workingDir(buildDir)
    40	    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
    41	}
    42	
    43	// Source-based tasks for Flathub submission
    44	val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
    45	    val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
    46	
    47	    from(resourcesDir) {
    48	        include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
    49	    }
    50	    into(flatpakSourceDir.map { it.asFile.resolve("build") })
    51	
    52	    doFirst {
    53	        val targetDir = outputs.files.singleFile
    54	        if (targetDir.exists()) {
    55	            targetDir.deleteRecursively()
    56	        }
    57	        targetDir.mkdirs()
    58	    }
    59	}
    60	
    61	val copyGeneratedSources = tasks.register<Copy>("copyGeneratedSources") {
    62	    dependsOn(copyFlatpakSourceResources)
    63	
    64	    val sourcesFile = layout.projectDirectory.file("generated-sources.json")
    65	
    66	    from(sourcesFile)
    67	    into(flatpakSourceDir.map { it.asFile.resolve("build") })
    68	
    69	    onlyIf {
    70	        sourcesFile.asFile.exists()
    71	    }
    72	}
    73	
    74	tasks.register<Exec>("packageFlatpakSource") {
    75	    dependsOn(copyGeneratedSources)
    76	    group = "build"
    77	    description = "Build Flatpak for Linux desktop (source-based for submission)"
    78	
    79	    val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
    80	    val repoDir = flatpakSourceDir.map { it.asFile.resolve("repo") }
    81	
    82	    workingDir(buildDir)
    83	    commandLine(
    84	        "/usr/bin/flatpak-builder",
    85	        "--install-deps-from=flathub",
    86	        "--repo=${repoDir.get().absolutePath}",
    87	        "--force-clean",
    88	        "build/${appId}",
    89	        "$appId.yml"
    90	    )
    91	}
    92	
    93	tasks.register<Exec>("exportFlatpakSource") {
    94	    dependsOn("packageFlatpakSource")
    95	    group = "build"
    96	    description = "Export Flatpak bundle (source-based)"
    97	
    98	    val versionName = appVersionName
    99	
   100	    workingDir(flatpakSourceDir)
   101	    commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, "master")
   102	}
   103	
   104	tasks.register<Exec>("runFlatpakSource") {
   105	    dependsOn("packageFlatpakSource")
   106	    group = "run"
   107	    description = "Run the Flatpak locally (source-based)"
   108	
   109	    val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
   110	
   111	    workingDir(buildDir)
   112	    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
   113	}

Script executed:

cd composeApp && rg -n "tasks.register<Exec>" build.gradle.kts

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

300:tasks.register<Exec>("packageFlatpak") {
320:tasks.register<Exec>("exportFlatpak") {
331:tasks.register<Exec>("runFlatpak") {
373:tasks.register<Exec>("packageFlatpakSource") {
392:tasks.register<Exec>("exportFlatpakSource") {
403:tasks.register<Exec>("runFlatpakSource") {

Script executed:

cd composeApp && rg -B 5 -A 15 "onlyIf.*os\.|only.*linux\.|OS\.isLinux" build.gradle.kts

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 1


Script executed:

cd composeApp && sed -n '1,300p' build.gradle.kts | tail -100 | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	        }
     2	    }
     3	    compileOptions {
     4	        sourceCompatibility = JavaVersion.VERSION_21
     5	        targetCompatibility = JavaVersion.VERSION_21
     6	    }
     7	    buildFeatures {
     8	        buildConfig = true
     9	    }
    10	}
    11	
    12	tasks.named<Test>("jvmTest") {
    13	    useJUnitPlatform()
    14	}
    15	
    16	dependencies {
    17	    debugImplementation(compose.uiTooling)
    18	
    19	    ksp(libs.androidx.room.compiler)
    20	}
    21	
    22	room {
    23	    schemaDirectory("$projectDir/schemas")
    24	}
    25	
    26	compose.desktop {
    27	    application {
    28	        mainClass = "zed.rainxch.githubstore.MainKt"
    29	        nativeDistributions {
    30	            packageName = "GitHub-Store"
    31	            packageVersion = appVersionName
    32	            vendor = "rainxchzed"
    33	            includeAllModules = true
    34	            targetFormats(
    35	                TargetFormat.Dmg,
    36	                TargetFormat.Pkg,
    37	                TargetFormat.Exe,
    38	                TargetFormat.Msi,
    39	                TargetFormat.Deb,
    40	                TargetFormat.Rpm,
    41	                TargetFormat.AppImage
    42	            )
    43	            windows {
    44	                iconFile.set(project.file("logo/app_icon.ico"))
    45	                menuGroup = "Github Store"
    46	                shortcut = true
    47	                perUserInstall = true
    48	            }
    49	            macOS {
    50	                iconFile.set(project.file("logo/app_icon.icns"))
    51	                bundleID = "zed.rainxch.githubstore"
    52	            }
    53	            linux {
    54	                iconFile.set(project.file("logo/app_icon.png"))
    55	                appRelease = appVersionCode.toString()
    56	                debPackageVersion = appVersionName
    57	                menuGroup = "Development"
    58	                appCategory = "Development"
    59	            }
    60	        }
    61	    }
    62	}
    63	
    64	val appId = "zed.rainxch.githubstore"
    65	val appName = "GitHub-Store"
    66	val flatpakDir = layout.buildDirectory.dir("flatpak")
    67	val flatpakSourceDir = layout.buildDirectory.dir("flatpak-source")
    68	
    69	// Define copy tasks separately to avoid configuration cache issues
    70	val copyFlatpakBinary = tasks.register<Copy>("copyFlatpakBinary") {
    71	    dependsOn("packageAppImage")
    72	
    73	    val appImageOutput = layout.buildDirectory.dir("compose/binaries/main/app/$appName")
    74	
    75	    from(appImageOutput)
    76	    into(flatpakDir.map { it.asFile.resolve("build") })
    77	
    78	    doFirst {
    79	        val targetDir = outputs.files.singleFile
    80	        if (targetDir.exists()) {
    81	            targetDir.deleteRecursively()
    82	        }
    83	        targetDir.mkdirs()
    84	    }
    85	}
    86	
    87	val copyFlatpakResources = tasks.register<Copy>("copyFlatpakResources") {
    88	    dependsOn(copyFlatpakBinary)
    89	
    90	    val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
    91	
    92	    from(resourcesDir) {
    93	        include("manifest.yml", "*.desktop", "*.xml", "app_icon.png")
    94	    }
    95	    into(flatpakDir.map { it.asFile.resolve("build") })
    96	
    97	    rename("manifest.yml", "$appId.yml")
    98	}
    99	
   100	tasks.register<Exec>("packageFlatpak") {

Script executed:

cd composeApp && sed -n '300,415p' build.gradle.kts | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	tasks.register<Exec>("packageFlatpak") {
     2	    dependsOn(copyFlatpakResources)
     3	    group = "build"
     4	    description = "Build Flatpak for Linux desktop (binary-based)"
     5	
     6	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
     7	    val repoDir = flatpakDir.map { it.asFile.resolve("repo") }
     8	
     9	    workingDir(buildDir)
    10	    commandLine(
    11	        "/usr/bin/flatpak-builder",
    12	        "--install-deps-from=flathub",
    13	        "--repo=${repoDir.get().absolutePath}",
    14	        "--force-clean",
    15	        "--disable-rofiles-fuse",
    16	        "build/${appId}",
    17	        "$appId.yml"
    18	    )
    19	}
    20	
    21	tasks.register<Exec>("exportFlatpak") {
    22	    dependsOn("packageFlatpak")
    23	    group = "build"
    24	    description = "Export Flatpak bundle (binary-based)"
    25	
    26	    val versionName = appVersionName
    27	
    28	    workingDir(flatpakDir)
    29	    commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, "master")
    30	}
    31	
    32	tasks.register<Exec>("runFlatpak") {
    33	    dependsOn("packageFlatpak")
    34	    group = "run"
    35	    description = "Run the Flatpak locally (binary-based)"
    36	
    37	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
    38	
    39	    workingDir(buildDir)
    40	    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
    41	}
    42	
    43	// Source-based tasks for Flathub submission
    44	val copyFlatpakSourceResources = tasks.register<Copy>("copyFlatpakSourceResources") {
    45	    val resourcesDir = layout.projectDirectory.dir("src/jvmMain/resources/flatpak")
    46	
    47	    from(resourcesDir) {
    48	        include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
    49	    }
    50	    into(flatpakSourceDir.map { it.asFile.resolve("build") })
    51	
    52	    doFirst {
    53	        val targetDir = outputs.files.singleFile
    54	        if (targetDir.exists()) {
    55	            targetDir.deleteRecursively()
    56	        }
    57	        targetDir.mkdirs()
    58	    }
    59	}
    60	
    61	val copyGeneratedSources = tasks.register<Copy>("copyGeneratedSources") {
    62	    dependsOn(copyFlatpakSourceResources)
    63	
    64	    val sourcesFile = layout.projectDirectory.file("generated-sources.json")
    65	
    66	    from(sourcesFile)
    67	    into(flatpakSourceDir.map { it.asFile.resolve("build") })
    68	
    69	    onlyIf {
    70	        sourcesFile.asFile.exists()
    71	    }
    72	}
    73	
    74	tasks.register<Exec>("packageFlatpakSource") {
    75	    dependsOn(copyGeneratedSources)
    76	    group = "build"
    77	    description = "Build Flatpak for Linux desktop (source-based for submission)"
    78	
    79	    val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
    80	    val repoDir = flatpakSourceDir.map { it.asFile.resolve("repo") }
    81	
    82	    workingDir(buildDir)
    83	    commandLine(
    84	        "/usr/bin/flatpak-builder",
    85	        "--install-deps-from=flathub",
    86	        "--repo=${repoDir.get().absolutePath}",
    87	        "--force-clean",
    88	        "build/${appId}",
    89	        "$appId.yml"
    90	    )
    91	}
    92	
    93	tasks.register<Exec>("exportFlatpakSource") {
    94	    dependsOn("packageFlatpakSource")
    95	    group = "build"
    96	    description = "Export Flatpak bundle (source-based)"
    97	
    98	    val versionName = appVersionName
    99	
   100	    workingDir(flatpakSourceDir)
   101	    commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, "master")
   102	}
   103	
   104	tasks.register<Exec>("runFlatpakSource") {
   105	    dependsOn("packageFlatpakSource")
   106	    group = "run"
   107	    description = "Run the Flatpak locally (source-based)"
   108	
   109	    val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }
   110	
   111	    workingDir(buildDir)
   112	    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
   113	}

Script executed:

cd composeApp && rg -n "commandLine\|doFirst" build.gradle.kts | head -30

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0


Script executed:

cd composeApp && head -50 build.gradle.kts | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	import org.jetbrains.compose.desktop.application.dsl.TargetFormat
     2	import org.jetbrains.kotlin.gradle.dsl.JvmTarget
     3	import java.util.Properties
     4	
     5	plugins {
     6	    alias(libs.plugins.kotlinMultiplatform)
     7	    alias(libs.plugins.androidApplication)
     8	    alias(libs.plugins.composeMultiplatform)
     9	    alias(libs.plugins.composeCompiler)
    10	    alias(libs.plugins.composeHotReload)
    11	    alias(libs.plugins.kotlinSerialization)
    12	    alias(libs.plugins.kotest)
    13	    alias(libs.plugins.ksp)
    14	    alias(libs.plugins.androidx.room)
    15	}
    16	
    17	val appVersionName = "1.5.1"
    18	val appVersionCode = 10
    19	
    20	val localProps = Properties().apply {
    21	    val file = rootProject.file("local.properties")
    22	    if (file.exists()) file.inputStream().use { this.load(it) }
    23	}
    24	val localGithubClientId =
    25	    (localProps.getProperty("GITHUB_CLIENT_ID") ?: "Ov23linTY28VFpFjFiI9").trim()
    26	
    27	// Generate BuildConfig for JVM (Configuration Cache Compatible)
    28	val generateJvmBuildConfig = tasks.register("generateJvmBuildConfig") {
    29	    val outputDir = layout.buildDirectory.dir("generated/buildconfig/jvm")
    30	    val clientId = localGithubClientId
    31	    val versionName = appVersionName
    32	
    33	    outputs.dir(outputDir)
    34	
    35	    doLast {
    36	        val file = outputDir.get().asFile.resolve("zed/rainxch/githubstore/BuildConfig.kt")
    37	        file.parentFile.mkdirs()
    38	        file.writeText(
    39	            """
    40	            package zed.rainxch.githubstore
    41	            
    42	            object BuildConfig {
    43	                const val GITHUB_CLIENT_ID = "$clientId"
    44	                const val VERSION_NAME = "$versionName"
    45	            }
    46	        """.trimIndent()
    47	        )
    48	    }
    49	}
    50	

Script executed:

cd composeApp && rg -n "doFirst|onlyIf|OS\.|System" build.gradle.kts | head -20

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

278:    doFirst {
351:    doFirst {
368:    onlyIf {

Script executed:

cd composeApp && sed -n '70,100p' build.gradle.kts | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	            implementation(libs.ktor.client.content.negotiation)
     2	            implementation(libs.ktor.serialization.kotlinx.json)
     3	            implementation(libs.kotlinx.serialization.json)
     4	            // Secure storage (direct coordinate to avoid catalog accessor mismatch)
     5	            implementation(libs.androidx.security.crypto)
     6	            implementation(libs.core.splashscreen)
     7	        }
     8	        commonMain.dependencies {
     9	            implementation(compose.runtime)
    10	            implementation(compose.foundation)
    11	            implementation(libs.material3)
    12	            implementation(compose.materialIconsExtended)
    13	            implementation(compose.ui)
    14	            implementation(compose.components.resources)
    15	            implementation(compose.components.uiToolingPreview)
    16	            implementation(libs.androidx.lifecycle.viewmodelCompose)
    17	            implementation(libs.androidx.lifecycle.runtimeCompose)
    18	
    19	            // Koin DI
    20	            implementation(libs.koin.core)
    21	            implementation(libs.koin.compose)
    22	            implementation(libs.koin.compose.viewmodel)
    23	
    24	            // HTTP and serialization
    25	            implementation(libs.ktor.client.core)
    26	            implementation(libs.ktor.client.content.negotiation)
    27	            implementation(libs.ktor.serialization.kotlinx.json)
    28	            implementation(libs.kotlinx.serialization.json)
    29	            implementation(libs.kotlinx.coroutines.core)
    30	
    31	            // Logging

Script executed:

cd composeApp && git log --oneline -20 -- composeApp/build.gradle.kts 2>/dev/null || echo "Git history not available"

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0


Script executed:

cd composeApp && sed -n '300,340p' build.gradle.kts | cat -n

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

     1	tasks.register<Exec>("packageFlatpak") {
     2	    dependsOn(copyFlatpakResources)
     3	    group = "build"
     4	    description = "Build Flatpak for Linux desktop (binary-based)"
     5	
     6	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
     7	    val repoDir = flatpakDir.map { it.asFile.resolve("repo") }
     8	
     9	    workingDir(buildDir)
    10	    commandLine(
    11	        "/usr/bin/flatpak-builder",
    12	        "--install-deps-from=flathub",
    13	        "--repo=${repoDir.get().absolutePath}",
    14	        "--force-clean",
    15	        "--disable-rofiles-fuse",
    16	        "build/${appId}",
    17	        "$appId.yml"
    18	    )
    19	}
    20	
    21	tasks.register<Exec>("exportFlatpak") {
    22	    dependsOn("packageFlatpak")
    23	    group = "build"
    24	    description = "Export Flatpak bundle (binary-based)"
    25	
    26	    val versionName = appVersionName
    27	
    28	    workingDir(flatpakDir)
    29	    commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}.flatpak", appId, "master")
    30	}
    31	
    32	tasks.register<Exec>("runFlatpak") {
    33	    dependsOn("packageFlatpak")
    34	    group = "run"
    35	    description = "Run the Flatpak locally (binary-based)"
    36	
    37	    val buildDir = flatpakDir.map { it.asFile.resolve("build") }
    38	
    39	    workingDir(buildDir)
    40	    commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
    41	}

Script executed:

cd composeApp && rg -C 5 "Provider|doFirst|onlyIf.*linux" build.gradle.kts | head -50

Repository: rainxchzed/Github-Store


Repository: rainxchzed/Github-Store
Exit code: 0

stdout:

    val appImageOutput = layout.buildDirectory.dir("compose/binaries/main/app/$appName")

    from(appImageOutput)
    into(flatpakDir.map { it.asFile.resolve("build") })

    doFirst {
        val targetDir = outputs.files.singleFile
        if (targetDir.exists()) {
            targetDir.deleteRecursively()
        }
        targetDir.mkdirs()
--
    from(resourcesDir) {
        include("zed.rainxch.githubstore.yml", "*.desktop", "*.xml", "app_icon.png")
    }
    into(flatpakSourceDir.map { it.asFile.resolve("build") })

    doFirst {
        val targetDir = outputs.files.singleFile
        if (targetDir.exists()) {
            targetDir.deleteRecursively()
        }
        targetDir.mkdirs()

packageFlatpakSource: Add Linux-only guard, use PATH for tool discovery, defer command configuration

Both packageFlatpak and packageFlatpakSource have the same concerns: hardcoded /usr/bin/flatpak-builder path (not portable), Provider.get() forcing eager evaluation, and commandLine set in task configuration rather than deferred. Add onlyIf { System.getProperty("os.name").lowercase().contains("linux") } or similar guard, replace the hardcoded path with PATH-based lookup (or configurable property), and move commandLine into doFirst.

Comment on lines +392 to +401
tasks.register<Exec>("exportFlatpakSource") {
dependsOn("packageFlatpakSource")
group = "build"
description = "Export Flatpak bundle (source-based)"

val versionName = appVersionName

workingDir(flatpakSourceDir)
commandLine("/usr/bin/flatpak", "build-bundle", "repo", "github-store-${versionName}-source.flatpak", appId, "master")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the build.gradle.kts file around the exportFlatpak task to see the comparison
cd composeApp && wc -l build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 87


🏁 Script executed:

# Search for the exportFlatpak task to see what issues exist there
rg -n "exportFlatpak" composeApp/build.gradle.kts -B 2 -A 8

Repository: rainxchzed/Github-Store

Length of output: 850


🏁 Script executed:

# Check for hardcoded tool paths to see if there's a pattern
rg -n "/usr/bin" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 607


🏁 Script executed:

# Look for any configuration of the flatpak tool path or branch name
rg -n "flatpak|master" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 1660


Use which branch and make flatpak tool path portable

Both exportFlatpak and exportFlatpakSource hardcode /usr/bin/flatpak (fails on systems where flatpak is installed elsewhere) and "master" branch. Use a configurable tool path (e.g., via environment variable or exec availability) and confirm whether "master" is the correct/intended branch or should be configurable.

🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 392 - 401, The exportFlatpakSource
task (tasks.register<Exec>("exportFlatpakSource")) hardcodes the flatpak binary
path and the "master" branch; make the tool path and branch configurable by:
read an env var or Gradle property (e.g., System.getenv("FLATPAK_CMD") or
project.findProperty("flatpakCmd")) and fall back to resolving the executable
via runtime lookup (e.g., "which flatpak" or default "flatpak"), and similarly
read a branch property/env var (e.g., FLATPAK_BRANCH or project property
"flatpakBranch") defaulting to "master" or "main" as appropriate; then update
the commandLine invocation in exportFlatpakSource (and mirror the same change in
exportFlatpak) to use the resolved flatpak command and the configurable branch
variable.

Comment on lines +403 to +412
tasks.register<Exec>("runFlatpakSource") {
dependsOn("packageFlatpakSource")
group = "run"
description = "Run the Flatpak locally (source-based)"

val buildDir = flatpakSourceDir.map { it.asFile.resolve("build") }

workingDir(buildDir)
commandLine("/usr/bin/flatpak-builder", "--run", "build/${appId}", "${appId}.yml", "GitHub-Store")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the gradle build file
fd build.gradle.kts | head -20

Repository: rainxchzed/Github-Store

Length of output: 111


🏁 Script executed:

# Check the file size to determine how to read it
wc -l composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 98


🏁 Script executed:

# Read the specific lines mentioned in the comment plus surrounding context
sed -n '380,450p' composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 1033


🏁 Script executed:

# Search for other Flatpak-related tasks and appName/appId definitions
rg -n "Flatpak|appName|appId" composeApp/build.gradle.kts | head -30

Repository: rainxchzed/Github-Store

Length of output: 1793


🏁 Script executed:

# Get the exact lines for both runFlatpak and runFlatpakSource tasks
sed -n '331,340p' composeApp/build.gradle.kts
sed -n '403,412p' composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 770


🏁 Script executed:

# Check if there are any other references to how flatpak-builder or similar tools are invoked elsewhere
rg -n "flatpak-builder|flatpak\"|appName" composeApp/build.gradle.kts

Repository: rainxchzed/Github-Store

Length of output: 967


runFlatpakSource and runFlatpak: use PATH for flatpak-builder and the appName variable

Both tasks hardcode /usr/bin/flatpak-builder and the string "GitHub-Store". Use flatpak-builder via PATH and replace the hardcoded string with appName for better portability and consistency.

🤖 Prompt for AI Agents
In @composeApp/build.gradle.kts around lines 403 - 412, The
tasks.register<Exec>("runFlatpakSource") (and the analogous runFlatpak task)
currently hardcode the flatpak-builder binary path and the hardcoded
"GitHub-Store" app name; change the commandLine to call "flatpak-builder" (so it
is resolved via PATH) and replace the literal "GitHub-Store" with the existing
appName variable so the command becomes portable and consistent with the rest of
the build.

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