Skip to content

Commit 6e4a2c3

Browse files
authored
Undici 8'e geç ve PR kontrollerini hızlandır (#74)
## Özet - Undici `7.29.1` → `8.10.2` ve override aynı sürüme taşındı. - Undici 8'in global dispatcher sembol değişimi için test helper'ı major-sürüm bağımsız hale getirildi. - Node'un yerleşik `fetch`'inin npm Undici dispatcher'ını gerçekten kullandığını `MockAgent` ile ölçen regresyon eklendi. - Pull Request Check, production deploy'daki eksiksiz D1 split'lerini `core / publication / platform` olarak paralel çalıştırıyor; frontend doğrulaması da ayrı runner'da. - Branch protection için mevcut `verify` check adı fail-closed aggregator olarak korunuyor. - Foundation PR workflow'u artık ana PR workflow'unun zaten yaptığı D1/check/production-live işlerini tekrarlamıyor; yalnız generic/staging/dark-launch bundle kontrollerini ekliyor. Manuel foundation koşuları eski tam tanıyı koruyor. ## Neden Önceki dependency PR'ında `Pull Request Check` 2m53s, `Orbit V6 Foundation Check` 3m08s sürdü ve ikisi aynı ağır D1/test/build işlerinin önemli bölümünü tekrar ediyordu. Production deploy zaten D1 paketini eksiksiz üç bölüme ayırıp ayrı runner'larda çalıştırıyor; PR yolu da aynı doğrulanmış partition'ı kullanabilir. ## Doğrulama - temiz `npm ci` - `npm audit` — 0 vulnerability - `npm run actions:scope:test` — 5/5 - Undici dispatcher/port regresyonu — 4/4 - PR dilimleri aynı anda yerelde çalıştırıldı: - core: 246/246 + JS 11/11 + Python 7/7 - publication: 67/67 - platform: 29/29 - frontend: check 213 dosya 0/0/0, content 63, site integrity 3463, browser 855, production config 85, live bundle dry-run başarılı - foundation'a özgü `worker:build`, staging ve production dark-launch dry-run'larının üçü de başarılı Bu PR'ın GitHub Actions koşumu, paralelleştirmenin gerçek ayrı-runner duvar süresini ölçecek.
1 parent 2e70dd7 commit 6e4a2c3

8 files changed

Lines changed: 191 additions & 50 deletions

File tree

‎.github/workflows/pull-request.yml‎

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ concurrency:
1313
group: pull-request-${{ github.event.pull_request.number || github.ref }}
1414
cancel-in-progress: true
1515

16+
# Reuse the production deploy's exhaustive D1 partition instead of running the
17+
# full D1 suite serially before every other check. The final verify job keeps
18+
# the stable branch-protection check name and fails unless every slice passes.
1619
jobs:
17-
verify:
20+
frontend:
1821
runs-on: ubuntu-latest
19-
timeout-minutes: 25
20-
22+
timeout-minutes: 12
2123
steps:
2224
- name: Checkout
2325
uses: actions/checkout@v7
@@ -31,13 +33,86 @@ jobs:
3133
- name: Install locked dependencies
3234
run: npm ci
3335

34-
- name: Verify types, content, API, D1 and browser behavior
36+
- name: Verify scope classifier and production frontend
3537
run: |
3638
npm run actions:scope:test
37-
npm run check
38-
npm run build
39+
npm run verify:frontend:production
40+
41+
backend:
42+
runs-on: ubuntu-latest
43+
timeout-minutes: 12
44+
steps:
45+
- name: Checkout
46+
uses: actions/checkout@v7
47+
48+
- name: Set up Node
49+
uses: actions/setup-node@v7
50+
with:
51+
node-version: 24
52+
cache: npm
53+
54+
- name: Install locked dependencies
55+
run: npm ci
56+
57+
- name: Verify D1 core and reference clients
58+
run: npm run test:d1:core && npm run orbit:test:clients
59+
60+
backend-publication:
61+
runs-on: ubuntu-latest
62+
timeout-minutes: 12
63+
steps:
64+
- name: Checkout
65+
uses: actions/checkout@v7
66+
67+
- name: Set up Node
68+
uses: actions/setup-node@v7
69+
with:
70+
node-version: 24
71+
cache: npm
72+
73+
- name: Install locked dependencies
74+
run: npm ci
75+
76+
- name: Verify publication, renderer and style behavior
77+
run: npm run test:d1:publication
3978

40-
- name: Verify production configuration and Worker bundle
79+
backend-platform:
80+
runs-on: ubuntu-latest
81+
timeout-minutes: 12
82+
steps:
83+
- name: Checkout
84+
uses: actions/checkout@v7
85+
86+
- name: Set up Node
87+
uses: actions/setup-node@v7
88+
with:
89+
node-version: 24
90+
cache: npm
91+
92+
- name: Install locked dependencies
93+
run: npm ci
94+
95+
- name: Verify dashboard, media and platform behavior
96+
run: npm run test:d1:platform
97+
98+
verify:
99+
needs:
100+
- frontend
101+
- backend
102+
- backend-publication
103+
- backend-platform
104+
if: ${{ always() }}
105+
runs-on: ubuntu-latest
106+
timeout-minutes: 3
107+
steps:
108+
- name: Require every parallel verification slice
109+
env:
110+
FRONTEND_RESULT: ${{ needs.frontend.result }}
111+
BACKEND_RESULT: ${{ needs.backend.result }}
112+
PUBLICATION_RESULT: ${{ needs.backend-publication.result }}
113+
PLATFORM_RESULT: ${{ needs.backend-platform.result }}
41114
run: |
42-
npm run production:config:check
43-
npm run worker:build:production:live
115+
test "$FRONTEND_RESULT" = success
116+
test "$BACKEND_RESULT" = success
117+
test "$PUBLICATION_RESULT" = success
118+
test "$PLATFORM_RESULT" = success

‎.github/workflows/v6-foundation-check.yml‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
name: Orbit V6 Foundation Check
22

3-
# Bu iş akışı bir zamanlar `v6/server-platform` geliştirme dalına da push
4-
# tetikleyicisiyle bağlıydı. O dal birleşip emekli olduğundan tetikleyici
5-
# hiçbir şeyi çalıştırmıyordu; kaldırıldı. Altyapıya dokunan PR'lar ve elle
6-
# tetikleme kalıyor — main'e push'ta üretim doğrulamasını deploy-production
7-
# iş akışı yapar.
3+
# Pull Request Check already validates the complete D1 split, source checks
4+
# and production-live artifact. This workflow is the extra deployment-mode
5+
# guard for foundation-sensitive PRs. Manual runs retain full diagnostics.
86
on:
97
pull_request:
108
branches:
@@ -49,17 +47,19 @@ jobs:
4947
- name: Install dependencies
5048
run: npm ci
5149

52-
- name: Verify D1 foundation and Worker build
53-
run: npm run v6:check
50+
- name: Verify generic Worker bundle
51+
run: npm run worker:build
5452

5553
- name: Verify isolated staging bundle
5654
run: npm run worker:build:staging
5755

58-
- name: Verify production config parity
59-
run: npm run production:config:check
60-
61-
- name: Verify production live bundle without deployment
62-
run: npm run worker:build:production:live
63-
6456
- name: Verify production dark-launch rollback bundle without deployment
6557
run: npm run worker:build:production:dark-launch
58+
59+
- name: Run standalone full foundation diagnostics
60+
if: ${{ github.event_name == 'workflow_dispatch' }}
61+
run: |
62+
npm run test:d1
63+
npm run check
64+
npm run production:config:check
65+
npm run worker:build:production:live

‎docs/V6_PROJECT_LEDGER.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2305,3 +2305,25 @@ Anything under `docs/archive/` describes an earlier state and is frozen. Read it
23052305
`site.tokens_issued`, `site.grant_revoked`, then a fresh consent and issue —
23062306
in that order. Worth knowing before anyone reads a live grant row as proof
23072307
that access was never withdrawn.
2308+
2309+
### 2026-09-18 — Undici 8 and PR CI de-duplication
2310+
2311+
- Upgraded the test-only direct Undici dependency and override from `7.29.1`
2312+
to `8.10.2`. Orbit's deployed Worker still does not ship Undici; it remains
2313+
Node-side test/build tooling.
2314+
- Undici 8 moved the npm package's global dispatcher slot from the historical
2315+
`.1`/`.2` aliasing to `.2` while Node 26 still retained `.1`. The old guard
2316+
treated every visible historical slot as authoritative and would therefore
2317+
reject a valid upgrade. The guard now verifies the package dispatcher plus
2318+
at least one live global slot, and a MockAgent regression proves that Node's
2319+
built-in `fetch` actually honors the dispatcher installed by the npm package.
2320+
- PR validation previously ran the full 341-test D1 suite serially in
2321+
`pull-request.yml` while `v6-foundation-check.yml` repeated the same D1,
2322+
source and production-live work for package/foundation changes. The PR path
2323+
now reuses the production deploy's exhaustive core/publication/platform
2324+
partition in parallel with the production frontend verifier, then preserves
2325+
the stable `verify` check as a fail-closed aggregator.
2326+
- The foundation PR workflow now owns only the extra generic, staging and
2327+
dark-launch bundle checks. Manual foundation runs still execute the full
2328+
historical diagnostics. This removes duplicate CI work without deleting any
2329+
validation surface.

‎package-lock.json‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@
7777
"sharp": "^0.35.4",
7878
"tsx": "4.23.13",
7979
"typescript": "6.0.3",
80-
"undici": "^7.29.1",
80+
"undici": "^8.10.2",
8181
"wrangler": "4.135.0"
8282
},
8383
"overrides": {
84-
"undici": "^7.29.1"
84+
"undici": "^8.10.2"
8585
}
8686
}

‎scripts/orbit-actions-scope-tests.mjs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,22 @@ test('every D1 test file the full suite runs is also in exactly one split', asyn
7373
`${name} is not run by the production deploy workflow`,
7474
);
7575
}
76+
77+
/* PR doğrulaması da aynı eksiksiz bölünmeyi paralel çalıştırıyor. Deploy
78+
* listesi doğru kalıp PR listesi sessizce eksilirse merge öncesi kapı
79+
* zayıflar; bunu ayrı bir sözleşme olarak kilitle. */
80+
const pullRequestWorkflow = await readFile(
81+
new URL('../.github/workflows/pull-request.yml', import.meta.url),
82+
'utf8',
83+
);
84+
for (const name of splitNames) {
85+
assert.ok(
86+
pullRequestWorkflow.includes(`npm run ${name}`),
87+
`${name} is not run by the pull-request workflow`,
88+
);
89+
}
90+
assert.ok(
91+
pullRequestWorkflow.includes('npm run verify:frontend:production'),
92+
'production frontend verifier is not run by the pull-request workflow',
93+
);
7694
});

‎scripts/orbit-test-ports-tests.ts‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { spawn } from 'node:child_process';
33
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
44
import path from 'node:path';
55
import { after, describe, test } from 'node:test';
6+
import { getGlobalDispatcher, MockAgent, setGlobalDispatcher } from 'undici';
67
import { claimPort, releasePort, reserveWorkerPorts, RESERVATION_DIRECTORY } from './orbit-test-ports';
78

89
/* Bu dosyanın ölçtüğü şey bir gecelik regresyon hatası.
@@ -58,6 +59,28 @@ async function holdPortsInAChildProcess(): Promise<{
5859
return { ports, stop: () => { child.stdin.write('\n'); child.kill('SIGTERM'); } };
5960
}
6061

62+
describe('test HTTP dispatcher compatibility', () => {
63+
test('Node global fetch honors the installed undici dispatcher', async () => {
64+
const previous = getGlobalDispatcher();
65+
const mock = new MockAgent();
66+
mock.disableNetConnect();
67+
mock
68+
.get('http://orbit-undici-dispatcher.invalid')
69+
.intercept({ path: '/probe', method: 'GET' })
70+
.reply(200, 'orbit-dispatcher-ok');
71+
72+
setGlobalDispatcher(mock);
73+
try {
74+
const response = await globalThis.fetch('http://orbit-undici-dispatcher.invalid/probe');
75+
assert.equal(response.status, 200);
76+
assert.equal(await response.text(), 'orbit-dispatcher-ok');
77+
} finally {
78+
setGlobalDispatcher(previous);
79+
await mock.close();
80+
}
81+
});
82+
});
83+
6184
describe('test worker port reservation', () => {
6285
test('concurrent processes never receive the same port', async () => {
6386
/* Yarışın gerçek şekli bu: aynı anda başlayan süreçler.

‎scripts/support/test-http.ts‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@
3030
* kendi bağlantısını açar ve yarış ortadan kalkar. Yerelde birkaç düzine
3131
* istek için maliyeti ölçülemeyecek kadar küçük.
3232
*/
33-
import { Agent, setGlobalDispatcher } from 'undici';
33+
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
3434
import type { Dispatcher } from 'undici';
3535

3636
let installed = false;
3737

38-
/* Node'un global `fetch`'i dispatcher'ı `Symbol.for('undici.globalDispatcher.1')`
39-
* üzerinden okuyor; undici paketinin `setGlobalDispatcher`'ı aynı sembole
40-
* yazdığı için yerleşik fetch de bu ayarı görüyor. */
38+
/* Node'un global `fetch`'i ile npm'den kurulu undici aynı dispatcher
39+
* sözleşmesini paylaşır. Sembolün sürüm eki undici major'ları arasında
40+
* değişebilir; bu yüzden aşağıdaki bekçi tek bir tarihsel sembole bağlanmaz.
41+
* Bu sözleşmenin gerçekten Node `fetch`'ine ulaştığı ayrıca
42+
* orbit-test-ports-tests.ts içindeki MockAgent regresyonuyla ölçülür. */
4143
export function useFreshConnectionPerRequest(): void {
4244
if (installed) return;
4345
const agent = new Agent({ pipelining: 0 });
@@ -48,31 +50,32 @@ export function useFreshConnectionPerRequest(): void {
4850

4951
/* Bekçi: ayarın gerçekten yerleşip yerleşmediğini kontrol eder.
5052
*
51-
* `setGlobalDispatcher` global bir sembole yazıyor ve Node'un yerleşik
52-
* `fetch`'i onu okuyor. Bugün (Node 26 / undici 7) iki sembol de —
53-
* `undici.globalDispatcher.1` ve `.2` — aynı nesneyi gösteriyor. Yarın
54-
* paket ile Node'un sembolleri ayrışırsa çağrı sessizce hiçbir şey yapmaz:
55-
* keep-alive geri gelir, flake geri gelir ve hiçbir test kırılmaz, çünkü
56-
* bu yalnızca ara sıra düşen bir yarışı geri açar.
53+
* Undici 7 aynı dispatcher'ı hem `.1` hem `.2` sembolünde görüyordu;
54+
* undici 8 ile eski `.1` slotu Node tarafından tutulurken yeni paket `.2`
55+
* slotunu kullanıyor. Bütün tarihsel slotların aynı nesne olmasını istemek
56+
* geçerli major yükseltmesini yanlış negatifle durdurur.
5757
*
58-
* Sessiz bozulmayı gürültülü hataya çeviriyoruz: yerleşmediyse testler
59-
* daha ilk satırda dursun. */
58+
* Bunun yerine paket API'sinin beklenen dispatcher'ı döndürmesini ve bilinen
59+
* global slotlardan en az birinin aynı nesneyi taşımasını istiyoruz. Node'un
60+
* yerleşik fetch'inin de bu dispatcher'ı kullandığı ayrı regresyon testinde
61+
* davranışsal olarak doğrulanıyor. */
6062
function assertDispatcherInstalled(expected: Dispatcher): void {
61-
const seen = [
63+
const symbols = [
6264
Symbol.for('undici.globalDispatcher.1'),
6365
Symbol.for('undici.globalDispatcher.2'),
64-
].filter((symbol) => symbol in globalThis);
65-
66-
const missed = seen.filter(
67-
(symbol) => (globalThis as Record<symbol, unknown>)[symbol] !== expected,
66+
Symbol.for('undici.globalDispatcher.3'),
67+
];
68+
const seen = symbols.filter((symbol) => symbol in globalThis);
69+
const matching = seen.filter(
70+
(symbol) => (globalThis as Record<symbol, unknown>)[symbol] === expected,
6871
);
6972

70-
if (seen.length === 0 || missed.length > 0) {
73+
if (getGlobalDispatcher() !== expected || matching.length === 0) {
7174
throw new Error(
7275
'Test fetch bağlantı politikası yerleşmedi: undici setGlobalDispatcher ' +
73-
"Node'un yerleşik fetch'inin okuduğu sembole yazmıyor. Keep-alive " +
74-
'yeniden kullanımı geri döner ve UND_ERR_SOCKET flake\'i yeniden ' +
75-
'başlar. support/test-http.ts içindeki gerekçeye bak; undici ve Node ' +
76+
"beklenen global dispatcher'ı kurmadı. Keep-alive yeniden kullanımı " +
77+
"geri döner ve UND_ERR_SOCKET flake'i yeniden başlar. " +
78+
'support/test-http.ts içindeki gerekçeye bak; undici ve Node ' +
7679
'sürümlerinin uyumunu kontrol et.',
7780
);
7881
}

0 commit comments

Comments
 (0)