From b7672d8abaa032bbeb461d6785f7b8b60daf55ef Mon Sep 17 00:00:00 2001 From: Rushaway Date: Thu, 29 Oct 2026 11:14:31 +0100 Subject: [PATCH 1/2] fix(ci): pin sigstore/cosign-installer to v4.1.2 #86 bumped it to `@v4`, but cosign-installer publishes no floating major tag (only v4.0.0, v4.1.x), so the Docker image workflow fails at "Unable to resolve action sigstore/cosign-installer@v4" on every push to main. Pin the latest release. v4 still installs the cosign v2.4.1 this workflow asks for. --- .github/workflows/docker-image.yml | 5 ++++- CHANGELOG.md | 9 +++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index afc0108d2..94ffa3acd 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -152,8 +152,11 @@ jobs: # form is the documented best-practice (signs the immutable digest, # not the mutable tag — so a future re-tag doesn't invalidate the # signature). + # + # cosign-installer publishes no floating `v4` tag (only v4.x.y), + # so pin the exact release. v4 still installs cosign v2.x. - name: Install cosign - uses: sigstore/cosign-installer@v4 + uses: sigstore/cosign-installer@v4.1.2 with: cosign-release: 'v2.4.1' diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ec23cc5..f8b337fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,15 @@ Legend: ? = Other stuff ``` +(29/10/26): Version 2.2.9-srcdslab +----------------------- +``` +01. ! CI: the Docker image workflow failed on every push to main since + the Actions bump; cosign-installer is now pinned to v4.1.2 +02. ? E2E: the "bulk select → assign web group" test no longer races + the page reload that follows the bulk action +``` + (29/10/26): Version 2.2.8-srcdslab ----------------------- ``` From 29e4fa01fb67f90655834f83a0c8324746ba7e8d Mon Sep 17 00:00:00 2001 From: Rushaway Date: Thu, 29 Oct 2026 11:14:31 +0100 Subject: [PATCH 2/2] test(e2e): read the admins.bulk response inside the route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "bulk select → assign web group" waited for the response and then called .json() on it. With no rehash SIDs the page reloads as soon as admins.bulk resolves, so the body can be gone before Playwright reads it ("Protocol error (Network.getResponseBody): No resource with given identifier found"). Fetch the real response in a page.route handler, keep its body, then fulfill the page with it. --- .../specs/flows/admin-deactivate-bulk.spec.ts | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/web/tests/e2e/specs/flows/admin-deactivate-bulk.spec.ts b/web/tests/e2e/specs/flows/admin-deactivate-bulk.spec.ts index 35a52dc0f..6373fc905 100644 --- a/web/tests/e2e/specs/flows/admin-deactivate-bulk.spec.ts +++ b/web/tests/e2e/specs/flows/admin-deactivate-bulk.spec.ts @@ -351,13 +351,32 @@ test.describe('flow: admin deactivate + bulk (#1509)', () => { .toBe('visible'); await dialog.locator('[data-testid="admins-bulk-web-group-select"]').selectOption(String(group.gid)); - const bulkResp = page.waitForResponse( - (r) => r.url().includes('api.php') && r.request().method() === 'POST' && r.status() === 200, - ); + // The page reloads as soon as admins.bulk resolves (no rehash + // SIDs here), so a waitForResponse() body can be evicted before + // .json() reads it. Read the real response inside the route. + const bulk: { body: { ok?: boolean; data?: { op?: string; applied?: number[] } } | null } = { + body: null, + }; + await page.route((url) => url.pathname.endsWith('/api.php'), async (route) => { + const req = route.request(); + let action = ''; + try { + action = JSON.parse(req.postData() ?? '{}').action ?? ''; + } catch { + action = ''; + } + if (req.method() !== 'POST' || action !== 'admins.bulk') { + await route.continue(); + return; + } + const response = await route.fetch(); + bulk.body = await response.json(); + await route.fulfill({ response }); + }); await dialog.locator('[data-testid="admins-bulk-web-group-submit"]').click(); - const body = await (await bulkResp).json(); - expect(body.ok).toBe(true); - expect(body.data?.op).toBe('set_web_group'); - expect(body.data?.applied).toEqual([aid]); + await expect.poll(() => bulk.body).not.toBeNull(); + expect(bulk.body?.ok).toBe(true); + expect(bulk.body?.data?.op).toBe('set_web_group'); + expect(bulk.body?.data?.applied).toEqual([aid]); }); });