Some checks failed
Build & Release APK / build (push) Failing after 55m38s
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WontRuPqKcod9XK4ZN9MXv
90 lines
4.0 KiB
Markdown
90 lines
4.0 KiB
Markdown
# Kegel — "Him: Kegel Exercises for Men"
|
||
|
||
Free, open-source pelvic floor trainer. A local-only take on what Stamina was
|
||
before it went paid: no account, no ads, no analytics, no network. Expo /
|
||
React Native, Android + iOS.
|
||
|
||
- **App Store**: https://apps.apple.com/app/id6809350427 (once approved)
|
||
- **Android APK**: releases on this repo, installable via Obtainium
|
||
- **Google Play**: coming
|
||
- **Privacy policy**: https://rehbock.xyz/kegel/privacy.html
|
||
- **License**: MIT — see `LICENSE`. Fork it, ship your own, no strings.
|
||
|
||
## How it works
|
||
|
||
A session is four guided blocks driven by a step timer with haptic cues:
|
||
|
||
1. **Holds** — 8 × squeeze-and-hold. This is the progressive-overload driver:
|
||
holds start at 5s and grow **+1s every 3 completed sessions** (capped at
|
||
30s). Manual ±1s adjustment in Settings.
|
||
2. **Pushes** — 5 × reverse kegel (gently bear down). Scales at half the rate
|
||
of holds, capped at 12s.
|
||
3. **Quick squeezes** — 10 × 1s burst contractions with 1s release.
|
||
4. **Quick pushes** — 10 × 1s burst pushes with 1s release.
|
||
|
||
Progress (session count, difficulty adjustment, completion history) lives in
|
||
AsyncStorage — no server, no account. The home screen shows your **day streak**
|
||
(alive until a full day is missed), best streak, and a two-week activity strip.
|
||
|
||
## Layout
|
||
|
||
- `src/app/` — expo-router screens: `index` (home), `session` (timer), `settings`
|
||
- `src/lib/workout.ts` — pure session-plan + progression logic
|
||
- `src/lib/streak.ts` — pure streak / calendar-day logic (tested)
|
||
- `src/lib/store.ts` — tiny local store persisted to AsyncStorage
|
||
- `android/` — committed prebuild output; CI builds it directly
|
||
|
||
## Development
|
||
|
||
```sh
|
||
npm install
|
||
npm start # Expo dev server
|
||
npm run typecheck
|
||
npm test # node --test over src/lib/*.test.ts
|
||
```
|
||
|
||
## Contributing
|
||
|
||
Issues and PRs welcome. Keep the app offline-only and dependency-light; the
|
||
whole point is that it never phones home. Run `npm run typecheck && npm test`
|
||
before opening a PR.
|
||
|
||
## Release
|
||
|
||
### Android APK (Gitea / Obtainium)
|
||
|
||
Push to `main` → Gitea Actions builds a signed APK and publishes a Gitea
|
||
release — install via Obtainium. `android/app/build.gradle` keeps literal
|
||
`versionCode`/`versionName`; CI stamps the run number in with `sed` before
|
||
building. Keystore: `~/.android-keys/kegel-release.jks` (repo secrets
|
||
`KEYSTORE_B64`, `KEYSTORE_PASSWORD`; alias `kegel`). Beware: `expo prebuild -p android`
|
||
(used to regenerate launcher icons from `store/icons/make.js`) rewrites
|
||
`build.gradle` and drops the release `signingConfigs` block — restore it from git.
|
||
|
||
### App Store / Google Play (EAS)
|
||
|
||
Only the maintainer can publish to the stores; `eas.json` references an App
|
||
Store Connect API key by key ID / issuer ID, which are identifiers, not
|
||
secrets — the `.p8` private key is never committed. Store builds run on EAS (project `@mrmen23/kegel`, profile `production`,
|
||
version source remote with auto-increment). The EAS-managed Android keystore
|
||
differs from the Gitea one, so Play and Obtainium installs can't update over
|
||
each other.
|
||
|
||
```sh
|
||
# Apple: needs an App Store Connect API key for non-interactive auth
|
||
export EXPO_ASC_API_KEY_PATH=~/Downloads/AuthKey_L9Z6F5H5B7.p8 EXPO_ASC_KEY_ID=L9Z6F5H5B7 \
|
||
EXPO_ASC_ISSUER_ID=<issuer uuid> EXPO_APPLE_TEAM_ID=HZ6XCKM95R EXPO_APPLE_TEAM_TYPE=INDIVIDUAL
|
||
eas build -p ios --profile production --non-interactive
|
||
eas submit -p ios --profile production --latest --non-interactive # creates the ASC record on first run
|
||
|
||
# Android: needs a Play service-account JSON with release access
|
||
eas build -p android --profile production --non-interactive
|
||
eas submit -p android --profile production --latest --key <service-account.json> --track internal
|
||
```
|
||
|
||
Listing copy, keyword rationale and screenshot generation live in `store/`
|
||
(`node store/screenshots/make.js` renders `store/screenshots/raw/*.png` into
|
||
store-sized slides). Privacy policy is served from
|
||
https://rehbock.xyz/kegel/privacy.html (source in `store/web/`). iOS dev runs
|
||
via `npx expo run:ios` from a Mac.
|