diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6ea871caf7d..dc6974e189da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1186,8 +1186,10 @@ jobs: retention-days: 7 # - We skip optional tests on release branches + # - The tests run in optional-e2e.yml, so their failures do not mark the PR as failed. This job only waits for + # them and reports failures in a PR comment. job_optional_e2e_tests: - name: E2E ${{ matrix.label || matrix.test-application }} Test (optional) + name: E2E Tests (optional) # We only run E2E tests for non-fork PRs because the E2E tests require secrets to work and they can't be accessed from forks # We need to add the `always()` check here because the previous step has this as well :( # See: https://github.com/actions/runner/issues/2205 @@ -1196,35 +1198,14 @@ jobs: needs.job_build.outputs.e2e-matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' needs: [job_get_metadata, job_build, job_build_tarballs] - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript-e2e${{ github.event_name == - 'pull_request' && '-pr' || '' }} - timeout-minutes: 15 - env: - E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} - E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - # Needed because some apps expect a certain prefix - NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' - E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' - E2E_OPENROUTER_API_KEY: ${{ secrets.E2E_OPENROUTER_API_KEY }} - # Only this app talks to OpenAI directly (OpenRouter has no Decisions route), so only it gets the key. - E2E_OPENAI_DECISIONS_API_KEY: - ${{ matrix.test-application == 'node-openai-decisions-send-to-sentry' && secrets.E2E_OPENAI_DECISIONS_API_KEY || - '' }} - # Used by test apps that deploy a real Cloudflare Worker, e.g. cloudflare-workers-send-to-sentry - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - # The `runtime` of a runtime variant, see dev-packages/e2e-tests/README.md - RUNTIME: ${{ matrix.runtime }} - # Without the root node_modules, `pnpm run` would first install the whole workspace. - pnpm_config_verify_deps_before_run: 'false' - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.job_build.outputs.e2e-matrix-optional) }} - + # This job only waits for optional-e2e.yml, which can take most of an hour. GitHub runners are free for public + # repos and Namespace runners are paid per minute, so the wait runs here and the tests run on Namespace. + runs-on: ubuntu-24.04 + timeout-minutes: 60 + permissions: + contents: read + actions: write + pull-requests: write steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -1233,79 +1214,18 @@ jobs: - name: Set up Node uses: actions/setup-node@v7 with: - node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' - - name: Set up Bun - if: - matrix.test-application == 'node-exports-test-app' || contains(matrix.test-application, 'bun') || - matrix.runtime == 'bun' - uses: oven-sh/setup-bun@v2 - with: - bun-version: '1.3.14' - - name: Set up Deno - if: contains(matrix.test-application, 'deno') || matrix.runtime == 'deno' - uses: denoland/setup-deno@v2.0.5 - with: - deno-version: ${{ matrix.deno-version || 'v2.8.3' }} - # Keeps the pnpm store on the cache volume for the `pnpm install` of the test application - - name: Mount pnpm store - uses: ./.github/actions/mount-pnpm-store + node-version-file: 'package.json' - name: Install pnpm uses: ./.github/actions/install-pnpm - - - name: Restore tarball artifacts - uses: actions/download-artifact@v8 + # The Namespace cache volume is not available here, so only install what the action needs + - name: Install action dependencies + run: pnpm install --frozen-lockfile --filter @sentry-internal/optional-e2e-gh-action + - name: Run optional E2E tests + uses: ./dev-packages/optional-e2e-gh-action with: - name: build-tarball-output - path: ${{ env.TARBALL_ARTIFACT_DOWNLOAD_PATH }} - - - name: Restore test-utils tarball artifact - uses: actions/download-artifact@v8 - with: - name: build-test-utils-tarball-output - path: dev-packages/test-utils - - - name: Prepare E2E tests - run: pnpm run test:prepare - working-directory: dev-packages/e2e-tests - - - name: Validate test setup - run: pnpm run test:validate - working-directory: dev-packages/e2e-tests - - - name: Copy to temp - run: - pnpm run ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp - }}/test-application - working-directory: dev-packages/e2e-tests - - - name: Add pnpm overrides - run: - pnpm run ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace - }}/dev-packages/e2e-tests/packed - working-directory: dev-packages/e2e-tests - - # Installs Playwright while the app builds. Keep it right before the build: the runner sets up a background - # composite action's steps from the job's shared `github` context, which fails with "Collection was modified" if - # a foreground step starts at that moment. Starting next to `download-artifact` also made it fail to read the event file. - - name: Install Playwright - id: install-playwright - background: true - uses: ./.github/actions/install-playwright - with: - browsers: chromium - cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} - - - name: Build E2E app - working-directory: ${{ runner.temp }}/test-application - timeout-minutes: 7 - run: ${{ matrix.build-command || 'pnpm test:build' }} - - - wait: [install-playwright] - - - name: Run E2E test - working-directory: ${{ runner.temp }}/test-application - timeout-minutes: 10 - run: ${{ matrix.assert-command || 'pnpm test:assert' }} + github_token: ${{ secrets.GITHUB_TOKEN }} + commit: ${{ env.HEAD_COMMIT }} + matrix: ${{ needs.job_build.outputs.e2e-matrix-optional }} job_required_jobs_passed: name: All required jobs passed or were skipped diff --git a/.github/workflows/optional-e2e.yml b/.github/workflows/optional-e2e.yml new file mode 100644 index 000000000000..b0c52a21961e --- /dev/null +++ b/.github/workflows/optional-e2e.yml @@ -0,0 +1,167 @@ +name: 'CI: Optional E2E Tests' +run-name: Optional E2E tests for ${{ inputs.commit }} (${{ inputs.correlation_id }}) + +# Dispatched by `job_optional_e2e_tests` in build.yml. This is a separate workflow because GitHub +# does not count failed checks of a dispatched run in the PR status, so these failures do not mark +# the PR as failed. +on: + # Only here so that GitHub registers this workflow before it is on the default branch, which a + # dispatch needs. The job is skipped for this event. Remove this trigger after the merge. + pull_request: + workflow_dispatch: + inputs: + commit: + description: The commit that the tarballs were built from + required: true + build_run_id: + description: The build.yml run that uploaded the tarball artifacts + required: true + matrix: + description: The optional E2E test matrix as JSON + required: true + pr_number: + description: The PR that the run is for, if there is one + required: false + correlation_id: + description: Lets the dispatching job find this run + required: true + +# Like build.yml: a new push to a PR cancels the old run, other runs are never cancelled. +concurrency: + group: ${{ github.workflow }}-${{ inputs.pr_number || github.run_id }} + cancel-in-progress: true + +env: + HEAD_COMMIT: ${{ inputs.commit }} + + # See build.yml + TARBALL_ARTIFACT_DOWNLOAD_PATH: ${{ github.workspace }}/packages + DISABLE_V8_COMPILE_CACHE: '1' + +jobs: + job_optional_e2e_tests: + name: E2E ${{ matrix.label || matrix.test-application }} Test (optional) + if: github.event_name == 'workflow_dispatch' + # Same cache volume as the E2E jobs in build.yml. The event is always `workflow_dispatch`, so the PR number + # decides if the run gets the PR volume. + runs-on: + namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript-e2e${{ inputs.pr_number != '' && '-pr' + || '' }} + timeout-minutes: 15 + permissions: + contents: read + # To download the artifacts of the build.yml run + actions: read + env: + E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} + E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + # Needed because some apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' + E2E_OPENROUTER_API_KEY: ${{ secrets.E2E_OPENROUTER_API_KEY }} + # Only this app talks to OpenAI directly (OpenRouter has no Decisions route), so only it gets the key. + E2E_OPENAI_DECISIONS_API_KEY: + ${{ matrix.test-application == 'node-openai-decisions-send-to-sentry' && secrets.E2E_OPENAI_DECISIONS_API_KEY || + '' }} + # Used by test apps that deploy a real Cloudflare Worker, e.g. cloudflare-workers-send-to-sentry + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + # Keeps the Worker name at `-pr-`, which the cleanup-e2e-workers workflow deletes + E2E_PR_NUMBER: ${{ inputs.pr_number }} + # The `runtime` of a runtime variant, see dev-packages/e2e-tests/README.md + RUNTIME: ${{ matrix.runtime }} + # Without the root node_modules, `pnpm run` would first install the whole workspace. + pnpm_config_verify_deps_before_run: 'false' + strategy: + fail-fast: false + # The fallback only keeps the expression valid for the skipped `pull_request` run. + matrix: ${{ fromJson(inputs.matrix || '{"include":[{}]}') }} + + steps: + - name: Check out commit ${{ env.HEAD_COMMIT }} + uses: actions/checkout@v7 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v7 + with: + node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json' + - name: Set up Bun + if: + matrix.test-application == 'node-exports-test-app' || contains(matrix.test-application, 'bun') || + matrix.runtime == 'bun' + uses: oven-sh/setup-bun@v2 + with: + bun-version: '1.3.14' + - name: Set up Deno + if: contains(matrix.test-application, 'deno') || matrix.runtime == 'deno' + uses: denoland/setup-deno@v2.0.5 + with: + deno-version: ${{ matrix.deno-version || 'v2.8.3' }} + # Keeps the pnpm store on the cache volume for the `pnpm install` of the test application + - name: Mount pnpm store + uses: ./.github/actions/mount-pnpm-store + - name: Install pnpm + uses: ./.github/actions/install-pnpm + + - name: Restore tarball artifacts + uses: actions/download-artifact@v8 + with: + name: build-tarball-output + path: ${{ env.TARBALL_ARTIFACT_DOWNLOAD_PATH }} + run-id: ${{ inputs.build_run_id }} + github-token: ${{ github.token }} + + - name: Restore test-utils tarball artifact + uses: actions/download-artifact@v8 + with: + name: build-test-utils-tarball-output + path: dev-packages/test-utils + run-id: ${{ inputs.build_run_id }} + github-token: ${{ github.token }} + + - name: Prepare E2E tests + run: pnpm run test:prepare + working-directory: dev-packages/e2e-tests + + - name: Validate test setup + run: pnpm run test:validate + working-directory: dev-packages/e2e-tests + + - name: Copy to temp + run: + pnpm run ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp + }}/test-application + working-directory: dev-packages/e2e-tests + + - name: Add pnpm overrides + run: + pnpm run ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace + }}/dev-packages/e2e-tests/packed + working-directory: dev-packages/e2e-tests + + # Installs Playwright while the app builds. Keep it right before the build: the runner sets up a background + # composite action's steps from the job's shared `github` context, which fails with "Collection was modified" if + # a foreground step starts at that moment. Starting next to `download-artifact` also made it fail to read the event file. + - name: Install Playwright + id: install-playwright + background: true + uses: ./.github/actions/install-playwright + with: + browsers: chromium + cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} + + - name: Build E2E app + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 7 + run: ${{ matrix.build-command || 'pnpm test:build' }} + + - wait: [install-playwright] + + - name: Run E2E test + working-directory: ${{ runner.temp }}/test-application + timeout-minutes: 10 + run: ${{ matrix.assert-command || 'pnpm test:assert' }} diff --git a/dev-packages/optional-e2e-gh-action/.oxlintrc.json b/dev-packages/optional-e2e-gh-action/.oxlintrc.json new file mode 100644 index 000000000000..e4b415b5e548 --- /dev/null +++ b/dev-packages/optional-e2e-gh-action/.oxlintrc.json @@ -0,0 +1,4 @@ +{ + "$schema": "../../node_modules/oxlint/configuration_schema.json", + "extends": ["../.oxlintrc.json"] +} diff --git a/dev-packages/optional-e2e-gh-action/action.yml b/dev-packages/optional-e2e-gh-action/action.yml new file mode 100644 index 000000000000..684ee62d491f --- /dev/null +++ b/dev-packages/optional-e2e-gh-action/action.yml @@ -0,0 +1,15 @@ +name: 'optional-e2e-gh-action' +description: 'Run the optional E2E tests in their own workflow, wait for them and report failures in a PR comment' +inputs: + github_token: + required: true + description: 'a github access token' + commit: + required: true + description: 'The commit that the tarballs were built from' + matrix: + required: true + description: 'The optional E2E test matrix as JSON' +runs: + using: 'node24' + main: 'index.mjs' diff --git a/dev-packages/optional-e2e-gh-action/index.mjs b/dev-packages/optional-e2e-gh-action/index.mjs new file mode 100644 index 000000000000..54136e26542f --- /dev/null +++ b/dev-packages/optional-e2e-gh-action/index.mjs @@ -0,0 +1,166 @@ +import * as core from '@actions/core'; +import { context, getOctokit } from '@actions/github'; +import { markdownTable } from 'markdown-table'; + +const WORKFLOW_PATH = '.github/workflows/optional-e2e.yml'; +const WORKFLOW_NAME = 'CI: Optional E2E Tests'; +const COMMENT_MARKER = ''; +const ICONS = { success: '✅', failure: '❌', cancelled: '⛔', timed_out: '⏱️' }; + +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + +async function run() { + const octokit = getOctokit(core.getInput('github_token')); + const { repo } = context; + const pr = context.payload.pull_request; + const correlationId = `${context.runId}-${process.env.GITHUB_RUN_ATTEMPT}`; + + // A dispatch by file name only works when the file is on the default branch, a dispatch by ID always works. + const workflows = await octokit.paginate(octokit.rest.actions.listRepoWorkflows, repo); + const workflow = workflows.find(({ path }) => path === WORKFLOW_PATH); + if (!workflow) { + throw new Error(`Workflow ${WORKFLOW_PATH} not found`); + } + + await octokit.rest.actions.createWorkflowDispatch({ + ...repo, + workflow_id: workflow.id, + // A dispatch needs a branch, so it cannot use the PR merge ref. The `commit` input is the merge commit. + ref: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME, + inputs: { + commit: core.getInput('commit'), + build_run_id: String(context.runId), + matrix: core.getInput('matrix'), + pr_number: pr ? String(pr.number) : '', + correlation_id: correlationId, + }, + }); + + const workflowRun = await waitForRun(octokit, repo, workflow.id, correlationId); + core.info(`Optional E2E tests finished with: ${workflowRun.conclusion}`); + if (workflowRun.conclusion !== 'success') { + core.warning(`Optional E2E tests ${workflowRun.conclusion}: ${workflowRun.html_url}`); + } + + if (pr) { + // A failed report must not fail this job, because that marks the PR as failed. + try { + await reportInComment(octokit, repo, pr.number, workflowRun); + } catch (error) { + core.warning(`Could not report the optional E2E results in a PR comment: ${error.message}`); + } + } +} + +async function waitForRun(octokit, repo, workflowId, correlationId) { + let workflowRun; + for (let attempt = 0; attempt < 30 && !workflowRun; attempt++) { + const { data } = await octokit.rest.actions.listWorkflowRuns({ + ...repo, + workflow_id: workflowId, + event: 'workflow_dispatch', + per_page: 20, + }); + workflowRun = data.workflow_runs.find(({ display_title }) => display_title.includes(correlationId)); + if (!workflowRun) { + await sleep(2_000); + } + } + if (!workflowRun) { + throw new Error('Optional E2E run not found'); + } + + core.info(`Optional E2E run: ${workflowRun.html_url}`); + + while (workflowRun.status !== 'completed') { + await sleep(30_000); + ({ data: workflowRun } = await octokit.rest.actions.getWorkflowRun({ ...repo, run_id: workflowRun.id })); + } + + return workflowRun; +} + +// Keeps one comment per PR up to date, so new pushes do not add more comments. The comment is only +// created when a check fails. +async function reportInComment(octokit, repo, prNumber, workflowRun) { + const jobs = await octokit.paginate(octokit.rest.actions.listJobsForWorkflowRun, { + ...repo, + run_id: workflowRun.id, + filter: 'latest', + per_page: 100, + }); + const ran = jobs.filter(({ conclusion }) => conclusion !== 'skipped'); + const failing = ran.filter(({ conclusion }) => conclusion !== 'success'); + const successful = ran.filter(({ conclusion }) => conclusion === 'success'); + + const comments = await octokit.paginate(octokit.rest.issues.listComments, { + ...repo, + issue_number: prNumber, + per_page: 100, + }); + const previousComment = comments.find( + ({ user, body }) => user?.login === 'github-actions[bot]' && body?.startsWith(COMMENT_MARKER), + ); + + if (!failing.length && !previousComment) { + return; + } + + const counts = [ + failing.length && `${failing.length} failing`, + successful.length && `${successful.length} successful`, + ].filter(Boolean); + const runLink = `[See the run](${workflowRun.html_url})`; + + const bodyParts = [COMMENT_MARKER, '### Optional E2E tests', '']; + if (failing.length) { + bodyParts.push( + `Some optional checks were not successful. They do not block the merge. ${counts.join(', ')}. ${runLink}.`, + '', + formatJobs(failing), + ); + } else { + bodyParts.push(`All optional checks were successful. ${counts.join(', ')}. ${runLink}.`); + } + if (successful.length) { + bodyParts.push('', '
Successful checks', '', formatJobs(successful), '', '
'); + } + const body = bodyParts.join('\n'); + + if (previousComment) { + await octokit.rest.issues.updateComment({ ...repo, comment_id: previousComment.id, body }); + } else { + await octokit.rest.issues.createComment({ ...repo, issue_number: prNumber, body }); + } +} + +// Looks like the checks list on the PR page +function formatJobs(jobs) { + return markdownTable( + [ + ['', 'Check', 'Status', ''], + ...jobs.map(job => [ + ICONS[job.conclusion] ?? '❔', + `${WORKFLOW_NAME} / ${job.name}`, + formatStatus(job), + `[Details](${job.html_url})`, + ]), + ], + { alignDelimiters: false }, + ); +} + +function formatStatus({ conclusion, started_at, completed_at }) { + const seconds = Math.round((new Date(completed_at) - new Date(started_at)) / 1000); + const duration = seconds >= 60 ? `${Math.floor(seconds / 60)}m` : `${seconds}s`; + + if (conclusion === 'success') { + return `Successful in ${duration}`; + } + if (conclusion === 'cancelled') { + return `Cancelled after ${duration}`; + } + return `Failing after ${duration}`; +} + +run().catch(error => core.setFailed(error.message)); diff --git a/dev-packages/optional-e2e-gh-action/package.json b/dev-packages/optional-e2e-gh-action/package.json new file mode 100644 index 000000000000..98e11476d393 --- /dev/null +++ b/dev-packages/optional-e2e-gh-action/package.json @@ -0,0 +1,24 @@ +{ + "name": "@sentry-internal/optional-e2e-gh-action", + "description": "An internal Github Action to run the optional E2E tests without failing the PR.", + "version": "11.8.0", + "license": "MIT", + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0 <23.0.0 || >=23.2.0" + }, + "private": true, + "main": "index.mjs", + "type": "module", + "scripts": { + "lint": "oxlint . --type-aware", + "lint:fix": "oxlint . --fix --type-aware" + }, + "dependencies": { + "@actions/core": "1.10.1", + "@actions/github": "^5.0.0", + "markdown-table": "3.0.3" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/test-utils/src/cloudflare.ts b/dev-packages/test-utils/src/cloudflare.ts index 2e21a316d789..f50908f03dc4 100644 --- a/dev-packages/test-utils/src/cloudflare.ts +++ b/dev-packages/test-utils/src/cloudflare.ts @@ -36,9 +36,11 @@ function getWorkerName(workerPrefix: string): string { return `${workerPrefix}-local-${randomBytes(3).toString('hex')}`; } - const { GITHUB_EVENT_NAME, GITHUB_REF_NAME = '' } = process.env; + const { GITHUB_EVENT_NAME, GITHUB_REF_NAME = '', E2E_PR_NUMBER } = process.env; + // The optional E2E workflow is dispatched on the PR branch, so its ref has no PR number. const prNumber = - GITHUB_EVENT_NAME === 'pull_request' ? GITHUB_REF_NAME.split('/')[0] : /\/pr-(\d+)-/.exec(GITHUB_REF_NAME)?.[1]; + E2E_PR_NUMBER || + (GITHUB_EVENT_NAME === 'pull_request' ? GITHUB_REF_NAME.split('/')[0] : /\/pr-(\d+)-/.exec(GITHUB_REF_NAME)?.[1]); const ref = prNumber ? `pr-${prNumber}` : GITHUB_REF_NAME; // Worker names allow lowercase alphanumerics and dashes only, up to 63 characters. const slug = ref.toLowerCase().replace(/[^a-z0-9]+/g, '-'); diff --git a/package.json b/package.json index c85338eb4b7a..f1a984aa9b49 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "dev-packages/size-limit-gh-action", "dev-packages/clear-cache-gh-action", "dev-packages/external-contributor-gh-action", + "dev-packages/optional-e2e-gh-action", "dev-packages/rollup-utils", "dev-packages/bundler-tests" ], @@ -143,6 +144,7 @@ "@sentry-internal/external-contributor-gh-action": "workspace:*", "@sentry-internal/integration-shims": "workspace:*", "@sentry-internal/node-integration-tests": "workspace:*", + "@sentry-internal/optional-e2e-gh-action": "workspace:*", "@sentry-internal/replay-worker": "workspace:*", "@sentry-internal/rollup-utils": "workspace:*", "@sentry-internal/size-limit-gh-action": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0fadee75700..a48011e1e3bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -220,6 +220,9 @@ importers: '@sentry-internal/node-integration-tests': specifier: workspace:* version: link:dev-packages/node-integration-tests + '@sentry-internal/optional-e2e-gh-action': + specifier: workspace:* + version: link:dev-packages/optional-e2e-gh-action '@sentry-internal/replay-worker': specifier: workspace:* version: link:packages/replay-worker @@ -1003,6 +1006,18 @@ importers: specifier: ^3.24.1 version: 3.25.76 + dev-packages/optional-e2e-gh-action: + dependencies: + '@actions/core': + specifier: 1.10.1 + version: 1.10.1 + '@actions/github': + specifier: ^5.0.0 + version: 5.1.1(encoding@0.1.13) + markdown-table: + specifier: 3.0.3 + version: 3.0.3 + dev-packages/rollup-utils: dependencies: acorn: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9ee2f49ffa6d..fc92ba437495 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -59,6 +59,7 @@ packages: - 'dev-packages/size-limit-gh-action' - 'dev-packages/clear-cache-gh-action' - 'dev-packages/external-contributor-gh-action' + - 'dev-packages/optional-e2e-gh-action' - 'dev-packages/rollup-utils' - 'dev-packages/bundler-tests'