Skip to content

chore: quality-tightening (oxfmt + drop prettier/eslint + tsgo + husky self-heal) - #95

Merged
frostebite merged 2 commits into
mainfrom
quality-tightening
May 6, 2026
Merged

chore: quality-tightening (oxfmt + drop prettier/eslint + tsgo + husky self-heal)#95
frostebite merged 2 commits into
mainfrom
quality-tightening

Conversation

@webbertakken

@webbertakken webbertakken commented May 5, 2026

Copy link
Copy Markdown
Member

This repo was already partially migrated (had oxlint, husky 9, lint-staged, vitest). Finishes the alignment with the rest of the rollout.

Drops

  • prettier (root) + prettier (functions/) \u2014 replaced with oxfmt across the workspace.
  • functions/ workspace's eslint + @typescript-eslint/* + jest \u2014 dropped entirely; the workspace now uses the root oxlint via yarn lint.
  • npm shellcheck package (playbook trap setup contributor and styles #2: it's broken and corrupts actionlint output). mise.toml adds shellcheck plus actionlint and gitleaks as managed binaries.

Adds

  • oxfmt, eslint-plugin-unicorn, @typescript/native-preview (tsgo).
  • Standard scripts/ensure-husky.mjs self-heal pattern + gitleaks integration in .husky/pre-commit. Drops the previous exec >/dev/tty redirect that fails in non-interactive shells.
  • oxlint plugin set extended to typescript + vitest + unicorn + oxc (was just baseline correctness rules).
  • tsgo --noEmit as default typecheck; tsc kept as typecheck:tsc fallback.

CI

Existing main.yml steps re-ordered (Format / Lint / Typecheck / Test) and yarn format (which writes) replaced with yarn format:check (which doesn't). codecov-action@v4.0.1 \u2192 @v5.

oxlint config tunes

  • typescript/no-extraneous-class off \u2014 the codebase uses static-only classes as namespaces, idiomatic in this repo.
  • no-await-in-loop off \u2014 Firestore migrations and rate-limited HTTP loops are single-threaded by design.

Caveats

13 pre-existing typescript/no-explicit-any warnings remain (mostly in firebase-functions handler signatures and json deserialisation). Per playbook trap #22 the lint script drops --deny-warnings.

Verified locally

  • yarn lint \u2014 0 errors, 13 warnings
  • yarn format:check \u2014 clean (73 files)
  • yarn typecheck \u2014 clean
  • yarn test \u2014 7 / 7

Summary by CodeRabbit

  • Documentation

    • Simplified Firebase setup instructions in development documentation.
  • Style

    • Redesigned 404 error page with updated layout, typography, and responsive styling.

…y self-heal)

This repo was already partially migrated (had oxlint, husky 9,
lint-staged, vitest). Finishes the alignment with the rest of the
rollout:

- prettier (root) + prettier (functions/) -> oxfmt across the workspace
- functions/ workspace eslint + @typescript-eslint/* + jest -> dropped
  entirely; the workspace now uses the root oxlint via 'yarn lint'.
- new: oxlint plugins extended to typescript + vitest + unicorn + oxc
  (was just baseline correctness rules)
- new: tsgo --noEmit as default 'typecheck'; tsc kept as
  'typecheck:tsc' fallback
- husky 9: existing /sh/ hook + 'husky install' replaced with the
  standard scripts/ensure-husky.mjs self-heal pattern + lint-staged
  + gitleaks. Drops the broken 'exec >/dev/tty' redirect that fails
  in non-interactive shells.
- npm 'shellcheck' package dropped (playbook trap #2: it's broken
  and corrupts actionlint output). mise.toml adds shellcheck plus
  actionlint and gitleaks as managed binaries.
- CI: existing main.yml steps re-ordered (Format/Lint/Typecheck/
  Test) and 'yarn format' (which writes) replaced with
  'yarn format:check' (which doesn't). codecov-action v4.0.1 -> v5.

oxlint config tunes:
- typescript/no-extraneous-class off (the codebase uses static-only
  classes as namespaces, idiomatic in this repo)
- no-await-in-loop off (Firestore migrations and rate-limited HTTP
  loops are single-threaded by design)

13 pre-existing typescript/no-explicit-any warnings remain (mostly
in firebase-functions handler signatures and json deserialisation).
Per playbook trap #22 the lint script drops --deny-warnings.

Verified locally: lint 0/13, format clean, typecheck clean, test 7/7.
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@webbertakken has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 41 minutes and 38 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 842490de-477e-4cd9-862e-cf85496a6f69

📥 Commits

Reviewing files that changed from the base of the PR and between 00f88fa and 2dd4ec2.

📒 Files selected for processing (1)
  • firebase.json
📝 Walkthrough

Walkthrough

This PR consolidates a tooling migration from Prettier to oxfmt/oxlint and reorganizes the development workflow from Turbo-based to Husky-based git hooks, with updated GitHub Actions, package scripts, and new helper utilities to manage Husky initialization across environments.

Changes

Tooling & Workflow Migration

Layer / File(s) Summary
Formatting & Linting Config
.oxfmtrc.json, .oxlintrc.json, .prettierrc.json, .prettierignore
New oxfmt and oxlint configs added; Prettier config removed. OxLint expanded with full categories, plugins, test overrides, and ignorePatterns; oxfmt configured with semi, singleQuote, trailingComma, printWidth, and ignorePatterns.
Toolchain & Environment Config
.yarnrc.yml, mise.toml
Yarn hardened mode disabled; enableHardenedMode set to false. Mise tools added: actionlint, shellcheck, gitleaks.
Package Dependencies & Scripts
package.json, functions/package.json
Root package: Turbo dev flow replaced with Husky+Yarn workspace; test/prepare scripts updated to ensure Husky initialization; lint-staged now uses oxlint/oxfmt for TS/JS. Dependencies added: firestore-backfire, @typescript/native-preview, @vitest/coverage-v8, @vitest/ui, oxfmt; Prettier removed. Functions: lint script removed; typecheck split into tsgo and tsc variants.
Git Hooks & CI Workflow
.husky/pre-commit, .github/workflows/main.yml
Pre-commit hook updated with yarn typecheck and conditional gitleaks protection. GitHub Actions: Node version extraction consolidated to single-line runs; test job refactored with modular Format/Lint/Typecheck/Test steps; Codecov upgraded to v5 with token passing; yarn cache key formatting unified across jobs.
Helper Scripts & Support
scripts/ensure-husky.mjs
New self-healing script for Husky git hooks; skips in CI/when disabled; detects hooks path and reinstalls if needed; includes error handling with detailed messages.
Docs & Styling
DEVELOPMENT.md, public/404.html, public/index.html, functions/src/config/credential.ts
Firebase setup docs simplified; 404.html redesigned with lighter theme and improved spacing; index.html doctype lowercased and CSS reformatted; credential.ts require call reflowed for readability.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • game-ci/versioning-backend#93: Updates toolchain pinning (mise.toml, .yarnrc.yml) and modifies GitHub Actions workflow to align with Node/Yarn version reading strategy.
  • game-ci/versioning-backend#69: Modifies test job structure, test scripts, and coverage tooling in package.json and GitHub Actions workflow.

Suggested reviewers

  • frostebite
  • davidmfinol

Poem

🐰 Formatters hop and linters bound,
Oxfmt paints the code around,
Husky guards the git with care,
Scripts ensure the hooks are there!
No more Prettier in sight,
Our tooling's cleaner, sharp, and bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main changes: quality improvements via oxfmt, dropping prettier/eslint, adding tsgo, and implementing husky self-healing.
Description check ✅ Passed The description provides comprehensive details on changes, includes checklist items, though the checklist section differs slightly from template structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch quality-tightening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
.oxfmtrc.json (1)

1-16: 💤 Low value

Add a $schema field for editor validation and autocomplete.

Adding a $schema field enables editor validation and autocomplete. The .oxlintrc.json in this PR already sets one; the same treatment here would keep the two configs consistent.

✨ Suggested addition
 {
+  "$schema": "./node_modules/oxfmt/configuration_schema.json",
   "semi": true,
🤖 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 @.oxfmtrc.json around lines 1 - 16, Add a top-level "$schema" field to the
.oxfmtrc.json to enable editor validation/autocomplete; edit the JSON object
that currently contains keys like "semi", "singleQuote", "trailingComma", etc.,
and insert a "$schema" property (matching the same schema used in
.oxlintrc.json) as the first or near-first property so editors can pick it up
and validate/autocomplete the config.
mise.toml (1)

4-6: ⚡ Quick win

Consider pinning tool versions for reproducibility.

actionlint, shellcheck, and gitleaks are set to "latest", unlike node and yarn which are version-pinned. A new release of any of these tools could introduce stricter rules or breaking changes that silently break developer environments or CI.

🔧 Suggested fix
-actionlint = "latest"
-shellcheck = "latest"
-gitleaks = "latest"
+actionlint = "1.7.7"
+shellcheck = "0.10.0"
+gitleaks = "8.27.2"

Replace the version strings above with the current versions you have validated locally.

🤖 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 `@mise.toml` around lines 4 - 6, Replace the floating "latest" pins for the
linters with concrete, tested versions: update the actionlint, shellcheck, and
gitleaks entries so their values are the specific version strings you validated
locally (e.g., "vX.Y.Z" or the exact release number), ensuring they match the
format used for node/yarn; change the keys actionlint, shellcheck, and gitleaks
in mise.toml to those fixed versions.
package.json (2)

28-29: 💤 Low value

actionlint in lint-staged requires out-of-band installation.

actionlint is not listed in devDependencies and is managed by mise.toml. Developers without mise (or with a stale mise cache) will get a command not found failure when staging .github/workflows/ files. Consider adding a comment or README note about this prerequisite, or wrapping the call with a guard that skips gracefully when actionlint isn't in PATH.

🤖 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 `@package.json` around lines 28 - 29, The lint-staged entry
".github/workflows/*.@(yml|yaml)": "actionlint" calls actionlint but actionlint
isn't listed in devDependencies (it's managed out-of-band by mise), causing
failures for developers without mise; fix by either adding actionlint to
devDependencies in package.json so it is installed for all contributors, or
change the lint-staged command for the ".github/workflows/*.@(yml|yaml)" glob to
a guarded invocation that skips gracefully when actionlint isn't on PATH (e.g.,
a shell wrapper that checks for the command before running), and optionally add
a short README note about the mise prerequisite if you keep the external
management.

54-54: 💤 Low value

Pin @typescript/native-preview to an exact version for predictable CI/local parity.

@typescript/native-preview is a nightly preview that may lack functionality present in the stable compiler, and unexpected breakage is possible when the lockfile is regenerated. The functions workspace typecheck script invokes tsgo from this package. The current ^ range means a fresh yarn install (after regenerating the lock file) will pull the latest nightly automatically. Pinning to an exact version (removing the caret) would prevent unintended version differences between CI and local builds.

🤖 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 `@package.json` at line 54, The dependency declaration for
"@typescript/native-preview" uses a caret range which allows updating to newer
nightly builds and can cause CI/local parity issues; update the package.json
entry for "@typescript/native-preview" to an exact version (remove the leading
"^") so installs always use the same nightly, and ensure any references to
"tsgo" in the functions workspace typecheck script remain compatible with that
pinned version.
.github/workflows/main.yml (1)

54-64: ⚖️ Poor tradeoff

Consider splitting quality gates into separate jobs for better CI feedback.

All four checks (Format → Lint → Typecheck → Test) run sequentially in the same job. A failure in Format blocks visibility of Lint/Typecheck/Test results until Format is fixed. Splitting these into parallel jobs (or at minimum using continue-on-error: true on the non-test steps) would surface all failures at once and speed up the feedback loop.

This is especially relevant now that Format and Lint are independent of Test infrastructure.

🤖 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/main.yml around lines 54 - 64, Split the single job that
runs the "Format", "Lint", "Typecheck", and "Test" steps into separate GitHub
Actions jobs so failures are reported independently and can run in parallel:
create standalone jobs named e.g. "format-check", "lint", "typecheck", and
"test" that each run the corresponding command (yarn format:check, yarn lint,
yarn typecheck, yarn coverage), or if you prefer keep them in one job add
continue-on-error: true to the non-test steps ("Format", "Lint", "Typecheck") so
they don't block subsequent checks; ensure job names match the existing step
labels ("Format", "Lint", "Typecheck", "Test") so it's easy to locate and verify
the changes in main.yml.
🤖 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/main.yml:
- Around line 17-18: The step that extracts node version using the run command
(the `run: echo "version=$(grep -E '^node\s*=' mise.toml | sed -E
's/.*"([^"]+)".*/\1/')" >> "$GITHUB_OUTPUT"` line) only matches double-quoted
values; change the sed/regex to accept optional surrounding quotes (double or
single) or bare values so it reliably captures node = "20.x", node = '20.x', or
node = 20, and replace the existing pattern in that run step; then apply the
identical fix to the repeated extraction steps in the build, testDeploy, and
deploy jobs so all actions/setup-node invocations receive a robust version
value.

---

Nitpick comments:
In @.github/workflows/main.yml:
- Around line 54-64: Split the single job that runs the "Format", "Lint",
"Typecheck", and "Test" steps into separate GitHub Actions jobs so failures are
reported independently and can run in parallel: create standalone jobs named
e.g. "format-check", "lint", "typecheck", and "test" that each run the
corresponding command (yarn format:check, yarn lint, yarn typecheck, yarn
coverage), or if you prefer keep them in one job add continue-on-error: true to
the non-test steps ("Format", "Lint", "Typecheck") so they don't block
subsequent checks; ensure job names match the existing step labels ("Format",
"Lint", "Typecheck", "Test") so it's easy to locate and verify the changes in
main.yml.

In @.oxfmtrc.json:
- Around line 1-16: Add a top-level "$schema" field to the .oxfmtrc.json to
enable editor validation/autocomplete; edit the JSON object that currently
contains keys like "semi", "singleQuote", "trailingComma", etc., and insert a
"$schema" property (matching the same schema used in .oxlintrc.json) as the
first or near-first property so editors can pick it up and validate/autocomplete
the config.

In `@mise.toml`:
- Around line 4-6: Replace the floating "latest" pins for the linters with
concrete, tested versions: update the actionlint, shellcheck, and gitleaks
entries so their values are the specific version strings you validated locally
(e.g., "vX.Y.Z" or the exact release number), ensuring they match the format
used for node/yarn; change the keys actionlint, shellcheck, and gitleaks in
mise.toml to those fixed versions.

In `@package.json`:
- Around line 28-29: The lint-staged entry ".github/workflows/*.@(yml|yaml)":
"actionlint" calls actionlint but actionlint isn't listed in devDependencies
(it's managed out-of-band by mise), causing failures for developers without
mise; fix by either adding actionlint to devDependencies in package.json so it
is installed for all contributors, or change the lint-staged command for the
".github/workflows/*.@(yml|yaml)" glob to a guarded invocation that skips
gracefully when actionlint isn't on PATH (e.g., a shell wrapper that checks for
the command before running), and optionally add a short README note about the
mise prerequisite if you keep the external management.
- Line 54: The dependency declaration for "@typescript/native-preview" uses a
caret range which allows updating to newer nightly builds and can cause CI/local
parity issues; update the package.json entry for "@typescript/native-preview" to
an exact version (remove the leading "^") so installs always use the same
nightly, and ensure any references to "tsgo" in the functions workspace
typecheck script remain compatible with that pinned version.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 18e9531a-ed2e-4956-a7fa-7bddae7e9f36

📥 Commits

Reviewing files that changed from the base of the PR and between a2fcfe0 and 00f88fa.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (15)
  • .github/workflows/main.yml
  • .husky/pre-commit
  • .oxfmtrc.json
  • .oxlintrc.json
  • .prettierignore
  • .prettierrc.json
  • .yarnrc.yml
  • DEVELOPMENT.md
  • functions/package.json
  • functions/src/config/credential.ts
  • mise.toml
  • package.json
  • public/404.html
  • public/index.html
  • scripts/ensure-husky.mjs
💤 Files with no reviewable changes (3)
  • .prettierrc.json
  • DEVELOPMENT.md
  • .prettierignore

Comment on lines +17 to 18
run: echo "version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*"([^"]+)".*/\1/')" >>
"$GITHUB_OUTPUT"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Node-version extraction relies on double-quoted value format in mise.toml.

The sed pattern s/.*"([^"]+)".*/\1/ requires the value to be double-quoted (e.g. node = "20.x"). If mise.toml is ever updated to use single quotes or bare integers, sed returns an empty string and actions/setup-node fails silently with a confusing error. The same pattern is repeated in the build, testDeploy, and deploy jobs (Lines 83-84, 133-134, 196-197).

🛡️ More robust extraction
-        run: echo "version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*"([^"]+)".*/\1/')" >>
-          "$GITHUB_OUTPUT"
+        run: |
+          version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*["'"'"']([^"'"'"']+)["'"'"'].*/\1/')
+          echo "version=$version" >> "$GITHUB_OUTPUT"
+          if [ -z "$version" ]; then echo "::error::Could not parse node version from mise.toml"; exit 1; fi

Apply the same fix to the identical steps in build (Line 83), testDeploy (Line 133), and deploy (Line 196).

📝 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.

Suggested change
run: echo "version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*"([^"]+)".*/\1/')" >>
"$GITHUB_OUTPUT"
run: |
version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*["'"'"']([^"'"'"']+)["'"'"'].*/\1/')
echo "version=$version" >> "$GITHUB_OUTPUT"
if [ -z "$version" ]; then echo "::error::Could not parse node version from mise.toml"; exit 1; fi
🤖 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/main.yml around lines 17 - 18, The step that extracts node
version using the run command (the `run: echo "version=$(grep -E '^node\s*='
mise.toml | sed -E 's/.*"([^"]+)".*/\1/')" >> "$GITHUB_OUTPUT"` line) only
matches double-quoted values; change the sed/regex to accept optional
surrounding quotes (double or single) or bare values so it reliably captures
node = "20.x", node = '20.x', or node = 20, and replace the existing pattern in
that run step; then apply the identical fix to the repeated extraction steps in
the build, testDeploy, and deploy jobs so all actions/setup-node invocations
receive a robust version value.

The Test Deploy job runs 'firebase deploy --only functions:testFunction'
which executes the firebase.json 'predeploy' hooks. The hook still
called:
  - yarn workspace functions lint
  - yarn workspace functions test run

Both broke after the migration:
- The 'lint' script is now at the root only (functions/ no longer has
  its own lint command).
- 'test run' was the jest CLI form ('jest run'); vitest's equivalent
  is 'vitest run' which we exposed as the 'test:run' workspace
  script.

Predeploy hooks now invoke:
  - yarn lint                          (root oxlint over the workspace)
  - yarn workspace functions run test:run  (vitest run, scoped to functions)
  - yarn workspace functions build      (unchanged)
@frostebite
frostebite merged commit dc13ec5 into main May 6, 2026
6 checks passed
@frostebite
frostebite deleted the quality-tightening branch May 6, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants