{"id":46292420,"url":"https://github.com/gfauredev/logout","last_synced_at":"2026-04-06T02:10:58.390Z","repository":{"id":338958444,"uuid":"1159863170","full_name":"gfauredev/LogOut","owner":"gfauredev","description":"Turn off your PC, log your workout (simple, efficient, cross-platform workout logging app)","archived":false,"fork":false,"pushed_at":"2026-04-02T21:14:47.000Z","size":1609,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T04:45:11.712Z","etag":null,"topics":["android","dioxus","gym","health","pwa","rust","sports","training","workout","workout-logging"],"latest_commit_sha":null,"homepage":"https://gfauredev.github.io/LogOut/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gfauredev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-17T08:57:44.000Z","updated_at":"2026-04-02T21:07:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"00ec10a6-22ca-4127-88fb-60eaf8c52a64","html_url":"https://github.com/gfauredev/LogOut","commit_stats":null,"previous_names":["gfauredev/logout"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/gfauredev/LogOut","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfauredev%2FLogOut","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfauredev%2FLogOut/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfauredev%2FLogOut/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfauredev%2FLogOut/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gfauredev","download_url":"https://codeload.github.com/gfauredev/LogOut/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gfauredev%2FLogOut/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31368156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","dioxus","gym","health","pwa","rust","sports","training","workout","workout-logging"],"created_at":"2026-03-04T08:33:27.811Z","updated_at":"2026-04-03T18:01:11.648Z","avatar_url":"https://github.com/gfauredev.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- LTeX: language=en-GB --\u003e\n\n\u003cdiv align=center\u003e\n\n[![Get it on Obtainium](https://img.shields.io/github/v/release/gfauredev/LogOut?sort=semver\u0026logo=obtainium\u0026label=get%20and%20update%20on%20Obtainium)](https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/gfauredev/LogOut)\n[![GitHub Release Date](https://img.shields.io/github/release-date/gfauredev/LogOut?logo=github\u0026label=released%20on%20GitHub)](https://github.com/gfauredev/LogOut/releases/latest)\n[![GitHub Pre-release Date](https://img.shields.io/github/release-date-pre/gfauredev/LogOut?logo=github\u0026label=pre-released%20on%20GitHub)](https://github.com/gfauredev/LogOut/releases)\n[![Garnix CI status badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2Fgfauredev%2FLogOut)](https://garnix.io/repo/gfauredev/LogOut)\n[![Deps.rs Repository Dependencies](https://img.shields.io/deps-rs/repo/github/gfauredev/LogOut?logo=rust\u0026labelColor=black)](https://github.com/gfauredev/LogOut/blob/main/Cargo.toml#L12)\n\n\u003c/div\u003e\n\n\u003c!-- https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://add/https://github.com/gfauredev/LogOut --\u003e\n\u003c!-- https://github.com/gfauredev/LogOut/releases/latest --\u003e\n\u003c!-- https://garnix.io/api/badges/gfauredev/LogOut --\u003e\n\n# LogOut\n\n\u003c!--toc:start--\u003e\n\n- [Project Structure](#project-structure)\n- [Tooling and Dependencies](#tooling-and-dependencies)\n- [Building and Running](#building-and-running)\n  - [Android Native APK](#android-native-apk)\n- [Engineering Principles and Contributing](#engineering-principles-and-contributing)\n- [Continuous Integration and Deployment (CI/CD)](#continuous-integration-and-deployment-cicd)\n  - [Heavier Checks](#heavier-checks)\n- [Licensing and Credits](#licensing-and-credits)\n\n\u003c!--toc:end--\u003e\n\n\u003e Close your laptop, *l*og your work*o*ut\n\nA simple, efficient and cross-platform workout logging application with\n[800+ exercises] built-in, by [Guilhem Fauré].\n\n- 💪 Easily log workout sessions with sets, reps, weights, distances, durations\n- 🏋️ Use the 870+ included exercises with images and instructions\n  - 📝 Easily add your custom exercises or customize existing ones\n- 🔍 Easily search them with powerful text search and attribute based filtering\n- 📊 Track your progress over time on several metrics and exercises in analytics\n- 📱 Responsive design, ergonomic navigation, local-first, performant\n\n\u003cp float=\"left\"\u003e\n  \u003cimg src=\".screenshot/search.png\" width=\"32%\" alt=\"Screenshot of LogOut exerices list page, with search terms entered\"\u003e\n  \u003cimg src=\".screenshot/home.png\" width=\"32%\" alt=\"Screenshot of LogOut home page, showing completed past sessions\"\u003e\n  \u003cimg src=\".screenshot/analytics.png\" width=\"32%\" alt=\"Screenshot of LogOut analytics page, showing evolution of Pull-Up weight and reps\"\u003e\n\u003c/p\u003e\n\n## Project Structure\n\nThe project follows a modular [Rust] structure for a [Dioxus] application:\n\n```text\nLogOut/\n├ Cargo.toml    Rust manifest (dependencies, features, targets)\n├ Dioxus.toml   Configuration for Dioxus CLI (build, serve, platform options)\n├ STORIES.md    User stories, serve as a basis for end-to-end tests\n├ android/      Android native app static assets and configuration files\n├ assets/       Application-wide static assets\n├ flake.nix     Nix flake: reproducible development environment, builds, checks\n├ maestro/      Maestro end-to-end tests (order-independent, self-contained)\n│ ├ android/    Android native app tests\n│ └ web/        Web browser PWA tests\n├ public/       PWA static assets required by the browser\n└ src/\n  ├ main.rs     App entry point, routing (Dioxus Router), global state \n  ├ utils.rs    Pure, side-effect-free utility functions (format, timestamps…)\n  ├ models/     Data models (Exercise, WorkoutSession, Enums), unit-safe types\n  ├ services/   Business logic and persistence layers\n  └ components/ Functional Dioxus UI components\n```\n\n## Tooling and Dependencies\n\n| Purpose            | Methodology            |\n| ------------------ | ---------------------- |\n| Project versioning | [SemVer]               |\n| Commit messages    | [Conventional Commits] |\n| Branch naming      | [Conventional Branch]  |\n| Branching model    | [GitHub Flow]          |\n| Changes submission | [GitHub Pull Requests] |\n| Issue tracking     | [GitHub Issues]        |\n\n| Purpose                      | Tool                                         |\n| ---------------------------- | -------------------------------------------- |\n| Rust compilation             | [rustc]                                      |\n| Build system                 | [Cargo]                                      |\n| Dependencies and environment | [Nix]                                        |\n| Versioning and collaboration | [Git] hosted on GitHub                       |\n| Unit tests                   | [Cargo test], [cargo-llvm-cov], [nextest]    |\n| End-to-end tests (PWA)       | [Maestro] (beta web)                         |\n| End-to-end tests (Android)   | [Maestro]                                    |\n| Code coverage                | [cargo-llvm-cov]                             |\n| Rust language assistance     | [rust-analyzer] (LSP)                        |\n| Documentation from code      | [rustdoc]                                    |\n| Rust formatting              | [rustfmt] and [dx] fmt                       |\n| Rust quality control         | [Clippy]                                     |\n| Rust debugging               | [lldb]                                       |\n| Code edition                 | Allows modern Rust dev ([Helix], [VS Code]…) |\n\n| Purpose                                                 | Library    |\n| ------------------------------------------------------- | ---------- |\n| Main UI reactive framework                              | [Dioxus]   |\n| (De)Serialization, data models and persistence          | [Serde]    |\n| PWA Workouts and custom exercises storage ([IndexedDB]) | [Rexie]    |\n| Native Workouts and custom exercises storage ([SQLite]) | [Rusqlite] |\n| Asynchronous HTTP client                                | [Reqwest]  |\n| Date and time manipulation (UTC/Local offsets)          | [Time]     |\n| Async runtime for the native application target.        | [Tokio]    |\n| Bindings to browser APIs (Service Worker…)              | [Web-sys]  |\n\n## Building and Running\n\nThe project uses [Nix] to download all (proper versions of) required\ndependencies, configure the development environment (shell) and build the\napplication, reproducibly. The [Nix] environment and tooling is defined in\n[`flake.nix`], enable it with `nix develop` or automatically with an allowed\n[`.envrc`](.envrc) (`direnv allow`) via [`direnv`] (recommended).\n\nFor release builds, we prefer pure reproducible `nix build`, but for development\nspeed, it is recommended to use the hot-reloading `dx serve` ([PWA] by default).\n\n### Android Native APK\n\nWe currently don’t support pure `nix build` for Android. To build the native\nAndroid APK, run the following from an activated development shell:\n\n```sh\ndx build --android --release --target aarch64-linux-android # Or desired arch\n```\n\n\u003e APK is signed with [`apk-sign.sh`](.script/apk-sign.sh) after the build, to\n\u003e keep it reproducible and because Dioxus requires secrets in clear in VCS\n\n## Engineering Principles and Contributing\n\nSometimes, we need to make tradeoffs between different positives outcomes.\nLogOut follows that priority order:\n\n1. User Experience\n   1. Maximize data integrity, never lose or corrupt user data\n      - ⚠️ WARNING Ignored until v1.0.0, expect breaking data model changes\n   2. Maximize correctness and stability, work as the user expects, reliably\n   3. Maximize extensibility, easily give users features they need\n   4. Minimize computational complexity, be snappy, pleasant to use\n2. Developer Experience\n   1. Maximize code readability and maintainability, make it easy to understand\n   2. Maximize simplicity, minimize complexity, avoid nesting, over-engineering\n   3. Maximize testability and iteration speed, isolated units, fast compile\n3. Resource Efficiency (also important for UX)\n   1. Minimize battery usage, don’t kill mobile devices\n   2. Minimize memory footprint, run smoothly on low-end devices\n   3. Minimize binary and stored data size, be unnoticed, load quickly\n   4. Minimize network usage, work offline, don’t waste users’ data plans\n\nThat doesn’t mean lower order items are not important, this list is just for\nwhen tradeoffs are strictly necessary. If possible, maximize all outcomes.\nFollow these general engineering principles:\n\n- **Decouple core logic** from **platform specificities** (storage, OS\n  integrations, UI frameworks, network…)\n  - Abstract these boundaries behind traits or interfaces to keep the\n    application testable and portable\n- Enforce a **Single Source of Truth**, derive component or local state directly\n  from a centralized global state to prevent desynchronization\n  - Confine all state mutations to atomic, centralized functions\n- Bind external resources to **strict lifecycles** (RAII), guarantee their\n  clean-up by tying their lifecycles directly to object scope\n  - Event listeners, database transactions, browser object URLs…\n- **Never block main/UI thread**, strictly offload synchronous, I/O-heavy, or\n  CPU-bound operations to background threads\n  - Ensure state is passed safely across async boundaries, and use cancellable\n    primitives instead of hanging tasks\n- **Lazily load and evaluate**, optimize memory, assume datasets will grow large\n  - Defer loading heavy data, historical records, and binary assets until the\n    exact moment they are needed\n  - Use reference counting (`Rc`/`Arc`) for heavy objects in memory to avoid\n    expensive deep copies\n- **Gracefully fail** (especially at system boundaries), anticipate failures\n  whenever interacting with the network, file system, foreign functions (FFI)…\n  - Handle errors explicitly without crashing the app, surface them gracefully\n    to the user via managed queues, and never swallow them silently\n  - Avoid methods/macros such as `.unwrap()`, `.expect()`, `.borrow()` variants,\n    direct `vec[i]`, `panic!()`… unless mathematically guaranteed to never fail\n- **Avoid or optimize I/O**, disk reads and network requests are expensive\n  - Cache assets, strictly normalize data, optimize database queries…\n- **Don’t reinvent the wheel**, use battle-tested abstractions, standardized,\n  widely adopted crates/libraries (URL encoding, timezone parsing, unit…)\n- **Explicit over implicit**, for developer experience, cache management…\n- Avoid \"**magic**\" hardcoded values, use clearly named constants\n  - Except where it really makes sense, like usually 0, 1, 100%…\n- Properly **document** what you do (functions, structs… with `rustdoc`)\n- Avoid nesting, avoid complexity; generally, avoid things with only one child\n- **Minimize** number of **classes**, keep the necessary one highly **semantic**\n  - `structure.scss` semantic nodes among the semantic hierarchical structure\n  - `component.scss` self-contained individual reusable components\n  - `unique.scss` self-contained unique non-reusable components\n  - `$page.scss` structure and components specific to `$page`\n- Ensure code is properly **formatted** with `dx fmt` and `cargo fmt --all`\n- Ensure code **compiles** with `dx build` plus eventual platform flags\n- Ensure all **unit tests** `cargo llvm-cov nextest` pass without warning\n- **No** `cargo clippy -- -D warnings -W clippy::all -W clippy::pedantic` warns\n- Ensure all **end-to-end tests** `maestro test` (`--headless`) pass\n\nFollow this contribution process, based on [GitHub Flow], [Conventional Branch]:\n\n1. Before writing any code, **open an issue** to discuss it with the maintainer\n2. Create a **branch** for your change with a clear [Conventional Branch] name:\n   - `feat/my-new-feature`\n   - `fix/my-bug-fix`\n   - `refactor/my-consequent-refactor`\n   - …\n3. Open a **Pull Request (PR)** as soon as your code compiles and checks\n   - Avoid touching things not strictly related to your desired changes, e.g.\n     updating dependencies\n4. Fulfil the **PR** template checks before marking it ready for review\n5. Fix your code if it don’t pass [CI checks](#continuous-integration-ci)\n\n## Continuous Integration and Deployment (CI/CD)\n\n[LogOut] keep high standards of code quality and reliability. Every change must\npass through a pull-request (PR), and every below check (that runs on pushes on\nPRs) must pass (for some, at a certain level) for it to be merged into `main`.\n\n- Run isolated in Garnix via [`flake.nix`], for every push on PR\n  - Check if the code is properly **formatted** `cargo fmt --all -- --check`\n  - **Lint** `cargo clippy -- -D warnings -W clippy::all -W clippy::pedantic`\n  - **Unit test** while measuring **coverage** with `cargo llvm-cov nextest`\n  - **Build production** release for Web `dx build --web --release`\n  - At each step, cache outputs to avoid redundant work (automatic in Garnix)\n- Run in standard Linux or macOS runners once necessary outputs are available\n  - Check that more than `80%` of code (excluding `components`) is covered,\n    publish the full coverage summary table as a PR comment\n  - Slower checks, only if above pass _and_ branch is up-to-date with `main`\n    - **PageSpeed** Lighthouse audit on PWA, publish report as a PR comment\n    - Web Maestro **end-to-end tests** with `maestro test maestro/web`\n    - Publish a report with screenshots of failed E2E tests as a PR comment\n\n[LogOut] stays continuously fresh and up-to-date thanks to its automated\ndeployment pipeline running at every push on `main` branch (coming only from\nvalidated PRs), on standard Linux runners.\n\n- Deploy the _production_ **Progressive Web App** to GitHub Pages\n- **Build production** release for Android `dx build --android --release`\n  - Deploy **Android APK** in a “Rolling” timestamped GitHub (pre-)Release\n  - Sign it with GitHub secrets and `scripts/apk-sign.sh`\n  - Only if the last release is from the previous (UTC) day, to avoid spamming\n  - Remove the previous “Rolling” pre-releases older than a week\n\nCD also runs when a [SemVer] `vMAJOR.MINOR.PATCH` **tag** is pushed, publishing\na “Stable” GitHub Release with a production Android APK built on this `tag`.\n\nAdditionally, [renovate] GitHub app is used to monitor the freshness of\nproject’s dependencies.\n\n### Heavier Checks\n\n[LogOut] ensures high quality code with additional resource intensive checks\nthat can be triggered manually to run on the `main` branch.\n\n- Run Android **end-to-end tests** in emulator `maestro test maestro/android`\n- Analyse dependencies for vulnerabilities or deprecations with `cargo deny`\n- Test the tests’ comprehensiveness by introducing bugs they should catch\n  - **Mutation testing** with `cargo-mutants`\n- Publish report(s) of the above checks accessible via the forge\n\n## Licensing and Credits\n\n[LogOut] is licensed under the GPL-3.0, see [LICENSE](LICENSE) for details.\n\nThe [exercise database] is under the Unlicense (public domain).\n\n[LogOut]: https://gfauredev.github.io/LogOut\n[800+ exercises]: https://gfauredev.github.io/free-exercise-db\n[old 800+ exercises]: https://github.com/yuhonas/free-exercise-db\n[Cargo]: https://doc.rust-lang.org/cargo\n[cargo test]: https://doc.rust-lang.org/cargo/commands/cargo-test.html\n[cargo-llvm-cov]: https://github.com/taiki-e/cargo-llvm-cov\n[Clippy]: https://github.com/rust-lang/rust-clippy\n[Conventional Commits]: https://www.conventionalcommits.org\n[Conventional Branch]: https://conventional-branch.github.io\n[Dioxus]: https://dioxuslabs.com\n[dx]: https://dioxuslabs.com\n[direnv]: https://direnv.net\n[`direnv`]: https://direnv.net\n[exercise database]: https://gfauredev.github.io/free-exercise-db\n[exercise db]: https://gfauredev.github.io/free-exercise-db\n[`flake.nix`]: flake.nix\n[free-exercise-db]: https://gfauredev.github.io/free-exercise-db\n[old free-exercise-db]: https://github.com/yuhonas/free-exercise-db\n[Guilhem Fauré]: https://www.guilhemfau.re\n[Git]: https://git-scm.com\n[GitHub Pull Requests]: https://github.com/gfauredev/LogOut/pulls\n[GitHub Issues]: https://github.com/gfauredev/LogOut/issues\n[GitHub Flow]: https://githubflow.github.io\n[Helix]: https://helix-editor.com\n[lcov]: https://github.com/linux-test-project/lcov\n[lldb]: https://lldb.llvm.org\n[llvm-cov]: https://llvm.org/docs/CommandGuide/llvm-cov.html\n[Maestro]: https://maestro.dev\n[nextest]: https://nexte.st\n[Nix]: https://nixos.org\n[pwa]: https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps\n[renovate]: https://www.mend.io/renovate\n[rust-analyzer]: https://rust-analyzer.github.io\n[rust]: https://www.rust-lang.org\n[rustc]: https://doc.rust-lang.org/rustc\n[rustdoc]: https://doc.rust-lang.org/rustdoc\n[rustfmt]: https://github.com/rust-lang/rustfmt\n[VS Code]: https://code.visualstudio.com\n[SemVer]: https://semver.org\n[Serde]: https://serde.rs\n[IndexedDB]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API\n[Rexie]: https://github.com/wasmerio/rexie\n[SQLite]: https://www.sqlite.org/index.html\n[Rusqlite]: https://github.com/rusqlite/rusqlite\n[Reqwest]: https://github.com/seanmonstar/reqwest\n[Time]: https://github.com/time-rs/time\n[Tokio]: https://tokio.rs\n[Web-sys]: https://rustwasm.github.io/wasm-bindgen/web-sys/index.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgfauredev%2Flogout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgfauredev%2Flogout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgfauredev%2Flogout/lists"}