Contributing
Keep changes small, testable, and inside existing trust boundaries. Read repository rules before changing download, verification, signing, caching, or release behavior.
Development setup
Requirements:
- Python 3.11–3.13
- Java 21+
- current
uv - Git
git clone https://github.com/Villoh/morphe-builder
cd morphe-builder
uv sync --locked
uv run morphe-builder --helpProject layout:
| Path | Purpose |
|---|---|
src/morphe_builder/ | CLI, configuration, download, verification, build, and release code. |
tests/ | Standard-library unittest suite and static fixtures. |
docs/ | User, security, architecture, and release documentation. |
.github/workflows/ | CI, security, build, release, and PyPI automation. |
morphe.toml | Committed example and release build matrix. |
docs/plan/ contains ignored local plans and handoffs. Never commit it.
Make a change
- Read the complete flow touched by the change and every caller of shared code.
- Reuse existing helpers and standard library before adding code or dependencies.
- Keep
goopdlbehind its subprocess CLI boundary. - Add one focused regression test for non-trivial logic.
- Update documentation when behavior, configuration, output, security, or workflow changes.
- Run required checks.
Avoid speculative interfaces, provider abstractions without a second implementation, compatibility layers without an active caller, and broad refactors mixed with behavior changes.
Tests
Required before commit or pull request:
uv run python -m unittest discover -s tests
uv lock --checkCI also compiles source and tests:
uv run python -m compileall -q src testsTests use Python standard-library unittest. Add the smallest check that fails without the logic change. Live Google Play tests must remain opt-in and require MORPHE_LIVE_TEST_PACKAGE; normal CI must not need credentials or network access to Google Play.
Dependencies
Prefer Python standard library. New runtime dependencies need a concrete requirement not covered by existing code or installed packages.
goopdl is security-sensitive and must remain exactly pinned in both pyproject.toml and uv.lock. Morphe Builder invokes its public CLI through subprocess, never private Python internals.
After dependency changes:
uv lock
uv lock --checkReview generated lock changes before commit.
Security invariants
Never weaken these to make a test or provider pass:
- downloaded artifacts remain untrusted until manifest, hash, package, version, architecture, split, and signer checks pass
- original verified split APKs remain unchanged
- integrity and signer failures remain terminal
- outputs publish atomically and never overwrite existing trusted artifacts
- passwords and tokens never enter argv, logs, provenance, committed files, or shared caches
- archive path, size, entry-count, and compression limits remain enforced
- private keystores remain outside repository
Do not commit auth tokens, cookies, token caches, downloaded APKs, authenticated URLs, private keystores, or secret-bearing fixtures.
See security for threat model and explicit non-goals.
Documentation
Topic documentation belongs in docs/. Keep README.md as project overview and quick entry point rather than duplicating complete reference material.
Install and preview VitePress locally:
npm ci
npm run docs:devBuild same static site produced by CI:
npm run docs:build
npm run docs:previewWhen adding or renaming a page:
- Update documentation index.
- Update root
README.mdwhen page belongs in public documentation table. - Update
docs/.vitepress/config.mtswhen navigation changes. - Keep relative links valid on GitHub and VitePress.
- Run
npm run docs:build.
Documentation should describe current behavior. Deferred work belongs in roadmap, not reference pages.
Commits
Use Conventional Commits 1.0.0:
<type>[optional scope]: <description>Examples:
feat(download): support separate ARMv7 output
fix(verify): reject truncated split APK
docs: add architecture overviewCommon types: feat, fix, docs, test, refactor, build, ci, chore, perf, and revert.
Breaking changes use ! or a BREAKING CHANGE: footer. Keep unrelated changes in separate commits.
Pull requests
A pull request should state:
- problem and chosen minimal fix
- security or compatibility impact
- tests run
- documentation changed
- live validation performed, when relevant
Do not claim device compatibility from static verification alone. Record device, Android version, architecture, install result, and launch result for runtime validation.
