The civfix consumer plane in one repo: the @civfix/tokens design tokens, the @civfix/ui
component set, the public web app and the community mobile app. It is a pnpm workspace + Turbo
monorepo.
The API contract is not here: it is @civfix/contract, published from
civfix/civfix-contract to the private registry
(https://repo.civfix.org), and this repo installs it like every other consumer. Until 0.61.0 the
contract and the tokens shipped together as @civfix/shared from this repo.
The three former repos (civfix-shared, civfix-web, civfix-mobile) were merged into this one and
are retired. The other civfix repos (civfix-backend, civfix-admin, civfix-infra, the gov plane)
stay separate and install @civfix/contract and @civfix/tokens from the private registry.
| Path | Package | What it is |
|---|---|---|
packages/tokens |
@civfix/tokens |
The design tokens (colour scales for both schemes, category and cleanup pin colours, type, spacing, radius and shadow scales) and the chip-contrast helpers. Framework-free, no runtime deps. Published to the private registry for civfix-admin and civfix-backend. README |
packages/ui |
@civfix/ui (private) |
The shared React Native UI (theme, primitives, shells, nav store, feature bodies, the Map seam, the app-only helpers) rendered on web via react-native-web. Never published. README |
packages/testing |
@civfix/testing (private) |
Shared test support: the console guard every suite loads first, which fails a test on an unexpected console.error or console.warn. Consumed as TypeScript source; never published. README |
apps/community-web |
community-web |
Next.js 15 static-export SPA (React 19 + RN-web + shadcn) plus its Cloudflare Pages config and Functions. README |
apps/community-mobile |
community-mobile |
React Native + Expo SDK 54 user app; custom dev client only, never Expo Go. README |
pnpm 10.34.9 (pinned via packageManager), Node 22 (.nvmrc), Turbo 2. Every pnpm setting lives in
pnpm-workspace.yaml: nodeLinker: hoisted (Metro and the config plugins need a flat node_modules),
the dependency overrides, the audit ignores, the allowed install scripts and the supply-chain gates.
.npmrc keeps only the @civfix scope routed to https://repo.civfix.org/ and a guard repeat of
node-linker=hoisted (EAS builds were proven with it there; the yaml setting wins).
Root scripts are turbo-delegated across every workspace package:
pnpm install
pnpm build # @civfix/tokens -> dist (tsup), @civfix/ui -> dist-types, web -> out/
pnpm typecheck
pnpm lint
pnpm test
pnpm test:coverage # the tests with each workspace's coverage threshold, as CI runs them
pnpm i18n:check # locale keys complete across languages, and every t(...) call site in ui, web and mobile resolves
pnpm knip # unused files, exports and dependencies
pnpm jscpd # duplicated code, fails above the configured threshold
pnpm clean
pnpm run doctor:expo # expo-doctor on the mobile app (`pnpm doctor` is pnpm's own command)Per package:
pnpm --filter @civfix/tokens dev # tsup --watch on the tokens
pnpm --filter community-web dev # next dev, http://localhost:3000
pnpm --filter community-mobile start # expo start --dev-client (Metro)Recommended for local testing:
NEXT_PUBLIC_API_URL=https://api.civfix.dev pnpm --filter community-web devpnpm installpnpm buildonce (orpnpm --filter @civfix/tokens devto keep the tokens'distwatched).- Web:
pnpm --filter community-web dev. - Mobile: build the custom dev client first
(
pnpm --filter community-mobile exec expo prebuild, thenpnpm --filter community-mobile exec expo run:ios/run:android), thenpnpm --filter community-mobile start.
Editing packages/tokens or packages/ui reaches both apps directly; there is no link: override
to add and no version bump to adopt inside this repo. A contract change is different: it is a PR in
civfix-contract, a publish, then an adoption PR here
(RELEASING.md).
The umbrella's local e2e environment (../dev/run.sh in the civfix checkout) still drives the
backend, Postgres/Redis, the media worker and the seeded data for end-to-end runs.
@civfix/uiand both apps depend on@civfix/contractby caret range from the registry (^0.62.0) and on@civfix/tokensasworkspace:*; both apps take@civfix/uiasworkspace:*.@civfix/contractcomes from the registry, so web and mobile run whatever version the lockfile pins. A new contract version reaches them only through an adoption PR (range edit on a new minor, orpnpm update @civfix/contractfor an in-range patch, since a plainpnpm installleaves the lockfile unchanged).@civfix/tokensis built: consumers resolve itsexportsfromdist/(gitignored), so it must be built before anything typechecks against it.turbo's^builddependency handles that forbuild/typecheck/lint/test/dev; the mobile app also has apostinstallthat runspnpm --filter @civfix/tokens build, becauseapp.config.jsrequires@civfix/tokensand EAS builders need itsdisttoo.@civfix/uiships untranspiled.tsxsource through its exports map (Metro and Next transpile it; the.web.tsx/.native.tsxseams must stay unbundled) and emits onlydist-types/fortsc. Turbo runs that build before any typecheck/lint/test, so there is no manual rebuild step.
main is staging. Production is a v* release. There is no dev branch: every feature branch PRs
into main, and a merge must be production-ready, but it lands on staging first and reaches
production only when a release is cut. PRs into main run .github/workflows/ci.yml (three jobs:
packages, web, mobile).
| Event | What happens |
|---|---|
push to main |
deploy-web.yml builds the web static export against api.civfix.dev and publishes it to the staging branch of the Cloudflare Pages project civfix-web (alias staging.civfix-web.pages.dev, served as https://civfix.dev). publish-tokens.yml publishes @civfix/tokens if its version is ahead of the registry. |
published v* release |
deploy-web.yml rebuilds the same commit against api.civfix.org with Turnstile on, and publishes it to the Pages production branch (https://civfix.org). |
A static export inlines every NEXT_PUBLIC_* at build time, so production is a rebuild of the release
commit rather than a byte-copy of the staging artifact, unlike the backend, whose Docker images really
are promoted as-is. The workflow resolves those values once and then asserts they match the target, so a
build cannot ship the staging API URL to civfix.org or the production one to civfix.dev.
The mobile app deploys through .github/workflows/deploy-mobile.yml on the same lane: a push to
main that touches the app or the packages builds the testflight profile (staging API) on a
GitHub-hosted Mac and uploads it to App Store Connect, where TestFlight hands it to the internal
testers; a manual run with profile=production uploads a build that bakes no API URL at all and
picks one at runtime from its iOS install source (api.civfix.dev while it is handed out through
TestFlight, api.civfix.org once it is downloaded from the App Store), and attaching that build to
a version and submitting it for review stays a human step in App Store Connect. The runner executes
the same scripts/store-build.sh a developer runs locally (eas build --local, then a direct
fastlane pilot upload to App Store Connect, with no EAS Submit queue), so CI and laptop builds share
one EAS signing-credential store and one build-number counter. See that app's README for the
prerequisites (an EXPO_TOKEN secret, the App Store Connect API key secrets, EAS credentials, the
remote build number).
Android still ships only by hand.
@civfix/ui and both apps are private and are never published. @civfix/tokens is, because
civfix-admin and civfix-backend (the email layout) install it from repo.civfix.org.
Record a changeset while developing (pnpm changeset), apply it before merging
(pnpm changeset version), and merging to main publishes it. Full runbook, including how each
external consumer adopts the new version and how this repo adopts a new @civfix/contract:
RELEASING.md. The contract's own release flow is RELEASING.md in
civfix-contract.
civfix-app is free software, licensed under the
GNU Affero General Public License, version 3 only. The apps built
from this repository are additionally conveyed under the
App Store and Play distribution exception, because the
store binaries bundle packages/tokens and packages/ui. @civfix/contract, which the store
binaries also bundle, carries the same exception in its own repository. Every file is
covered by the declaration in REUSE.toml; there are no per-file
license headers. Contributions are accepted under the
Contributor License Agreement; see CONTRIBUTING.md.
civfix is a project of Reach Out Los Angeles Inc.; the civfix name and logos are its
trademarks and are not covered by the license.