Thanks for your interest in helping out. This document covers everything you need to ship a change β from setting up the project locally to landing a clean PR.
If you're contributing to a sibling repo (backend or api), check that repo's own contributing notes; the rules below are specific to the GitHub-Store client app.
- Code of conduct
- Ways to contribute
- Reporting bugs
- Suggesting features
- Local development setup
- Project layout
- Coding conventions
- Branching and commit style
- Pull request process
- Translations
- Release flow
- Security disclosures
- Questions
Everyone interacting with this project β issues, pull requests, Discord, anywhere β is expected to follow our Code of Conduct (Contributor Covenant). Short version: be respectful, assume good intent, disagree about ideas β never about people. Maintainers will enforce it.
Plenty of ways to help, even without writing code:
- Report bugs. Reproducible reports with logs are gold.
- Suggest features. Open an issue describing the user-facing problem first; the implementation can be discussed there.
- Triage issues. Reproduce open bugs, ask for missing info, label them.
- Write code. Pick up a
good first issueor anyhelp wantedissue. - Translate strings. See Translations.
- Test pre-releases. Watch the repo for new tags and try them out.
Two ways:
- Inside the app:
Profile β Send feedback. Auto-fills app version, platform, and installer. Pick the channel (email or GitHub issue) and you're done. - On GitHub: open a bug report. Tell us what went wrong, how to reproduce, your setup. Logs and screenshots help but aren't required.
If you have logs handy, grab them from:
- Android:
adb logcat | grep zed.rainxch.githubstore - Desktop:
~/Library/Logs/GitHub-Store/session.log(macOS),%LOCALAPPDATA%/GitHub-Store/logs/session.log(Windows),$XDG_STATE_HOME/GitHub-Store/logs/session.log(Linux). Crashes also drop acrash-<timestamp>.lognext to it.
Open a feature request. Lead with the pain, not the solution: what are you trying to do, where does the app fall short?
For bigger ideas (new screens, new platform, architectural shifts), open the issue first and wait for a quick discussion before writing code.
- JDK 21 (Temurin recommended). Set
JAVA_HOMEaccordingly. - Android Studio Hedgehog or newer with the Kotlin Multiplatform plugin.
- Android SDK for Android builds (target API 36, min API 26).
- Git with a configured username/email.
git clone https://github.com/OpenHub-Store/GitHub-Store.git
cd GitHub-StoreCreate local.properties in the repo root:
sdk.dir=/path/to/Android/sdk
GITHUB_CLIENT_ID=your-oauth-client-id-or-leave-blank-for-localGITHUB_CLIENT_ID is only needed if you want to test the GitHub OAuth device-flow login locally. The rest of the app works without it.
# Android debug build
./gradlew :composeApp:assembleDebug
# Android release build (needs signing config in local.properties)
./gradlew :composeApp:assembleRelease
# Desktop run (dev mode)
./gradlew :composeApp:run
# Desktop installers
./gradlew :composeApp:packageExe :composeApp:packageMsi # Windows
./gradlew :composeApp:packageDmg :composeApp:packagePkg # macOS
./gradlew :composeApp:packageDeb :composeApp:packageRpm # Linux
# Full build check (compile + lint)
./gradlew build
# Format check (ktlint)
./gradlew ktlintCheck
# Auto-format
./gradlew ktlintFormatThe first build will pull a lot of dependencies. Subsequent builds reuse the Gradle build cache (enabled in gradle.properties).
composeApp/ # Main app entry points + navigation
core/
domain/ # Repository interfaces, models, use cases
data/ # Repo implementations, Ktor, Room, DI
presentation/ # Shared theme + reusable Compose components
feature/
apps/ # Installed apps management
auth/ # GitHub OAuth device flow
details/ # Repo + release details
dev-profile/ # Developer / user profile
favourites/ # Saved favorites
home/ # Discovery (trending / hot / popular)
profile/ # User profile, settings, appearance
search/ # Search with filters
starred/ # Starred repos
build-logic/convention/ # Custom Gradle convention plugins
Each feature/<name>/ typically has up to three sub-modules: domain/, data/, presentation/. Some features (favourites, starred) are presentation-only and consume core repositories directly.
The full architectural rundown lives in CLAUDE.md. Per-feature notes live in feature/<name>/CLAUDE.md.
We use Kotlin's official style (kotlin.code.style=official). CI lints every PR with ktlint. The big rules:
- Clean Architecture + MVI. Domain layer has zero framework dependencies. Data layer implements domain interfaces. Presentation layer holds ViewModels and Compose code.
- State / Action / Event pattern for every screen:
class XViewModel : ViewModel() { private val _state = MutableStateFlow(XState()) val state = _state.asStateFlow() private val _events = Channel<XEvent>() val events = _events.receiveAsFlow() fun onAction(action: XAction) { ... } }
- Sealed interfaces for actions, events, navigation routes.
- Koin for DI. Each feature module exposes a Koin module from
data/di/SharedModule.kt. ViewModels are wired incomposeApp/.../app/di/initKoin.ktand injected viakoinViewModel(). - Type-safe navigation via
@Serializablesealed interfaceGithubStoreGraph.
commonMainβ shared code (Compose UI, ViewModels, repository contracts).androidMainβ Android-only platform impls (Shizuku, PackageManager, OkHttp).jvmMainβ Desktop-only platform impls (CIO, file paths, native installers).
- Packages:
zed.rainxch.{module}.{layer}. - Private state: underscore prefix (
_state,_events). - Composables:
PascalCase. - Boolean state:
isXxx/hasXxx/canXxx.
- Don't write KDoc / docstrings to explain a function unless the function's intent isn't obvious from its signature and body.
- Inline comments are reserved for non-obvious invariants, tricky concurrency, third-party-bug workarounds, or counter-intuitive choices. If reading the code answers the "why," delete the comment.
- This applies globally. Reviewers will push back on filler comments.
- Kotlin 2.3.10 Β· Compose Multiplatform 1.10.x Β· Ktor 3.x Β· Room 2.8.x Β· Koin 4.x.
- All versions live in
gradle/libs.versions.toml(Version Catalog). Adding a dep? Add the version + alias there, not directly in the module'sbuild.gradle.kts. - Convention plugins in
build-logic/convention/standardize module setup. Adding a new module? Pick the right plugin (convention.kmp.library,convention.cmp.library,convention.cmp.feature, etc.) instead of hand-rolling the build script.
- Default branch:
main. - Never commit directly to
main. Open a feature branch first. - Naming convention (matches what's already in the repo):
feat/<issue#>-<short-slug>for features.fix/<short-slug>for bug fixes.chore/<short-slug>for refactors, dep bumps, docs, build cleanup.- Examples:
feat/470-sui-support,fix/auth-stuck-on-direct-and-dialog-loop,chore/drop-legacy-query-hash.
- One commit = one logical change. Don't bundle a refactor with a feature, or two unrelated features in the same commit. When asked to commit a batch of work, split it into the smallest meaningful commits.
- Commit messages are short and imperative. Match the repo's existing style β usually a single sentence under 72 characters:
- β
fix: clear parked install metadata once the system confirms install - β
feat: support multi-select platform filter on Home - β
fixed bug/wip/more changes
- β
- Long bodies are fine for non-trivial changes β explain the why, not the what (the diff already shows the what).
- AI assistants are fine β use whatever helps you ship. Just make sure the commit message + PR description are written in your own voice and the code is one you've read and stand behind. Auto-generated
Co-Authored-By: Claude/Cursor/...trailers add noise togit log; trim them before pushing.
- Open or claim an issue before doing anything substantial. For tiny fixes (typos, one-liners) you can skip this step.
- Branch from latest
main:git fetch origin git checkout -b feat/123-some-thing origin/main
- Make the change. Keep the PR focused; if it grows beyond one logical change, split it into a stack.
- Run the local checks:
(Or the narrower module-specific tasks if you know the change is scoped.)
./gradlew ktlintCheck build
- Push and open a PR against
main. Fill out the description with:- What the change does.
- Why β the user-facing problem or technical motivation.
- How to test β exact steps a reviewer can follow on a clean checkout.
- Screenshots or screen recordings for any UI change. Mandatory for visual diffs.
- Linked issue:
Closes #123/Fixes #123.
- Run the local checks yourself before review. No CI runs against PRs β
./gradlew ktlintCheck buildis your safety net. Don't expect the merge button to catch a broken build. - Work through CodeRabbit's review. Every PR gets an automated review from CodeRabbit AI. Read each comment, decide whether it's right, and either fix it or reply explaining why you're leaving it. "Looks fine to me" without engaging with the comments is not enough β a maintainer will ask you to address them before merging. CodeRabbit isn't always right; pushing back with reasoning is welcome.
- Address human review comments with follow-up commits (don't force-push during review unless the reviewer asks). Squash on merge happens automatically.
- Merge is performed by maintainers via GitHub's "Squash and merge". Your branch is deleted automatically.
- Mixed concerns (refactor + feature + dep bump in one PR).
- New dependencies without justification in the description.
- Visual changes without screenshots.
- Code that ignores the project's existing patterns ("why isn't this an
XViewModel?", "why did you skip the convention plugin?"). - Empty / vague commit messages or "WIP" commits left in history.
GitHub Store ships in 13 languages. String resources live in:
core/presentation/src/commonMain/composeResources/
values/strings.xml # Default (English)
values-ar/strings-ar.xml # Arabic
values-bn/strings-bn.xml # Bengali
values-es/strings-es.xml # Spanish
values-fr/strings-fr.xml # French
values-hi/strings-hi.xml # Hindi
values-it/strings-it.xml # Italian
values-ja/strings-ja.xml # Japanese
values-ko/strings-ko.xml # Korean
values-pl/strings-pl.xml # Polish
values-ru/strings-ru.xml # Russian
values-tr/strings-tr.xml # Turkish
values-zh-rCN/strings-zh-rCN.xml # Chinese (Simplified)
To add a translation:
- Find the missing or new key in
values/strings.xml. - Add the localized version to the matching
values-<lang>/strings-<lang>.xml. - Open a PR titled
i18n: <language> β <short note>(e.g.,i18n: Spanish β translate APK Inspect strings).
If you're adding a brand-new locale, ping a maintainer first so we can wire up the locale file and any locale-specific resources.
Don't machine-translate without verification β bad translations are worse than missing ones because they push out fallback English.
Releases are cut by maintainers from main after a short stabilization window:
- Bump
versionNameandversionCodeincomposeApp/build.gradle.kts. - Tag the commit
vX.Y.Z. - Maintainer builds Android with signing key (release) and Desktop (
.exe,.msi,.dmg,.pkg,.deb,.rpm,.appimage,.tar.zst). - Release notes are written manually β covering the user-facing changes, not every commit.
- F-Droid and the in-app updater pick up the release automatically.
You don't normally need to touch this as a contributor; just be aware that landing a feature in main doesn't immediately ship β there's a release cadence on top.
Do not open public issues for security vulnerabilities. Use GitHub's private vulnerability reporting or email hello@github-store.org with the details.
We treat token leaks, install-flow exploits, and signing-bypass paths as critical. Other issues we'll triage on a best-effort basis.
- Real-time chat: join the Discord server β fastest way to get a hand from maintainers and other contributors.
- Email: hello@github-store.org for anything that doesn't fit a public channel β sponsorship, partnerships, sensitive coordination.
- General questions / discussion: open a GitHub Discussion (if enabled) or a feature-request issue.
- Stuck on local setup: open a draft PR with what you have and ask for help in the description β we'd rather help you finish than have you give up silently.
- Sibling repos: backend and api have their own contributing notes.
Thanks for making GitHub Store better.