-
-
Notifications
You must be signed in to change notification settings - Fork 118
Pub flatpak #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Pub flatpak #168
Conversation
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.
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this 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: Movekotlinx.coroutinesSwingfromcommonMaintojvmMain—it breaks Android builds.
kotlinx-coroutines-swingis a JVM/Swing desktop–only module requiringjavax.swingand other Java Desktop APIs unavailable on Android. Placing it incommonMainexposes 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}inpackageFlatpakandpackageFlatpakSourcetasks callsProvider.get()at configuration time. Move thecommandLinesetup into adoFirst {}block or usemap()to keep the configuration cache friendly.outputs.files.singleFileincopyFlatpakBinaryandcopyFlatpakSourceResourceseagerly resolves the destination and breaks laziness. Instead, use the known destination directly:flatpakDir.get().asFile.resolve("build")orflatpakSourceDir.get().asFile.resolve("build"), or configureinto()lazily with a closure.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
composeApp/logo/app_icon.pngis excluded by!**/*.pngcomposeApp/src/jvmMain/resources/flatpak/app_icon.pngis excluded by!**/*.png
📒 Files selected for processing (5)
composeApp/build.gradle.ktscomposeApp/src/jvmMain/resources/flatpak/manifest.ymlcomposeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.appdata.xmlcomposeApp/src/jvmMain/resources/flatpak/zed.rainxch.githubstore.desktopcomposeApp/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.ymlandzed.rainxch.githubstore.yml) install the metadata file under different names:
manifest.ymluseszed.rainxch.githubstore.appdata.xmlzed.rainxch.githubstore.ymluseszed.rainxch.githubstore.metainfo.xmlPick one convention (
.metainfo.xmlis 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-Storeparameter is correct and properly matches the executable name defined in bothmanifest.ymlandbuild.gradle.kts. However, theStartupWMClass=GitHub-Storedeclaration has a potential naming mismatch:
- The application window sets
title = "Github Store"(space-separated) inmain.kt, whileStartupWMClass=GitHub-Storeuses a hyphenated form. Compose Desktop may derive the actualWM_CLASSfrom the window title or application configuration, potentially causing taskbar/dock grouping issues with duplicate icons.The
%Uparameter is appropriately used for URI handling. To confirm the exact WM_CLASS at runtime, launch the app and check withxprop WM_CLASS(X11) or equivalent Wayland tooling.
| // 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") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ 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 -20Repository: 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 -20Repository: 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 -100Repository: 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 -100Repository: 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 fRepository: 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/nullRepository: 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.xmlRepository: 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.desktopRepository: 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.ymlRepository: 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 -20Repository: 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 3Repository: 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.08 → org.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.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
There was a problem hiding this 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
📒 Files selected for processing (1)
composeApp/build.gradle.kts
🔇 Additional comments (2)
composeApp/build.gradle.kts (2)
360-371: LGTM!The conditional copying using
onlyIfis 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 fileapp_icon.pngis 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.
|
|
||
| implementation(libs.kotlinx.coroutinesSwing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
There was a problem hiding this 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/FlatpakYou now have
val appId/val appName, butandroid.defaultConfig.applicationIdandcompose.desktop.nativeDistributions.packageNamestill use literals. Consider moving these constants earlier and reusing them to keep packaging identifiers aligned.
343-358:copyFlatpakSourceResources: consider renaming the manifest to$appId.ymlfor symmetryRight now it copies
zed.rainxch.githubstore.ymland later references$appId.yml(which happens to match today). Adding an explicitrename(...)here makes future appId changes safer.
360-371:copyGeneratedSources: optional file copy is fine; consider failing fast if it’s requiredIf
generated-sources.jsonis required for the source-based manifest, consider makingpackageFlatpakSourcefail with a clear message when it’s missing, rather than silently proceeding.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 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 consistentlyLooks good overall; minor nit: you hardcode includes and later hardcode the run command name—consider using
appNameeverywhere you need the command string so renames don’t silently break.
|
|
||
| implementation(libs.kotlinx.coroutinesSwing) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 -5Repository: 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 -30Repository: 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).
| 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() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
fd "build.gradle.kts" | head -5Repository: 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 -50Repository: rainxchzed/Github-Store
Length of output: 1594
🏁 Script executed:
rg "packageAppImage|register.*AppImage" composeApp/build.gradle.kts -B 2 -A 5Repository: rainxchzed/Github-Store
Length of output: 407
🏁 Script executed:
rg "OperatingSystem|isLinux|isWindows|isMac" composeApp/build.gradle.ktsRepository: rainxchzed/Github-Store
Length of output: 49
🏁 Script executed:
rg "outputs\.files\.singleFile" composeApp/build.gradle.ktsRepository: rainxchzed/Github-Store
Length of output: 164
🏁 Script executed:
head -50 composeApp/build.gradle.ktsRepository: rainxchzed/Github-Store
Length of output: 1652
🏁 Script executed:
rg "import|onlyIf" composeApp/build.gradle.kts | head -20Repository: rainxchzed/Github-Store
Length of output: 222
🏁 Script executed:
rg "packageAppImage" . -A 3 -B 1 --type kotlinRepository: rainxchzed/Github-Store
Length of output: 408
🏁 Script executed:
rg "onlyIf.*Linux|onlyIf.*Windows|onlyIf.*platform" composeApp/build.gradle.kts -B 2 -A 2Repository: rainxchzed/Github-Store
Length of output: 49
🏁 Script executed:
grep -n "onlyIf" composeApp/build.gradle.ktsRepository: rainxchzed/Github-Store
Length of output: 83
🏁 Script executed:
rg "System.getProperty|os\.name" composeApp/build.gradle.ktsRepository: 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.ktsRepository: 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:
- 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")) }
- 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()
}
}| 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" | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, locate and read the file to confirm the code exists
find . -name "build.gradle.kts" -path "*/composeApp/*" | head -5Repository: 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
fiRepository: 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 15Repository: 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".
| 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") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for flatpak references in the build configuration
rg -i "flatpak" composeApp/build.gradle.kts -B 2 -A 2Repository: 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 gradleRepository: 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.ktsRepository: 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 -20Repository: 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 -100Repository: 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 2Repository: 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.ymlRepository: 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 20Repository: 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.
| 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") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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" | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Verification inconclusive
Script executed:
cd composeApp && wc -l build.gradle.ktsRepository: 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 -nRepository: 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.ktsRepository: 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.ktsRepository: rainxchzed/Github-Store
Repository: rainxchzed/Github-Store
Exit code: 1
Script executed:
cd composeApp && sed -n '1,300p' build.gradle.kts | tail -100 | cat -nRepository: 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 -nRepository: 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 -30Repository: rainxchzed/Github-Store
Repository: rainxchzed/Github-Store
Exit code: 0
Script executed:
cd composeApp && head -50 build.gradle.kts | cat -nRepository: 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 -20Repository: 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 -nRepository: 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 -nRepository: 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 -50Repository: 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.
| 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") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.ktsRepository: 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 8Repository: 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.ktsRepository: 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.ktsRepository: 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.
| 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") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the gradle build file
fd build.gradle.kts | head -20Repository: rainxchzed/Github-Store
Length of output: 111
🏁 Script executed:
# Check the file size to determine how to read it
wc -l composeApp/build.gradle.ktsRepository: 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.ktsRepository: 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 -30Repository: 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.ktsRepository: 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.ktsRepository: 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.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.