From 459cfd2f14766ee2465de06f616e5646b6c24043 Mon Sep 17 00:00:00 2001 From: k3mpaxl <> Date: Wed, 7 Oct 2026 08:23:16 +0200 Subject: [PATCH 1/2] CI on Node 24 actions; Node.js 26 for CI and the frontend image GitHub warns on every job that the actions target the deprecated Node.js 20 runtime (forced to Node 24). All actions move to their current major versions, which run on Node 24: checkout, setup-python, setup-node and upload-artifact v7; docker setup-buildx, setup-qemu and login v4, build-push v7, metadata v6. None of the breaking changes of the skipped majors affects the inputs these workflows use. CI and the Docker frontend stage use Node.js 26 (current release, LTS from late October 2026) instead of 22. The documented minimum stays Node.js 20+. The runner stays ubuntu-latest. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/docker-publish.yml | 12 ++++++------ README.md | 2 +- deploy/Dockerfile | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c6af4d..2447953 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.14' cache: pip @@ -28,9 +28,9 @@ jobs: run: python -m unittest discover -s tests -v - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: - node-version: '22' + node-version: '26' cache: npm cache-dependency-path: web/package-lock.json @@ -52,7 +52,7 @@ jobs: - name: Upload browser diagnostics on failure if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: browser-test-results path: web/test-results @@ -62,13 +62,13 @@ jobs: needs: test-and-build steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: deploy/Dockerfile diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 95acb04..3a14076 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -14,23 +14,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Log in to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Docker metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: docker.io/${{ secrets.DOCKERHUB_USERNAME }}/factgraph tags: | @@ -40,7 +40,7 @@ jobs: type=sha - name: Build and push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . file: deploy/Dockerfile diff --git a/README.md b/README.md index d804f32..87e9f9a 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ With Docker Compose from the repository: docker compose -f deploy/compose.yaml up --build ``` -Directly with Python 3.11+ (the Docker image and CI use 3.14) and Node.js 20+: +Directly with Python 3.11+ and Node.js 20+ (the Docker image and CI use Python 3.14 and Node.js 26): ```bash python3 -m venv .venv diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 0527733..668e05d 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -1,5 +1,5 @@ # The frontend bundle is platform independent: build it natively instead of under arm64 emulation. -FROM --platform=$BUILDPLATFORM node:22-alpine AS frontend +FROM --platform=$BUILDPLATFORM node:26-alpine AS frontend WORKDIR /build/web COPY web/package.json web/package-lock.json ./ RUN npm ci From 7cf4b10203caaa95ab10e9f9f65caf31348df110 Mon Sep 17 00:00:00 2001 From: k3mpaxl <> Date: Wed, 7 Oct 2026 08:32:50 +0200 Subject: [PATCH 2/2] Give the 10,000-row import test call 60 s The import of 10,000 rows through REST is stored by the browser in 50 acknowledged chunks; on shared CI runners that sometimes takes longer than the 10 s action timeout every API call inherits (failed on 6 and 7 October, passed in the parallel runs). Only this call gets 60 s; the test's own limit stays 120 s. Co-Authored-By: Claude Opus 5.5 --- web/e2e/workflows.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/e2e/workflows.spec.ts b/web/e2e/workflows.spec.ts index bfe5b6b..ef704ea 100644 --- a/web/e2e/workflows.spec.ts +++ b/web/e2e/workflows.spec.ts @@ -15,8 +15,9 @@ async function setup(page:Page,request:APIRequestContext) { const id=randomUUID();await page.goto(`/boards/${id}`) await expect(page.getByText('1 online',{exact:true})).toBeVisible() const token=await page.evaluate(id=>sessionStorage.getItem(`factgraph:sessionToken:${id}`)!,id) - const call=async(method:string,path:string,body?:object)=>{ - const result=await request.fetch(`${base}/api/boards/${id}${path}`,{method,data:body,headers:{'X-FactGraph-Token':token}}) + // timeout: for the few calls that legitimately take longer than an action (large imports); default actionTimeout. + const call=async(method:string,path:string,body?:object,timeout?:number)=>{ + const result=await request.fetch(`${base}/api/boards/${id}${path}`,{method,data:body,headers:{'X-FactGraph-Token':token},...(timeout?{timeout}:{})}) expect(result.ok(),await result.text()).toBeTruthy();return result.json() } return {id,token,call,graph:()=>call('GET','/graph')} @@ -219,7 +220,8 @@ test('layout preserves pinned positions; browser back navigates views without un test('large import retains 10000 evidence rows and renders a 1000-node graph',async({page,request})=>{ const {call,graph}=await setup(page,request) const rows=Array.from({length:10000},(_,i)=>({EventId:String(i),IPAddress:'10.0.0.5',FilePath:`repo/file-${i%999}`,TimeGenerated:'2026-09-28T10:00:00Z'})) - await call('POST','/imports/kql',{query:'AccessLogs',rows,title:'Large result'}) + // 10,000 rows: the browser stores them in 50 acknowledged chunks, which on shared CI runners can take longer than 10 s. + await call('POST','/imports/kql',{query:'AccessLogs',rows,title:'Large result'},60_000) const data=await graph() expect(data.entities).toHaveLength(1000) expect(data.facts.flatMap((f:any)=>f.assertions)).toHaveLength(10000)