Add CodeRabbit review, split CI into per-app workflows - #255
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdded CodeRabbit review settings, separate Android, API, and web workflows, and more stable web end-to-end tests. The previous combined build workflow was removed. ChangesRepository automation
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/build-and-test.yaml:
- Around line 10-20: Pin every uses: reference in the workflow, including those
at the specified lines, to its full commit SHA while preserving the
corresponding release tag in a comment. Separate the web/API pull-request jobs
from secret-backed execution, and gate the Android job that writes
GOOGLE_SERVICES_JSON_DEV and GOOGLE_SERVICES_JSON_PROD and runs Gradle so it
executes only for trusted same-repository pull requests, not fork-originated
code.
- Around line 128-133: Restrict the secret-backed Android job condition to
trusted non-pull-request branch events, or otherwise route all pull requests to
a secret-free job; do not allow same-repository pull requests to execute the
workflow that writes GOOGLE_SERVICES_JSON_DEV or GOOGLE_SERVICES_JSON_PROD
before running Gradle code. Also replace all eight uses: references in this
workflow with their corresponding full commit SHAs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 13221f34-06ff-4f80-ade4-167ff059a247
📒 Files selected for processing (1)
.github/workflows/build-and-test.yaml
textbee is public, so it qualifies for CodeRabbit's Open Source plan (the Pro+ feature set at no cost). Reviews are comment-only on the chill profile, so nothing gates a merge. Two settings are load-bearing and easy to get wrong: base_branches always includes the default branch and cannot exclude it, so main is reviewed implicitly and dev has to be listed explicitly. Without it, every feature PR would go unreviewed since they all target dev. The skip-ai-review label is the only lever left for skipping the dev to main roll-ups, which would otherwise re-review code already seen. CodeRabbit auto-reads CLAUDE.md from the repo root as review guidelines, but this repo's CLAUDE.md sits one directory above the git root and .cursor/rules is not on its auto-read list. The no-em-dash and pnpm-only rules are therefore encoded directly in tone_instructions and a global path instruction. Validated against schema.v2.json. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The workflow fired on push only, so pull requests carried no checks of their own. CodeRabbit's github-checks tool waits on GitHub Checks and comments on failures, which gave it nothing to read, and outside contributors got no CI signal before merge. It also ran everything in one workflow, with web and api sharing a single job. On the last green run that job was 5m23s, of which the e2e suite was 2m52s while already carrying continue-on-error, so it dominated wall clock while gating nothing. An api-only change paid for all of it plus both android builds, because a workflow-level paths filter is all or nothing. Now api, web and android each have their own workflow and their own paths filter, which needs no third-party change-detection action, and web e2e is its own job so the blocking signal lands in about 90 seconds. Also folded in, since the files were being rewritten anyway: - permissions: contents: read. Nothing was declared, so the token got default write scope on every run. - concurrency with cancel-in-progress, and timeout-minutes. A hung job previously burned the six hour default. - Action versions. actionlint rejected setup-node@v3, cache@v3 and setup-java@v3 as too old for current runners, and gradle-build-action is deprecated in favour of gradle/actions/setup-gradle. - setup-node's built-in pnpm cache replaces the manual store-path and actions/cache block. pnpm is installed first so the cache has a package manager to query. - The android job is skipped on fork pull requests. It writes google-services.json from repository secrets, and GitHub withholds secrets from forks, so it would fail every outside contribution. - google-services.json is written through an env var instead of shell interpolation, so the secret never lands on a command line and JSON containing quotes cannot break the redirect. - fail-fast: false on the android matrix, so a dev failure no longer cancels the prod build. - Dropped the android_variant dispatch input. It was declared but never referenced; the matrix always built both variants. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…specs Four specs had been failing on main for as long as the run history goes back. The old workflow set continue-on-error on the e2e step rather than the job, and a step with continue-on-error reports its conclusion as success even when the command exits non-zero, so the failures never surfaced. Splitting e2e into its own job made them visible. None of them were stale. Every string they assert on still exists in the source. Three are hydration races of the shape ab96e73 fixed for the messaging specs: the sections stream behind a loading.tsx boundary, so the HTML paints before React attaches handlers, and a click landing in that gap is silently dropped. The test then waits out its timeout on a dialog that was never going to open. Both specs now navigate through a helper that waits on a value only the client render can produce: the gateway stats figure on the dashboard, and the fetched device id in the guide. Prism tokens are not usable as that signal, because react-syntax-highlighter renders its spans server side too, so they are already in the SSR payload. The clipboard spec had a second, unrelated cause. It asserted on the button's "Copied" label, and code-block.tsx clears that label two seconds after the click, so the assertion raced a window narrow enough to miss on a loaded runner. It now polls the clipboard, which is the behaviour under test and does not expire. Verified with --repeat-each=4 --retries=0 --workers=2: 56 passed. Single worker runs are why local looked green while CI failed; the specs' own comments note that parallel workers background the page, which one worker never reproduces. No production code changed. These were test bugs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fd62c41 to
4ab8c0c
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/android.yaml:
- Around line 52-64: Pin every GitHub Action reference to its full immutable
commit SHA instead of a mutable version tag. Update the actions in
.github/workflows/android.yaml at lines 52-64 and 97-98,
.github/workflows/api.yaml at lines 46-60, and .github/workflows/web.yaml at
lines 45-59, 98-110, and 127-129; also pin all listed actions in
.github/workflows/docker-publish.yaml at lines 43, 48, 54, 60, 71, 87, 101, and
117.
- Around line 76-81: Update the “Create google-services.json” workflow step to
stop sourcing or writing repository secrets, and instead generate the variant
files from public, Firebase-restricted client configuration values. Preserve
separate dev and prod variant handling while ensuring server credentials remain
outside the APK.
In @.github/workflows/api.yaml:
- Around line 59-64: Update all three node-version entries in
.github/workflows/api.yaml:59-64, .github/workflows/web.yaml:58-63, and
.github/workflows/web.yaml:109-114 to the same supported Node.js runtime
compatible with the API and web packages. In these workflows, pin every
actions/checkout, pnpm/action-setup, actions/setup-node, and
actions/upload-artifact reference to its immutable commit SHA rather than a
mutable tag or branch.
In @.github/workflows/web.yaml:
- Around line 88-92: Remove continue-on-error: true from the e2e job
configuration so failures from pnpm test:e2e make the workflow fail and block
merging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8a5af45d-3ce0-462c-a60b-7fe67a9dae68
📒 Files selected for processing (7)
.coderabbit.yaml.github/workflows/android.yaml.github/workflows/api.yaml.github/workflows/build-and-test.yaml.github/workflows/web.yamlweb/e2e/api-guide.spec.tsweb/e2e/dashboard.spec.ts
💤 Files with no reviewable changes (1)
- .github/workflows/build-and-test.yaml
| - name: Checkout repository | ||
| uses: actions/checkout@v7 | ||
| with: | ||
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.branch || github.ref }} | ||
|
|
||
| - name: Set up JDK 17 | ||
| uses: actions/setup-java@v5 | ||
| with: | ||
| java-version: '17' | ||
| distribution: 'temurin' | ||
|
|
||
| - name: Setup Gradle | ||
| uses: gradle/actions/setup-gradle@v6 |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
if rg -nP '^\s*uses:\s+\S+@v[0-9]+(?:\.[0-9]+){0,2}\s*$' .github/workflows; then
echo "Replace each mutable tag with a verified full commit SHA."
exit 1
fiRepository: vernu/textbee
Length of output: 1859
Pin every GitHub Action to a full commit SHA.
Major-version tags are mutable. Apply this to all listed actions, including .github/workflows/docker-publish.yaml at lines 43, 48, 54, 60, 71, 87, 101, and 117.
🧰 Tools
🪛 zizmor (1.28.0)
[warning] 52-55: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
📍 Affects 3 files
.github/workflows/android.yaml#L52-L64(this comment).github/workflows/android.yaml#L97-L98.github/workflows/api.yaml#L46-L60.github/workflows/web.yaml#L45-L59.github/workflows/web.yaml#L98-L110.github/workflows/web.yaml#L127-L129
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/android.yaml around lines 52 - 64, Pin every GitHub Action
reference to its full immutable commit SHA instead of a mutable version tag.
Update the actions in .github/workflows/android.yaml at lines 52-64 and 97-98,
.github/workflows/api.yaml at lines 46-60, and .github/workflows/web.yaml at
lines 45-59, 98-110, and 127-129; also pin all listed actions in
.github/workflows/docker-publish.yaml at lines 43, 48, 54, 60, 71, 87, 101, and
117.
Source: Path instructions
| - name: Create google-services.json | ||
| env: | ||
| GOOGLE_SERVICES_JSON: ${{ matrix.variant == 'dev' && secrets.GOOGLE_SERVICES_JSON_DEV || secrets.GOOGLE_SERVICES_JSON_PROD }} | ||
| run: | | ||
| mkdir -p android/app/src/${{ matrix.variant }} | ||
| printf '%s' "$GOOGLE_SERVICES_JSON" > android/app/src/${{ matrix.variant }}/google-services.json |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- workflow excerpt ---'
sed -n '1,130p' .github/workflows/android.yaml
printf '%s\n' '--- Android configuration references ---'
rg -n -C 4 'google-services|GOOGLE_SERVICES_JSON|productFlavors|variant|assemble|upload-artifact' .github/workflows android/app/build.gradle android 2>/dev/null | head -240Repository: vernu/textbee
Length of output: 15872
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- tracked and present Firebase configuration files ---'
git ls-files 'android/app/src/*/google-services.json' 'android/app/google-services.json' || true
find android/app -type f -name 'google-services.json' -print
printf '%s\n' '--- Android build configuration ---'
sed -n '1,95p' android/app/build.gradle
printf '%s\n' '--- workflow data-flow verifier ---'
python3 - <<'PY'
from pathlib import Path
import re
workflow = Path(".github/workflows/android.yaml").read_text()
build = Path("android/app/build.gradle").read_text()
assert "GOOGLE_SERVICES_JSON:" in workflow
assert "secrets.GOOGLE_SERVICES_JSON_DEV" in workflow
assert "secrets.GOOGLE_SERVICES_JSON_PROD" in workflow
assert 'printf \'%s\' "$GOOGLE_SERVICES_JSON" > android/app/src/${{ matrix.variant }}/google-services.json' in workflow
variants = re.findall(r"^\s+(dev|prod)\s*\{", build, re.MULTILINE)
assert set(variants) == {"dev", "prod"}
assert "id 'com.google.gms.google-services'" in build
assert "assemble${{ matrix.variant }}Debug" in workflow
assert "path: android/app/build/outputs/apk/${{ matrix.variant }}/debug/app-${{ matrix.variant }}-debug.apk" in workflow
print("The workflow maps repository secrets to variant google-services.json files.")
print("The workflow builds both corresponding debug variants.")
print("The workflow uploads each corresponding APK.")
PYRepository: vernu/textbee
Length of output: 3151
Do not write repository secrets to google-services.json.
This step writes GOOGLE_SERVICES_JSON to files used by both APK builds. Use public, restricted Firebase client configuration. Keep server credentials outside the APK.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/android.yaml around lines 76 - 81, Update the “Create
google-services.json” workflow step to stop sourcing or writing repository
secrets, and instead generate the variant files from public, Firebase-restricted
client configuration values. Preserve separate dev and prod variant handling
while ensuring server credentials remain outside the APK.
Source: Path instructions
| - name: Set up Node.js | ||
| uses: actions/setup-node@v7 | ||
| with: | ||
| node-version: '20' | ||
| cache: pnpm | ||
| cache-dependency-path: api/pnpm-lock.yaml |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
for package in api/package.json web/package.json; do
printf '%s\n' "$package"
jq '{engines, packageManager}' "$package"
done
rg -n -C 2 "node-version: '20'" .github/workflowsRepository: vernu/textbee
Length of output: 1226
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' 'Project runtime declarations:'
for package in api/package.json web/package.json; do
printf '\n%s\n' "$package"
jq '{name,engines,packageManager}' "$package"
done
printf '\nRepository runtime files:\n'
git ls-files | rg '(^|/)(\.nvmrc|\.node-version|package\.json|pnpm-workspace\.yaml|pnpm-lock\.yaml|volta|tool-versions)$' || true
printf '\nRelevant workflow sections:\n'
sed -n '45,72p' .github/workflows/api.yaml
sed -n '45,70p' .github/workflows/web.yaml
sed -n '96,121p' .github/workflows/web.yaml
printf '\nAll Node runtime declarations:\n'
rg -n -C 2 'node-version|NODE_VERSION|node\":|node:|\\.nvmrc|\\.node-version' .github api web package.json pnpm-workspace.yaml 2>/dev/null || trueRepository: vernu/textbee
Length of output: 50370
🌐 Web query:
Node.js official previous releases Node.js 20 end of life date
💡 Result:
The official end-of-life (EOL) date for Node.js 20 was April 30, 2026 [1][2]. As of August 3, 2026, this version is no longer maintained by the Node.js project [3][4].
Citations:
- 1: https://github.com/nodejs/Release/blob/main/README.md
- 2: https://github.com/nodejs/release?tab=readme-ov-file
- 3: https://nodejs.org/en/about/eol
- 4: https://nodejs.org/en/about/previous-releases
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' 'Project manifests:'
jq '{name,engines,packageManager,scripts}' api/package.json
jq '{name,engines,packageManager,scripts}' web/package.json
printf '\n%s\n' 'Direct dependency engine constraints from web lockfile:'
awk '
/^ (dependencies|devDependencies):$/ {section=$0}
section && /^ [^ ]+:/ {name=$0}
section && /engines: \{node:/ {print name "\n" $0}
' web/pnpm-lock.yaml | head -n 80
printf '\n%s\n' 'Workflow action references:'
rg -n '^[[:space:]]*uses:' .github/workflows/api.yaml .github/workflows/web.yamlRepository: vernu/textbee
Length of output: 2046
Move CI from Node.js 20 to a supported runtime.
Node.js 20 reached end of life on April 30, 2026. Select a supported runtime that satisfies the API and web packages, then update all three node-version entries.
Pin all workflow actions to commit SHAs.
The workflows use mutable references for actions/checkout, pnpm/action-setup, actions/setup-node, and actions/upload-artifact.
📍 Affects 2 files
.github/workflows/api.yaml#L59-L64(this comment).github/workflows/web.yaml#L58-L63.github/workflows/web.yaml#L109-L114
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/api.yaml around lines 59 - 64, Update all three
node-version entries in .github/workflows/api.yaml:59-64,
.github/workflows/web.yaml:58-63, and .github/workflows/web.yaml:109-114 to the
same supported Node.js runtime compatible with the API and web packages. In
these workflows, pin every actions/checkout, pnpm/action-setup,
actions/setup-node, and actions/upload-artifact reference to its immutable
commit SHA rather than a mutable tag or branch.
| e2e: | ||
| name: e2e | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| continue-on-error: true |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make the e2e job blocking.
continue-on-error: true permits the workflow to pass when pnpm test:e2e fails. A browser regression can merge without a failing workflow result. Remove this setting before release. (docs.github.com)
Proposed fix
- continue-on-error: true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| e2e: | |
| name: e2e | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| continue-on-error: true | |
| e2e: | |
| name: e2e | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/web.yaml around lines 88 - 92, Remove continue-on-error:
true from the e2e job configuration so failures from pnpm test:e2e make the
workflow fail and block merging.
Two changes to how PRs get checked: an AI review layer, and a CI restructure so that review has something useful to read and lands faster.
1. CodeRabbit review (
.coderabbit.yaml)textbee is a public repo, so it qualifies for CodeRabbit's Open Source plan: the Pro+ feature set at no cost, activated automatically on install.
Reviews are comment only on the
chillprofile.request_changes_workflowstaysfalse, so nothing gates a merge.Two settings are doing real work:
base_branches: [dev]. The default branch is always reviewed and cannot be excluded, somainis covered implicitly.devhas to be listed explicitly. Without it, every feature PR would go unreviewed, since they all targetdev.labels: ['!skip-ai-review']. Becausemaincannot be dropped frombase_branches, this label is the only lever for skipping thedevtomainroll-up PRs, which would otherwise re-review code already seen on the way intodev. The label exists on the repo.CodeRabbit auto-reads
CLAUDE.mdfrom the repo root as review guidelines, but this repo'sCLAUDE.mdsits one directory above the git root and.cursor/rulesis not on its auto-read list. The no-em-dash and pnpm-only rules are encoded directly intone_instructionsand a global path instruction instead.path_filtersexclude build output, lockfiles, and Android artifacts.path_instructionsare scoped per app: tenant scoping and secret leakage forapi/src, client/server boundaries forweb, permissions and main-thread work forandroid, plus a note thatweb/components/uiis vendored shadcn.2. CI on pull requests, split per app
build-and-test.yamlfired onpushonly, so PRs carried no checks of their own. CodeRabbit'sgithub-checkstool waits on GitHub Checks and comments on failures, which gave it nothing to read, and outside contributors got no CI signal before merge.It also ran everything in one workflow, with
webandapisharing a single job. Measured on the last green run:continue-on-error)So e2e was 53% of the wall clock while gating nothing, and an api-only change still paid for all of it plus both android builds.
Now three workflows,
api.yaml/web.yaml/android.yaml, each with its ownpathsfilter. Separate workflows get per-app filtering natively, with no third-party change-detection action. Web e2e is its own job, so blocking feedback lands in roughly 90 seconds. It needs no build step because playwright'swebServeralready runspnpm build && pnpm start.Also folded in, since the files were being rewritten anyway:
permissions: contents: read. Nothing was declared, so the token got default write scope on every run.concurrencywithcancel-in-progress, andtimeout-minutes. A hung job previously burned the 6 hour default.setup-node@v3,cache@v3andsetup-java@v3as too old for current runners, andgradle-build-actionis deprecated in favour ofgradle/actions/setup-gradle.setup-node's built-in pnpm cache replaces the manual store-path andactions/cacheblock. pnpm is installed first so the cache has a package manager to query.github.event.pull_request.head.repo.full_name == github.repository. It writesgoogle-services.jsonfrom repository secrets, and GitHub withholds secrets from fork PRs, so without the guard every outside contribution would fail.google-services.jsonis written through an env var instead of shell interpolation, so the secret never lands on a command line and JSON containing quotes cannot break the redirect.fail-fast: falseon the android matrix, so a dev failure no longer cancels the prod build.android_variantdispatch input. It was declared but never referenced; the matrix always built both variants.Verification
.coderabbit.yamlvalidated againstschema.v2.jsonwith ajv: passes.path_filterschecked with minimatch against all 516 tracked files: excludes 3, no source files beyond the generatedweb/next-env.d.ts.build-and-test.yaml; those are now gone.pull_requesttrigger was validated live on this PR before the split, with the old workflow green.Note on required checks
If branch protection with required status checks is ever turned on, split workflows hit the classic "expected but never received" problem when a path filter skips one. At that point
dorny/paths-filterin a single workflow is the better shape. There is no branch protection today.🤖 Generated with Claude Code
Summary by CodeRabbit