ci: add pull-request quality checks

This commit is contained in:
wiiiii123
2026-07-10 05:07:19 +07:00
parent 641d2230a4
commit 4111959364
10 changed files with 75 additions and 45 deletions
+1
View File
@@ -0,0 +1 @@
* text=auto eol=lf
+60
View File
@@ -0,0 +1,60 @@
name: Code Quality
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
quality:
name: Repository quality
runs-on: ubuntu-latest
timeout-minutes: 20
env:
CI: true
steps:
- name: Checkout code
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22'
cache: npm
cache-dependency-path: package-lock.json
- name: Install dependencies
id: install
run: npm ci --ignore-scripts
- name: Typecheck
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npx tsc --noEmit
- name: Lint
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npm run lint
- name: Test
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npm test
# Main currently has known locale-parity debt covered by PR #710. Keep the
# check visible without blocking unrelated PRs; make it required once that
# existing translation PR (or an equivalent fix) lands.
- name: Check translations (advisory)
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
continue-on-error: true
run: npm run i18n:check
+2 -33
View File
@@ -92,42 +92,11 @@
"noWith": "error",
"useGetterReturn": "error"
}
},
"includes": ["**", "**/dist", "**/.eslintrc.cjs", "**", "**/dist", "**/.eslintrc.cjs"]
}
},
"javascript": { "formatter": { "quoteStyle": "double" } },
"css": { "parser": { "tailwindDirectives": true } },
"css": { "parser": { "cssModules": true, "tailwindDirectives": true } },
"overrides": [
{
"includes": ["*.ts", "*.tsx", "*.mts", "*.cts"],
"linter": {
"rules": {
"complexity": { "noArguments": "error" },
"correctness": {
"noConstAssign": "off",
"noGlobalObjectCalls": "off",
"noInvalidBuiltinInstantiation": "off",
"noInvalidConstructorSuper": "off",
"noSetterReturn": "off",
"noUndeclaredVariables": "off",
"noUnreachable": "off",
"noUnreachableSuper": "off"
},
"style": { "useConst": "error" },
"suspicious": {
"noDuplicateClassMembers": "off",
"noDuplicateObjectKeys": "off",
"noDuplicateParameters": "off",
"noFunctionAssign": "off",
"noImportAssign": "off",
"noRedeclare": "off",
"noUnsafeNegation": "off",
"noVar": "error",
"useGetterReturn": "off"
}
}
}
},
{
"includes": ["*.ts", "*.tsx", "*.mts", "*.cts"],
"linter": {
+1 -1
View File
@@ -62,7 +62,7 @@ export function registerAssetHandlers() {
await fs.writeFile(thumbPath, jpegData)
})
// Keep the queue moving even if one fails
thumbGenerationQueue = generation.catch(() => {})
thumbGenerationQueue = generation.catch(() => undefined)
await generation
return { success: true, data: jpegData! }
+3 -2
View File
@@ -18,9 +18,10 @@
"dev": "vite --config vite.config.ts",
"postinstall": "node scripts/postinstall.mjs",
"build": "npm run build:platform-native-helpers && tsc && vite build --config vite.config.ts && npm run normalize:electron-main-cjs && npm run smoke:electron-main-cjs && electron-builder",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"lint": "biome lint .",
"lint:fix": "biome lint --write .",
"format": "biome format --write .",
"format:check": "biome format .",
"preview": "vite preview --config vite.config.ts",
"rebuild:native": "node ./node_modules/@electron/rebuild/lib/cli.js --force --only uiohook-napi",
"build:native-helpers": "node scripts/build-native-helpers.mjs",
+1 -1
View File
@@ -80,7 +80,7 @@ export const SourceSelectorContent = ({
windowSources = [],
selectedSource = "Screen",
loading = false,
onSourceSelect = () => {},
onSourceSelect = () => undefined,
}: Pick<SourceSelectorProps, "screenSources" | "windowSources" | "selectedSource" | "loading" | "onSourceSelect">) => {
const t = useScopedT("launch");
const renderSourceItem = (source: DesktopSource, index: number) => {
@@ -1,8 +1,8 @@
import { createContext, useContext } from "react";
import { createContext, type MouseEvent, useContext } from "react";
interface HudInteractionContextType {
onMouseEnter: () => void;
onMouseLeave: (event: any) => void;
onMouseLeave: (event: MouseEvent<HTMLDivElement>) => void;
}
export const HudInteractionContext = createContext<HudInteractionContextType | null>(null);
@@ -169,7 +169,6 @@ import {
import { clampFocusToStage as clampFocusToStageUtil } from "./videoPlayback/focusUtils";
import {
layoutVideoContent as layoutVideoContentUtil,
scalePreviewBorderRadius,
} from "./videoPlayback/layoutUtils";
import { updateOverlayIndicator } from "./videoPlayback/overlayUtils";
import { createVideoEventHandlers } from "./videoPlayback/videoEventHandlers";
@@ -6,7 +6,7 @@ type WaveformWorkerRequest = {
interface WorkerContext {
onmessage: (e: MessageEvent<WaveformWorkerRequest>) => void;
postMessage: (message: any, transfer?: Transferable[]) => void;
postMessage: (message: unknown, transfer?: Transferable[]) => void;
}
const workerScope = self as unknown as WorkerContext;
+4 -4
View File
@@ -67,7 +67,7 @@ describe("StreamingVideoDecoder local media loading", () => {
const decoder = new StreamingVideoDecoder();
await decoder.loadMetadata("http://127.0.0.1:43123/video?path=%2Ftmp%2Fcapture.mp4");
expect((window as any).electronAPI.readLocalFile).not.toHaveBeenCalled();
expect(window.electronAPI.readLocalFile).not.toHaveBeenCalled();
expect(mockDemuxerLoad).toHaveBeenCalledWith(
"http://127.0.0.1:43123/video?path=%2Ftmp%2Fcapture.mp4",
);
@@ -77,7 +77,7 @@ describe("StreamingVideoDecoder local media loading", () => {
const decoder = new StreamingVideoDecoder();
await decoder.loadMetadata("/tmp/capture.mp4");
expect((window as any).electronAPI.getLocalMediaUrl).toHaveBeenCalledWith(
expect(window.electronAPI.getLocalMediaUrl).toHaveBeenCalledWith(
"/tmp/capture.mp4",
);
expect(mockDemuxerLoad).toHaveBeenCalledWith(
@@ -90,7 +90,7 @@ describe("StreamingVideoDecoder local media loading", () => {
mockDemuxerLoad
.mockRejectedValueOnce(new Error("get_media_info failed: Failed after 3 attempts"))
.mockResolvedValueOnce(undefined);
(window as any).electronAPI.readLocalFile = vi.fn(async () => ({
window.electronAPI.readLocalFile = vi.fn(async () => ({
success: true,
data: new Uint8Array([1, 2, 3]),
}));
@@ -103,7 +103,7 @@ describe("StreamingVideoDecoder local media loading", () => {
"http://127.0.0.1:4321/video?path=%2Ftmp%2Ffallback.mp4",
);
expect(mockDemuxerLoad.mock.calls[1]?.[0]).toBeInstanceOf(File);
expect((window as any).electronAPI.readLocalFile).toHaveBeenCalledWith(
expect(window.electronAPI.readLocalFile).toHaveBeenCalledWith(
"/tmp/fallback.mp4",
);
});