From 89bf6a33c23d12c592c2c45b07ac36a362d418e6 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Thu, 3 Sep 2026 21:28:49 -0500 Subject: [PATCH] ci: activate Node-first pnpm setup for PRs (#12810) ## Thinking Path > - Paperclip validates every change through an immutable reusable PR workflow. > - That caller still pinned a revision that ran pnpm setup before Node setup. > - The implementation fix in #12808 is therefore present on master but inactive for ordinary PR CI. > - Advancing the immutable caller pin activates the already tested Node-first workflow. > - A focused contract prevents the caller from silently returning to the old revision. > - The benefit is a faster PR feedback loop without changing product code or secret boundaries. ## Linked Issues or Issue Description Refs #12808 ## What Changed - Pin ordinary PR CI to trusted workflow revision `a0a78ee60946a5f79f85b2bd0584fc766fae43bb`. - Assert that the reusable workflow call is canonical, unique, and SHA-pinned to that audited revision. ## Verification - Focused workflow security test: 8/8. - Prettier passed. - Actionlint passed. - `git diff --check` passed. ## Risks Low risk. The change only advances an immutable reusable-workflow pin to a revision whose full ordinary CI and security checks passed. Product code and credentials are unchanged. ## Model Used OpenAI Codex GPT-5 with agentic reasoning and repository tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used - [x] I have linked the related public PR - [x] I have not referenced internal issue links - [x] My branch name describes the change - [x] I have run focused tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have considered and documented risks --- .github/workflows/pr.yml | 2 +- tests/runner-e2e/workflow-security.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3883269071..e67096111e 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,4 +10,4 @@ permissions: jobs: ci: - uses: paperclipai/paperclip/.github/workflows/pr-trusted.yml@f038633bf5b04163ff985ef0542876bd9f455379 + uses: paperclipai/paperclip/.github/workflows/pr-trusted.yml@a0a78ee60946a5f79f85b2bd0584fc766fae43bb diff --git a/tests/runner-e2e/workflow-security.test.ts b/tests/runner-e2e/workflow-security.test.ts index 74e1c5924a..bac160ea97 100644 --- a/tests/runner-e2e/workflow-security.test.ts +++ b/tests/runner-e2e/workflow-security.test.ts @@ -3,6 +3,8 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; const repositoryRoot = path.resolve(import.meta.dirname, "../.."); +const ordinaryPrTrustedWorkflowRevision = + "a0a78ee60946a5f79f85b2bd0584fc766fae43bb"; const fullStackTestNeeds = /needs:\s*\[\s*authorize,\s*target_lock,\s*catalog,\s*daytona_image,\s*build_runner_artifacts,\s*build_remote_provider_pack,?\s*\]/u; const buildRunnerNeeds = @@ -11,6 +13,26 @@ const buildRemoteProviderPackNeeds = /needs:\s*\[\s*authorize,\s*target_lock,\s*catalog,\s*daytona_image,\s*build_runner_artifacts,?\s*\]/u; describe("public repository paid workflow security", () => { + it("pins ordinary PR CI to the trusted Node-before-pnpm workflow", async () => { + const ordinaryPrWorkflow = await readFile( + path.join(repositoryRoot, ".github/workflows/pr.yml"), + "utf8", + ); + const trustedWorkflowCalls = [ + ...ordinaryPrWorkflow.matchAll( + /^\s+uses:\s+(paperclipai\/paperclip\/\.github\/workflows\/pr-trusted\.yml)@([0-9a-f]{40})$/gmu, + ), + ]; + + expect(trustedWorkflowCalls).toHaveLength(1); + expect(trustedWorkflowCalls[0]?.[1]).toBe( + "paperclipai/paperclip/.github/workflows/pr-trusted.yml", + ); + expect(trustedWorkflowCalls[0]?.[2]).toBe( + ordinaryPrTrustedWorkflowRevision, + ); + }); + it("keeps pnpm bootstrap registry telemetry out of trusted workflow setup", async () => { for (const workflowName of [ "runner-full-stack-e2e.yml",