-
Notifications
You must be signed in to change notification settings - Fork 44
CI/CD Linting + Smoke #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: ['**'] | ||
| pull_request: | ||
| branches: ['**'] | ||
|
|
||
| jobs: | ||
| lint-typecheck: | ||
| name: Lint & Type-check (backend) | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
| with: | ||
| version: latest | ||
|
|
||
| - name: Setup Node 24 | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24' | ||
| cache: 'pnpm' | ||
|
|
||
| - name: Install dependencies | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Build shared | ||
| run: pnpm run build:shared | ||
|
|
||
| - name: Lint backend | ||
| run: pnpm --filter @notblox/back run lint | ||
|
|
||
| - name: Type-check backend | ||
| run: pnpm --filter @notblox/back run build | ||
|
|
||
| docker-smoke: | ||
| name: Docker build & smoke test | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Build image | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| load: true | ||
| tags: notblox-game-server:ci | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
|
|
||
| - name: Start container | ||
| run: docker run -d --name smoke notblox-game-server:ci | ||
|
|
||
| - name: Wait for healthy | ||
| run: | | ||
| for i in $(seq 1 12); do | ||
| STATUS=$(docker inspect --format='{{.State.Health.Status}}' smoke 2>/dev/null || echo 'missing') | ||
| echo "Attempt $i: $STATUS" | ||
| if [ "$STATUS" = "healthy" ]; then | ||
| echo "Container is healthy" | ||
| exit 0 | ||
| fi | ||
| if [ "$STATUS" = "unhealthy" ]; then | ||
| echo "Container became unhealthy" | ||
| docker logs smoke | ||
| exit 1 | ||
| fi | ||
| sleep 5 | ||
| done | ||
| echo "Timed out waiting for container to become healthy" | ||
| docker logs smoke | ||
| exit 1 | ||
|
|
||
| - name: Print logs | ||
| if: always() | ||
| run: docker logs smoke | ||
|
|
||
| - name: Stop container | ||
| if: always() | ||
| run: docker rm -f smoke | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,9 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| import js from "@eslint/js"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import globals from "globals"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import tseslint from "typescript-eslint"; | ||||||||||||||||||||||||||||||||||||||||||||||||
| import { defineConfig } from "eslint/config"; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| export default defineConfig([ | ||||||||||||||||||||||||||||||||||||||||||||||||
| { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } }, | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
| { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } }, | |
| { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.node } }, |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint.config.ts is using flat-config style (defineConfig) but mixes in legacy config fields: plugins: { js } and extends: ["js/recommended"]. @eslint/js exports config objects (e.g. js.configs.recommended) rather than a plugin, and flat config extends should reference config objects (not strings). As written, ESLint 10 will likely fail to load or silently not apply the intended rules—please restructure the config to compose js.configs.recommended + the TypeScript ESLint configs directly.
| { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } }, | |
| tseslint.configs.recommended, | |
| { | |
| ...js.configs.recommended, | |
| files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], | |
| languageOptions: { | |
| ...js.configs.recommended.languageOptions, | |
| globals: { | |
| ...(js.configs.recommended.languageOptions?.globals ?? {}), | |
| ...globals.browser, | |
| }, | |
| }, | |
| }, | |
| ...tseslint.configs.recommended, |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new TS config file doesn’t follow the repository’s Prettier settings (single quotes, no semicolons, line width). This will introduce avoidable formatting diffs when the file is auto-formatted. Please run Prettier (or adjust the file) to match .prettierrc.
| import js from "@eslint/js"; | |
| import globals from "globals"; | |
| import tseslint from "typescript-eslint"; | |
| import { defineConfig } from "eslint/config"; | |
| export default defineConfig([ | |
| { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"], languageOptions: { globals: globals.browser } }, | |
| tseslint.configs.recommended, | |
| ]); | |
| import js from '@eslint/js' | |
| import globals from 'globals' | |
| import tseslint from 'typescript-eslint' | |
| import { defineConfig } from 'eslint/config' | |
| export default defineConfig([ | |
| { | |
| files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], | |
| plugins: { js }, | |
| extends: ['js/recommended'], | |
| languageOptions: { globals: globals.browser }, | |
| }, | |
| tseslint.configs.recommended, | |
| ]) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,30 +10,32 @@ | |||||
| "dev": "tsx watch src/sandbox.ts", | ||||||
| "build": "tsc --noEmit", | ||||||
| "start": "node --import tsx/esm src/sandbox.ts", | ||||||
| "lint": "eslint src/**/*.ts", | ||||||
| "format": "eslint src/**/*.ts --fix" | ||||||
| "lint": "eslint src/", | ||||||
| "format": "eslint src/ --fix" | ||||||
| }, | ||||||
| "author": "iercann", | ||||||
| "license": "MIT", | ||||||
| "engines": { | ||||||
| "node": ">=24" | ||||||
| }, | ||||||
| "overrides": { | ||||||
| "minimatch": "^10.2.1" | ||||||
| "minimatch": "^10.2.1", | ||||||
| "jiti": "latest" | ||||||
|
||||||
| "jiti": "latest" | |
| "jiti": "^2.6.1" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ import { | |||||||||||||||||
| HttpRequest, | ||||||||||||||||||
| HttpResponse, | ||||||||||||||||||
| SSLApp, | ||||||||||||||||||
| WebSocket, | ||||||||||||||||||
| us_listen_socket, | ||||||||||||||||||
| us_socket_context_t, | ||||||||||||||||||
| } from 'uWebSockets.js' | ||||||||||||||||||
|
Comment on lines
+7
to
10
|
||||||||||||||||||
| WebSocket, | |
| us_listen_socket, | |
| us_socket_context_t, | |
| } from 'uWebSockets.js' | |
| us_listen_socket, | |
| us_socket_context_t, | |
| } from 'uWebSockets.js' | |
| import type { WebSocket } from 'uWebSockets.js' |
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The as MessageHandler casts here weaken type-safety and can hide mismatches between ClientMessageType and the handler payload types. Consider typing the handler map as a discriminated/mapped type keyed by ClientMessageType (or using a switch in onMessage) so handlers can accept their specific message shapes without assertions.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These casts to MessageHandler remove compile-time guarantees that the handler matches the message type. A typed handler map keyed by ClientMessageType would keep this safe without as assertions.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as above: the as MessageHandler casts hide mismatches between the message discriminator and the handler signature. Prefer a typed mapping (or narrowing in onMessage) so the compiler enforces correct handler/message pairing.
Copilot
AI
Feb 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onConnect still says it will respond to the client when rate-limited, but now it just calls ws.close() with no close code/reason. If you want clients to handle this, send a meaningful WebSocket close code/reason (valid WS codes are 1000–4999) or reject the upgrade with an HTTP status before upgrading.
| return ws.close() | |
| return ws.close(1013, 'Rate limit exceeded') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI installs pnpm with
version: latest, which can introduce unexpected breakages when pnpm releases new majors/minors. Pin pnpm to a known-good version (or at least a major) to keep CI reproducible with the checked-in lockfile.