Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .eas/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Preview build (internal, TEST distribution)

# TEST distribution is EAS internal distribution, not a store — see docs/deploy.md. Triggers on
# `main` only; the repo's default branch should be `main` (see checklist in the deploy plan).
on:
push:
branches: ['main']

jobs:
build_android_preview:
name: Build Android (preview)
type: build
params:
platform: android
profile: preview

build_ios_preview:
name: Build iOS (preview)
type: build
params:
platform: ios
profile: preview
76 changes: 76 additions & 0 deletions .eas/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Release (production build + store submit)

# Triggered by pushing a `vX.Y.Z` tag (ChatbotX's own release) or manually for a white-label
# customer build via `eas workflow:run .eas/workflows/release.yml -F brand=<customer-id>`. A
# non-default `brand` is expected to live in the private `chatbotx-mobile-brands` repo — see
# docs/white-label.md — cloned into `brands/` before the build so the customer's brand.json/assets
# are never committed to this (public) repo.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
brand:
type: string
description: 'Brand id (brands/<id>/) to build and submit'
default: chatbotx

jobs:
fetch_brand:
name: Fetch customer brand overlay
type: custom
if: ${{ inputs.brand != '' && inputs.brand != 'chatbotx' }}
steps:
- uses: eas/checkout
- name: Clone private brand overlay repo
run: |
set -euo pipefail
: "${BRANDS_REPO_TOKEN:?BRANDS_REPO_TOKEN secret is not set}"
rm -rf "brands/${{ inputs.brand }}" /tmp/chatbotx-mobile-brands
git clone --depth 1 \
"https://x-access-token:${BRANDS_REPO_TOKEN}@github.com/ChatbotXIO/chatbotx-mobile-brands.git" \
/tmp/chatbotx-mobile-brands
test -f "/tmp/chatbotx-mobile-brands/${{ inputs.brand }}/brand.json" \
|| { echo "No brand.json for brand '${{ inputs.brand }}' in chatbotx-mobile-brands"; exit 1; }
cp -R "/tmp/chatbotx-mobile-brands/${{ inputs.brand }}" "brands/${{ inputs.brand }}"

build_android_production:
name: Build Android (production)
type: build
# `after` (not `needs`) — `needs` requires the dependency to *succeed*, which would block this
# job whenever `fetch_brand` is skipped (the default chatbotx brand, nothing to clone). `after`
# only waits for it to complete, success or skip, so the `if` below is the only real gate.
after: [fetch_brand]
if: ${{ always() && (after.fetch_brand.status == 'success' || after.fetch_brand.status == 'skipped') }}
env:
BRAND: ${{ inputs.brand || 'chatbotx' }}
params:
platform: android
profile: production

build_ios_production:
name: Build iOS (production)
type: build
after: [fetch_brand]
if: ${{ always() && (after.fetch_brand.status == 'success' || after.fetch_brand.status == 'skipped') }}
env:
BRAND: ${{ inputs.brand || 'chatbotx' }}
params:
platform: ios
profile: production

submit_android_production:
name: Submit to Play Store
type: submit
needs: [build_android_production]
params:
build_id: ${{ needs.build_android_production.outputs.build_id }}
profile: production

submit_ios_production:
name: Submit to App Store
type: submit
needs: [build_ios_production]
params:
build_id: ${{ needs.build_ios_production.outputs.build_id }}
profile: production
27 changes: 27 additions & 0 deletions .eas/workflows/update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: OTA update

# Automatic: every push to main publishes a JS-only OTA update to the `preview` (TEST) channel —
# the common case when no native config changed. Manual: publish to either channel on demand via
# `eas workflow:run .eas/workflows/update.yml -F channel=production`. See docs/deploy.md for the
# `runtimeVersion: appVersion` implication (a native change requires a new build, not just an OTA
# update, or the update won't be receivable by builds on a different `version`).
on:
push:
branches: ['main']
workflow_dispatch:
inputs:
channel:
type: choice
description: 'Which channel to publish the OTA update to'
options:
- preview
- production
default: preview

jobs:
publish_update:
name: Publish OTA update
type: update
environment: ${{ inputs.channel || 'preview' }}
params:
channel: ${{ inputs.channel || 'preview' }}
2 changes: 2 additions & 0 deletions .easignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# CodeGraph local index (contains a live daemon socket file that cannot be archived)
.codegraph
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ API_BASE_URL=http://localhost:3123

# PartyKit realtime server URL. Defaults to the local `partykit dev` port when unset.
WS_URL=http://localhost:1999

# Selects brands/<BRAND>/brand.json — app id, name, colors, icons, EAS project. `chatbotx` is the
# default brand tracked in this repo; see docs/white-label.md to add your own.
BRAND=chatbotx

# development | preview | production — suffixes the bundle id/app name and picks the EAS Update
# channel so dev/test/prod builds can coexist on one device. See docs/deploy.md.
APP_ENV=development
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7

- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v6

- uses: actions/setup-node@v4
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
Expand All @@ -35,3 +35,9 @@ jobs:

- name: Expo config/dependency doctor
run: npx expo-doctor

- name: Verify brand mechanism (chatbotx)
run: BRAND=chatbotx pnpm prebuild:check

- name: Verify brand mechanism (_template)
run: BRAND=_template pnpm prebuild:check
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ yarn-error.*

# local env files
.env
.env*.local
.env.*
!.env.example

# brand overlays (white-label customer brands live outside this repo — see docs/white-label.md)
brands/*
!brands/chatbotx/
!brands/_template/

# typescript
*.tsbuildinfo
Expand Down
55 changes: 55 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Contributing

Thanks for considering a contribution to ChatbotX's mobile app.

## Getting set up

```bash
pnpm install
cp .env.example .env
pnpm start
```

See the [README](README.md) for the full quick start and available scripts.

## Before opening a PR

```bash
pnpm typecheck
pnpm lint
pnpm format:check
pnpm test
npx expo-doctor
```

CI runs all of these on every push/PR — please run them locally first so review cycles aren't
spent on failures these would have caught.

## Code style

- TypeScript, strict mode. Avoid `any`; narrow `unknown` instead.
- Match existing patterns in the file/feature you're touching before introducing a new one.
- Keep PRs focused — unrelated refactors make review harder and should be a separate PR.

## Commit messages

Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `chore:`, `perf:`, `ci:`.

## Reporting bugs / requesting features

Open a GitHub issue with:

- What you expected vs. what happened
- Steps to reproduce (for bugs)
- Your environment (OS, Expo Go vs. dev build, physical device vs. simulator)

## Security issues

Please do not open a public issue for a security vulnerability. See the repository's security
policy (or contact the maintainers directly) instead.

## Scope note

Contributions to the core app (chat, contacts, auth, etc.) are welcome. White-label/brand-specific
work (see [docs/white-label.md](docs/white-label.md)) is maintained by the ChatbotX team for
customer builds and isn't something this repo's contribution flow covers.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 ChatbotX

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ pnpm start
```

Then follow the Expo CLI prompts to open the app in a development build, Android emulator, iOS
simulator, or Expo Go.
simulator, or Expo Go. `.env`'s `BRAND=chatbotx` and `APP_ENV=development` defaults are enough to
run the app with zero EAS setup — see [Deploying](#deploying) once you need a real build.

## Development Commands

Expand All @@ -48,12 +49,25 @@ pnpm test # jest
pnpm format # prettier --write .
pnpm format:check # prettier --check .
pnpm generate:api # regenerate the typed API client from a running backend instance
pnpm brand:new <id> # scaffold a new brand from brands/_template
```

## Deploying

TEST builds are EAS internal distribution; production builds go through EAS Build/Submit to the
App Store and Play Store. See [docs/deploy.md](docs/deploy.md) for the full flow.

## White-label

This app can be built for a different brand (app name, icons, colors, bundle id, EAS project) from
the same codebase — see [docs/white-label.md](docs/white-label.md).

## Docs

- [Project structure](docs/project-structure.md)
- [Regenerating the API client](docs/api-client.md)
- [Realtime event types](docs/realtime-events.md)
- [Android push notifications](docs/push-notifications.md)
- [EAS Update](docs/eas-updates.md)
- [Deploying (TEST and production)](docs/deploy.md)
- [White-label](docs/white-label.md)
Loading