Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 84 additions & 9 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ concurrency:
group: pull-request-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

# Reuse the production deploy's exhaustive D1 partition instead of running the
# full D1 suite serially before every other check. The final verify job keeps
# the stable branch-protection check name and fails unless every slice passes.
jobs:
verify:
frontend:
runs-on: ubuntu-latest
timeout-minutes: 25

timeout-minutes: 12
steps:
- name: Checkout
uses: actions/checkout@v7
Expand All @@ -31,13 +33,86 @@ jobs:
- name: Install locked dependencies
run: npm ci

- name: Verify types, content, API, D1 and browser behavior
- name: Verify scope classifier and production frontend
run: |
npm run actions:scope:test
npm run check
npm run build
npm run verify:frontend:production

backend:
runs-on: ubuntu-latest
timeout-minutes: 12
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Node
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm

- name: Install locked dependencies
run: npm ci

- name: Verify D1 core and reference clients
run: npm run test:d1:core && npm run orbit:test:clients

backend-publication:
runs-on: ubuntu-latest
timeout-minutes: 12
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Node
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm

- name: Install locked dependencies
run: npm ci

- name: Verify publication, renderer and style behavior
run: npm run test:d1:publication

- name: Verify production configuration and Worker bundle
backend-platform:
runs-on: ubuntu-latest
timeout-minutes: 12
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Node
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm

- name: Install locked dependencies
run: npm ci

- name: Verify dashboard, media and platform behavior
run: npm run test:d1:platform

verify:
needs:
- frontend
- backend
- backend-publication
- backend-platform
if: ${{ always() }}
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- name: Require every parallel verification slice
env:
FRONTEND_RESULT: ${{ needs.frontend.result }}
BACKEND_RESULT: ${{ needs.backend.result }}
PUBLICATION_RESULT: ${{ needs.backend-publication.result }}
PLATFORM_RESULT: ${{ needs.backend-platform.result }}
run: |
npm run production:config:check
npm run worker:build:production:live
test "$FRONTEND_RESULT" = success
test "$BACKEND_RESULT" = success
test "$PUBLICATION_RESULT" = success
test "$PLATFORM_RESULT" = success
26 changes: 13 additions & 13 deletions .github/workflows/v6-foundation-check.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
name: Orbit V6 Foundation Check

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

- name: Verify D1 foundation and Worker build
run: npm run v6:check
- name: Verify generic Worker bundle
run: npm run worker:build

- name: Verify isolated staging bundle
run: npm run worker:build:staging

- name: Verify production config parity
run: npm run production:config:check

- name: Verify production live bundle without deployment
run: npm run worker:build:production:live

- name: Verify production dark-launch rollback bundle without deployment
run: npm run worker:build:production:dark-launch

- name: Run standalone full foundation diagnostics
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
npm run test:d1
npm run check
npm run production:config:check
npm run worker:build:production:live
22 changes: 22 additions & 0 deletions docs/V6_PROJECT_LEDGER.md
Original file line number Diff line number Diff line change
Expand Up @@ -2305,3 +2305,25 @@ Anything under `docs/archive/` describes an earlier state and is frozen. Read it
`site.tokens_issued`, `site.grant_revoked`, then a fresh consent and issue —
in that order. Worth knowing before anyone reads a live grant row as proof
that access was never withdrawn.

### 2026-09-18 — Undici 8 and PR CI de-duplication

- Upgraded the test-only direct Undici dependency and override from `7.29.1`
to `8.10.2`. Orbit's deployed Worker still does not ship Undici; it remains
Node-side test/build tooling.
- Undici 8 moved the npm package's global dispatcher slot from the historical
`.1`/`.2` aliasing to `.2` while Node 26 still retained `.1`. The old guard
treated every visible historical slot as authoritative and would therefore
reject a valid upgrade. The guard now verifies the package dispatcher plus
at least one live global slot, and a MockAgent regression proves that Node's
built-in `fetch` actually honors the dispatcher installed by the npm package.
- PR validation previously ran the full 341-test D1 suite serially in
`pull-request.yml` while `v6-foundation-check.yml` repeated the same D1,
source and production-live work for package/foundation changes. The PR path
now reuses the production deploy's exhaustive core/publication/platform
partition in parallel with the production frontend verifier, then preserves
the stable `verify` check as a fail-closed aggregator.
- The foundation PR workflow now owns only the extra generic, staging and
dark-launch bundle checks. Manual foundation runs still execute the full
historical diagnostics. This removes duplicate CI work without deleting any
validation surface.
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@
"sharp": "^0.35.4",
"tsx": "4.23.13",
"typescript": "6.0.3",
"undici": "^7.29.1",
"undici": "^8.10.2",
"wrangler": "4.135.0"
},
"overrides": {
"undici": "^7.29.1"
"undici": "^8.10.2"
}
}
18 changes: 18 additions & 0 deletions scripts/orbit-actions-scope-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,22 @@ test('every D1 test file the full suite runs is also in exactly one split', asyn
`${name} is not run by the production deploy workflow`,
);
}

/* PR doğrulaması da aynı eksiksiz bölünmeyi paralel çalıştırıyor. Deploy
* listesi doğru kalıp PR listesi sessizce eksilirse merge öncesi kapı
* zayıflar; bunu ayrı bir sözleşme olarak kilitle. */
const pullRequestWorkflow = await readFile(
new URL('../.github/workflows/pull-request.yml', import.meta.url),
'utf8',
);
for (const name of splitNames) {
assert.ok(
pullRequestWorkflow.includes(`npm run ${name}`),
`${name} is not run by the pull-request workflow`,
);
}
assert.ok(
pullRequestWorkflow.includes('npm run verify:frontend:production'),
'production frontend verifier is not run by the pull-request workflow',
);
});
23 changes: 23 additions & 0 deletions scripts/orbit-test-ports-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { spawn } from 'node:child_process';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { after, describe, test } from 'node:test';
import { getGlobalDispatcher, MockAgent, setGlobalDispatcher } from 'undici';
import { claimPort, releasePort, reserveWorkerPorts, RESERVATION_DIRECTORY } from './orbit-test-ports';

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

describe('test HTTP dispatcher compatibility', () => {
test('Node global fetch honors the installed undici dispatcher', async () => {
const previous = getGlobalDispatcher();
const mock = new MockAgent();
mock.disableNetConnect();
mock
.get('http://orbit-undici-dispatcher.invalid')
.intercept({ path: '/probe', method: 'GET' })
.reply(200, 'orbit-dispatcher-ok');

setGlobalDispatcher(mock);
try {
const response = await globalThis.fetch('http://orbit-undici-dispatcher.invalid/probe');
assert.equal(response.status, 200);
assert.equal(await response.text(), 'orbit-dispatcher-ok');
} finally {
setGlobalDispatcher(previous);
await mock.close();
}
});
});

describe('test worker port reservation', () => {
test('concurrent processes never receive the same port', async () => {
/* Yarışın gerçek şekli bu: aynı anda başlayan süreçler.
Expand Down
45 changes: 24 additions & 21 deletions scripts/support/test-http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@
* kendi bağlantısını açar ve yarış ortadan kalkar. Yerelde birkaç düzine
* istek için maliyeti ölçülemeyecek kadar küçük.
*/
import { Agent, setGlobalDispatcher } from 'undici';
import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
import type { Dispatcher } from 'undici';

let installed = false;

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

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

const missed = seen.filter(
(symbol) => (globalThis as Record<symbol, unknown>)[symbol] !== expected,
Symbol.for('undici.globalDispatcher.3'),
];
const seen = symbols.filter((symbol) => symbol in globalThis);
const matching = seen.filter(
(symbol) => (globalThis as Record<symbol, unknown>)[symbol] === expected,
);

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