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 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)