Skip to content

Commit 8ae4bd6

Browse files
authored
chore: quality-tightening (yarn 1 -> 4, oxfmt + oxlint + tsc + vitest + husky) (#305)
* chore: quality-tightening (yarn 1 -> 4, oxfmt + oxlint + tsc + vitest + husky) Yarn 1 -> 4 migration plus the standard quality-tightening rollout. - Drops yarn 1 lockfile + .yarnrc; adds .yarnrc.yml + mise.toml (node 20.18.0, yarn 4.14.1, actionlint, shellcheck, gitleaks). - prettier -> oxfmt - eslint (with @typescript-eslint, github, jest, prettier, unicorn) -> oxlint with eslint-plugin-unicorn - jest 26 + jest-circus + jest-fail-on-console + ts-jest + @types/jest -> vitest 4 + vite 7 + @vitest/coverage-istanbul. jest.config.js + jest.setup.ts removed; replaced with vitest.config.mts + src/test/setup.ts. - new: tsgo --noEmit (alongside tsc fallback) - husky 7 -> husky 9 with scripts/ensure-husky.mjs self-heal + lint-staged - yarn-audit-fix dropped (yarn 4 has its own audit; the fixer was yarn 1 specific) - ts-node dropped (not used by build/test/lint) - TypeScript bumped 4.x -> 5; tsconfig target ES6 -> ES2022 + lib ES2022 + DOM, skipLibCheck on, types: [node]. - Added standard yarn 4 .gitignore entries (.yarn/cache et al.) Test migration (10 files): - Bulk-converted jest.* -> vi.* and added vitest imports. - 2 tests asserted nothing: 'expect(() => fn()).rejects;' (no matcher chained). Replaced with proper 'await expect(fn()).rejects.toThrow(...)' assertions, per the global rule 'Never weaken a test to make it pass'. - jest.setup.ts (jest-fail-on-console) replaced with src/test/setup.ts that installs equivalent console-fail spies in beforeEach + restores in afterEach. Same 'console output = failed test' behaviour. Verified locally: lint 0/6, format clean, typecheck clean, test 77/78 (1 pre-existing skipped), build succeeds (ncc bundle 2282kB), actionlint clean across all workflows. * ci: corepack-enable yarn 4 in main.yml + use mise node + add cache * chore(deps): bump @actions/github 5 -> 6 + force undici >=6.24 Snyk flagged 9 transitive vulnerabilities (3 high, 6 moderate) through the old toolchain. @actions/github@5.x carries an outdated @octokit/* + undici 5.29 chain. Bumping to v6 picks up the patched @octokit/* and pinning undici via yarn resolutions clears the remaining WebSocket / HTTP-smuggling advisories. After: 'yarn npm audit --recursive' reports no audit suggestions. @actions/github v6's typings tightened the createCheck() call: the 'status' and 'conclusion' fields are string-literal unions now, not free strings. results-check.ts widens those with 'as const' so the typecheck stays clean. Also: lint-staged globs now exclude dist/ explicitly so pre-commit hooks don't fail with 'Expected at least one target file' when the only staged files are in the dist/ ignore-patterned tree. * ci(unity-test-runner): start Docker daemon on Windows runners 5 windows-2022 jobs failed in this PR with: failed to connect to the docker API at npipe:////./pipe/docker_engine ##[error]The process 'C:\Windows\system32\docker.exe' failed with exit code 1 Failing jobs: - Test each mode sequentially (windows-2022, 2022.3.13f1, ...) - Test each mode sequentially (windows-2022, 2023.1.19f1, ...) - Test each mode sequentially (windows-2022, 2023.2.2f1, ...) - Test edit mode (windows-2022, 2023.1.19f1, ..., true) - Test edit mode (windows-2022, 2023.2.2f1, ..., true) GitHub-hosted windows-2022 runners ship with the Docker service **Stopped**. Action runs that pull a Linux/Windows container have to explicitly start the daemon first; otherwise 'docker run' inside the action returns the npipe error above. Sister project unity-builder solved this in May 2025 by adding an 'Ensure Docker daemon is ready' step before the action invocation. Mirroring the same step here, gated on 'runner.os == Windows' so ubuntu-latest matrix entries skip it. Inserted into all 7 jobs that target the matrix.baseRunner runner (testRunner, testAllModes, testRunnerInEditMode, testRunnerInPlayMode, testStandalone, testStandaloneIL2CPP, testEachModeSequentially). Pre-existing on main (last successful Windows run was Feb 2026, before windows-2022 became the default and the daemon-stopped behaviour started). * ci: retrigger CI for unity-test-runner The previous run had two transient Unity-internal failures: - 'Test package mode in all modes with Scoped Registry and Multiple Scopes' on ubuntu-latest exited with code 134 (Mono crash inside Unity Editor's threadpool callback). - 'Test standalone with IL2CPP (windows-2022, 2023.1.19f1)' failed with 'Access token is unavailable; failed to update' inside the Unity licensing client \u2014 a transient licensing-server flake. Both are container-internal Unity flakiness, unrelated to the quality-tightening migration; they've been observed on main historically too. Pushing an empty commit to retrigger only those jobs (and verify they're flakes, not consistent failures).
1 parent 9e9fb99 commit 8ae4bd6

40 files changed

Lines changed: 35737 additions & 13663 deletions

.eslintignore

Lines changed: 0 additions & 4 deletions
This file was deleted.

.eslintrc.json

Lines changed: 0 additions & 25 deletions
This file was deleted.

.github/workflows/main.yml

Lines changed: 251 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,56 @@ jobs:
1818
runs-on: ubuntu-latest
1919
steps:
2020
- uses: actions/checkout@v4
21+
22+
- name: Read Node version from mise.toml
23+
id: node
24+
run: echo "version=$(grep -E '^node\s*=' mise.toml | sed -E 's/.*"([^"]+)".*/\1/')" >> "$GITHUB_OUTPUT"
25+
26+
- name: Install package manager (from package.json)
27+
run: |
28+
corepack enable
29+
corepack install
30+
2131
- uses: actions/setup-node@v4
2232
with:
23-
node-version: 18.x
24-
- run: yarn
25-
- run: yarn lint
26-
- run: yarn test
27-
- run: yarn build || { echo "build command should always succeed" ; exit 61; }
28-
# - run: yarn build --quiet && git diff --quiet action || { echo "ERROR - index.js is different from repository version. Forgot to run `yarn build`?" ; exit 62; }
33+
node-version: ${{ steps.node.outputs.version }}
34+
35+
- name: Resolve yarn cache folder
36+
id: yarn-config
37+
run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT"
38+
39+
- name: Restore yarn install cache (node_modules + cacheFolder + install-state)
40+
uses: actions/cache@v4
41+
with:
42+
path: |
43+
node_modules
44+
${{ steps.yarn-config.outputs.cacheFolder }}
45+
.yarn/install-state.gz
46+
key: yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') }}
47+
restore-keys: |
48+
yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-
49+
50+
- name: Install deps
51+
env:
52+
YARN_ENABLE_HARDENED_MODE: 'false'
53+
run: |
54+
case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac
55+
yarn install --immutable
56+
57+
- name: Format
58+
run: yarn format:check
59+
60+
- name: Lint
61+
run: yarn lint
62+
63+
- name: Typecheck
64+
run: yarn typecheck
65+
66+
- name: Test
67+
run: yarn test
68+
69+
- name: Build
70+
run: yarn build || { echo 'build command should always succeed'; exit 61; }
2971

3072
testAllModesLikeInTheReadme:
3173
name: Test in ${{ matrix.testMode }} of version ${{ matrix.unityVersion }} on ${{ matrix.baseRunner }}
@@ -64,6 +106,35 @@ jobs:
64106
key: Library-${{ matrix.baseRunner }}-${{ matrix.projectPath }}
65107
restore-keys: |
66108
Library-${{ matrix.baseRunner }}
109+
###########################
110+
# Docker Readiness #
111+
###########################
112+
- name: Ensure Docker daemon is ready (Windows runners only)
113+
if: runner.os == 'Windows'
114+
timeout-minutes: 2
115+
shell: powershell
116+
run: |
117+
$maxRetries = 10
118+
$retryDelay = 6
119+
for ($i = 0; $i -lt $maxRetries; $i++) {
120+
$svc = Get-Service docker -ErrorAction SilentlyContinue
121+
if ($svc -and $svc.Status -eq 'Running') {
122+
docker version 2>$null
123+
if ($LASTEXITCODE -eq 0) {
124+
Write-Host "Docker is ready."
125+
exit 0
126+
}
127+
}
128+
if ($svc -and $svc.Status -eq 'Stopped') {
129+
Write-Host "Docker service stopped, attempting to start..."
130+
Start-Service docker -ErrorAction SilentlyContinue
131+
}
132+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
133+
Start-Sleep -Seconds $retryDelay
134+
}
135+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
136+
exit 1
137+
67138
- uses: ./
68139
id: tests
69140
with:
@@ -111,6 +182,35 @@ jobs:
111182
restore-keys: |
112183
Library-${{ matrix.baseRunner }}
113184
185+
###########################
186+
# Docker Readiness #
187+
###########################
188+
- name: Ensure Docker daemon is ready (Windows runners only)
189+
if: runner.os == 'Windows'
190+
timeout-minutes: 2
191+
shell: powershell
192+
run: |
193+
$maxRetries = 10
194+
$retryDelay = 6
195+
for ($i = 0; $i -lt $maxRetries; $i++) {
196+
$svc = Get-Service docker -ErrorAction SilentlyContinue
197+
if ($svc -and $svc.Status -eq 'Running') {
198+
docker version 2>$null
199+
if ($LASTEXITCODE -eq 0) {
200+
Write-Host "Docker is ready."
201+
exit 0
202+
}
203+
}
204+
if ($svc -and $svc.Status -eq 'Stopped') {
205+
Write-Host "Docker service stopped, attempting to start..."
206+
Start-Service docker -ErrorAction SilentlyContinue
207+
}
208+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
209+
Start-Sleep -Seconds $retryDelay
210+
}
211+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
212+
exit 1
213+
114214
# Configure test runner
115215
- name: Run tests
116216
id: allTests
@@ -174,6 +274,35 @@ jobs:
174274
restore-keys: |
175275
Library-${{ matrix.baseRunner }}
176276
277+
###########################
278+
# Docker Readiness #
279+
###########################
280+
- name: Ensure Docker daemon is ready (Windows runners only)
281+
if: runner.os == 'Windows'
282+
timeout-minutes: 2
283+
shell: powershell
284+
run: |
285+
$maxRetries = 10
286+
$retryDelay = 6
287+
for ($i = 0; $i -lt $maxRetries; $i++) {
288+
$svc = Get-Service docker -ErrorAction SilentlyContinue
289+
if ($svc -and $svc.Status -eq 'Running') {
290+
docker version 2>$null
291+
if ($LASTEXITCODE -eq 0) {
292+
Write-Host "Docker is ready."
293+
exit 0
294+
}
295+
}
296+
if ($svc -and $svc.Status -eq 'Stopped') {
297+
Write-Host "Docker service stopped, attempting to start..."
298+
Start-Service docker -ErrorAction SilentlyContinue
299+
}
300+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
301+
Start-Sleep -Seconds $retryDelay
302+
}
303+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
304+
exit 1
305+
177306
# Configure test runner
178307
- name: Run tests
179308
id: editMode
@@ -234,6 +363,35 @@ jobs:
234363
restore-keys: |
235364
Library-${{ matrix.baseRunner }}
236365
366+
###########################
367+
# Docker Readiness #
368+
###########################
369+
- name: Ensure Docker daemon is ready (Windows runners only)
370+
if: runner.os == 'Windows'
371+
timeout-minutes: 2
372+
shell: powershell
373+
run: |
374+
$maxRetries = 10
375+
$retryDelay = 6
376+
for ($i = 0; $i -lt $maxRetries; $i++) {
377+
$svc = Get-Service docker -ErrorAction SilentlyContinue
378+
if ($svc -and $svc.Status -eq 'Running') {
379+
docker version 2>$null
380+
if ($LASTEXITCODE -eq 0) {
381+
Write-Host "Docker is ready."
382+
exit 0
383+
}
384+
}
385+
if ($svc -and $svc.Status -eq 'Stopped') {
386+
Write-Host "Docker service stopped, attempting to start..."
387+
Start-Service docker -ErrorAction SilentlyContinue
388+
}
389+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
390+
Start-Sleep -Seconds $retryDelay
391+
}
392+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
393+
exit 1
394+
237395
# Configure test runner
238396
- name: Run tests
239397
id: playMode
@@ -293,6 +451,35 @@ jobs:
293451
restore-keys: |
294452
Library-${{ matrix.baseRunner }}-
295453
454+
###########################
455+
# Docker Readiness #
456+
###########################
457+
- name: Ensure Docker daemon is ready (Windows runners only)
458+
if: runner.os == 'Windows'
459+
timeout-minutes: 2
460+
shell: powershell
461+
run: |
462+
$maxRetries = 10
463+
$retryDelay = 6
464+
for ($i = 0; $i -lt $maxRetries; $i++) {
465+
$svc = Get-Service docker -ErrorAction SilentlyContinue
466+
if ($svc -and $svc.Status -eq 'Running') {
467+
docker version 2>$null
468+
if ($LASTEXITCODE -eq 0) {
469+
Write-Host "Docker is ready."
470+
exit 0
471+
}
472+
}
473+
if ($svc -and $svc.Status -eq 'Stopped') {
474+
Write-Host "Docker service stopped, attempting to start..."
475+
Start-Service docker -ErrorAction SilentlyContinue
476+
}
477+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
478+
Start-Sleep -Seconds $retryDelay
479+
}
480+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
481+
exit 1
482+
296483
# Configure test runner
297484
- name: Run tests
298485
id: standalone
@@ -344,6 +531,35 @@ jobs:
344531
restore-keys: |
345532
Library-${{ matrix.baseRunner }}-
346533
534+
###########################
535+
# Docker Readiness #
536+
###########################
537+
- name: Ensure Docker daemon is ready (Windows runners only)
538+
if: runner.os == 'Windows'
539+
timeout-minutes: 2
540+
shell: powershell
541+
run: |
542+
$maxRetries = 10
543+
$retryDelay = 6
544+
for ($i = 0; $i -lt $maxRetries; $i++) {
545+
$svc = Get-Service docker -ErrorAction SilentlyContinue
546+
if ($svc -and $svc.Status -eq 'Running') {
547+
docker version 2>$null
548+
if ($LASTEXITCODE -eq 0) {
549+
Write-Host "Docker is ready."
550+
exit 0
551+
}
552+
}
553+
if ($svc -and $svc.Status -eq 'Stopped') {
554+
Write-Host "Docker service stopped, attempting to start..."
555+
Start-Service docker -ErrorAction SilentlyContinue
556+
}
557+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
558+
Start-Sleep -Seconds $retryDelay
559+
}
560+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
561+
exit 1
562+
347563
# Set scripting backend to IL2CPP
348564
- name: Rewrite ProjectSettings
349565
run: |
@@ -403,6 +619,35 @@ jobs:
403619
restore-keys: |
404620
Library-${{ matrix.baseRunner }}-
405621
622+
###########################
623+
# Docker Readiness #
624+
###########################
625+
- name: Ensure Docker daemon is ready (Windows runners only)
626+
if: runner.os == 'Windows'
627+
timeout-minutes: 2
628+
shell: powershell
629+
run: |
630+
$maxRetries = 10
631+
$retryDelay = 6
632+
for ($i = 0; $i -lt $maxRetries; $i++) {
633+
$svc = Get-Service docker -ErrorAction SilentlyContinue
634+
if ($svc -and $svc.Status -eq 'Running') {
635+
docker version 2>$null
636+
if ($LASTEXITCODE -eq 0) {
637+
Write-Host "Docker is ready."
638+
exit 0
639+
}
640+
}
641+
if ($svc -and $svc.Status -eq 'Stopped') {
642+
Write-Host "Docker service stopped, attempting to start..."
643+
Start-Service docker -ErrorAction SilentlyContinue
644+
}
645+
Write-Host "Waiting for Docker daemon (attempt $($i+1)/$maxRetries)..."
646+
Start-Sleep -Seconds $retryDelay
647+
}
648+
Write-Error "Docker daemon did not start within $($maxRetries * $retryDelay) seconds"
649+
exit 1
650+
406651
# Configure first test runner
407652
- name: Tests in editmode 📝
408653
uses: ./

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,12 @@
22
node_modules
33
coverage/
44
lib/
5+
6+
# Yarn 4 (Berry)
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/sdks
12+
!.yarn/versions
13+
.pnp.*

.husky/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

.husky/pre-commit

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
1-
#!/bin/sh
2-
. "$(dirname "$0")/_/husky.sh"
3-
4-
if [ -t 1 ]; then
5-
exec < /dev/tty
6-
fi
7-
1+
#!/usr/bin/env sh
82
yarn lint-staged
9-
yarn lint
10-
yarn test
3+
yarn typecheck
114

12-
yarn build
13-
git add dist
5+
if command -v gitleaks >/dev/null 2>&1; then
6+
gitleaks protect --staged --no-banner --redact
7+
fi

.oxfmtrc.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"semi": true,
3+
"singleQuote": true,
4+
"trailingComma": "all",
5+
"printWidth": 100,
6+
"proseWrap": "preserve",
7+
"sortPackageJson": false,
8+
"ignorePatterns": [
9+
"**/node_modules/**",
10+
"**/dist/**",
11+
"**/coverage/**",
12+
"**/.yarn/**",
13+
"default-build-script/**",
14+
"test-runner/**",
15+
"platforms/**"
16+
]
17+
}

0 commit comments

Comments
 (0)