From 5871ebefc6c8931c556381c010954c08a2ef8deb Mon Sep 17 00:00:00 2001 From: Dotta Date: Thu, 3 Sep 2026 16:10:39 -0500 Subject: [PATCH] fix(runner-e2e): prepare Daytona plugin before paid cells --- .github/workflows/runner-full-stack-e2e.yml | 28 +++++++++++++++++++ tests/runner-e2e/workflow-security.test.ts | 31 ++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/.github/workflows/runner-full-stack-e2e.yml b/.github/workflows/runner-full-stack-e2e.yml index f22a141522..375f04c6e3 100644 --- a/.github/workflows/runner-full-stack-e2e.yml +++ b/.github/workflows/runner-full-stack-e2e.yml @@ -766,6 +766,34 @@ jobs: # the protected environment during setup. - run: pnpm install --frozen-lockfile --ignore-scripts + # Sandbox-provider plugins are intentionally excluded from the root + # workspace. The ordinary root postinstall links the in-repo plugin SDK, + # but that lifecycle hook is deliberately disabled above. Prepare the + # one host plugin needed by Daytona explicitly, before this job receives + # provider credentials, and keep dependency lifecycle scripts disabled. + - name: Prepare bundled Daytona plugin without dependency lifecycle scripts + if: matrix.environmentId == 'daytona' + run: | + set -euo pipefail + daytona_root="packages/plugins/sandbox-providers/daytona" + sdk_root="packages/plugins/sdk" + test -d "$daytona_root" + test -d "$sdk_root" + test ! -L "$daytona_root" + test ! -L "$sdk_root" + test "$(jq -r .name "$daytona_root/package.json")" = "@paperclipai/plugin-daytona" + test "$(jq -r .name "$sdk_root/package.json")" = "@paperclipai/plugin-sdk" + ( + cd "$daytona_root" + pnpm install --ignore-workspace --no-lockfile --ignore-scripts + ) + node scripts/link-plugin-dev-sdk.mjs + test "$(realpath "$daytona_root/node_modules/@paperclipai/plugin-sdk")" = "$(realpath "$sdk_root")" + pnpm --dir "$daytona_root" build + test -f "$daytona_root/dist/manifest.js" + test -f "$daytona_root/dist/worker.js" + test -e "$daytona_root/node_modules/@daytonaio/sdk" + - name: Materialize verified pinned OpenCode executable if: matrix.environmentId == 'local' && (matrix.profileId == 'legacy-opencode' || matrix.profileId == 'runner-opencode' || matrix.suiteId == 'openrouter-model-breadth') run: node packages/paperclip-runner/scripts/materialize-opencode-binary.mjs diff --git a/tests/runner-e2e/workflow-security.test.ts b/tests/runner-e2e/workflow-security.test.ts index c5d0772e13..1d1ee7b1de 100644 --- a/tests/runner-e2e/workflow-security.test.ts +++ b/tests/runner-e2e/workflow-security.test.ts @@ -125,9 +125,38 @@ describe("public repository paid workflow security", () => { const paidInstall = paidJob.indexOf( "pnpm install --frozen-lockfile --ignore-scripts", ); + const daytonaPluginPreparation = paidJob.indexOf( + "Prepare bundled Daytona plugin without dependency lifecycle scripts", + ); const paidExecution = paidJob.indexOf("- name: Run paid cell"); expect(paidInstall).toBeGreaterThan(0); - expect(paidExecution).toBeGreaterThan(paidInstall); + expect(daytonaPluginPreparation).toBeGreaterThan(paidInstall); + expect(paidExecution).toBeGreaterThan(daytonaPluginPreparation); + const preparedBeforeProviderAccess = paidJob.slice( + daytonaPluginPreparation, + paidExecution, + ); + expect(preparedBeforeProviderAccess).toContain( + "if: matrix.environmentId == 'daytona'", + ); + expect(preparedBeforeProviderAccess).toContain( + "pnpm install --ignore-workspace --no-lockfile --ignore-scripts", + ); + expect(preparedBeforeProviderAccess).toContain( + "node scripts/link-plugin-dev-sdk.mjs", + ); + expect(preparedBeforeProviderAccess).toContain( + '"@paperclipai/plugin-daytona"', + ); + expect(preparedBeforeProviderAccess).toContain('"@paperclipai/plugin-sdk"'); + expect(preparedBeforeProviderAccess).toContain( + 'realpath "$daytona_root/node_modules/@paperclipai/plugin-sdk"', + ); + expect(preparedBeforeProviderAccess).toContain( + 'pnpm --dir "$daytona_root" build', + ); + expect(preparedBeforeProviderAccess).not.toContain("secrets."); + expect(preparedBeforeProviderAccess).not.toContain("pnpm rebuild"); expect(paidJob.slice(0, paidExecution)).not.toMatch( /secrets\.(?:OPENAI|ANTHROPIC|OPENROUTER|DAYTONA)_API_KEY/, );