Repository navigation
131 lines (118 loc) · 6.59 KB
/
Copy pathe2e.yml
File metadata and controls
131 lines (118 loc) · 6.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
name: E2E (real DeepSeek API)
# Real-API end-to-end suite (`pnpm run test:e2e`). Unlike ci.yml this job
# consumes the DEEPSEEK_API_KEY_EXTERNAL secret (mapped to the env var
# DEEPSEEK_API_KEY the tests read) and hits the external API at
# https://api.deepseek.com/anthropic (DEEPSEEK_BASE_URL is pinned explicitly so a
# stray repo-root .env can't redirect the run).
#
# pull_request IS a trigger, but GitHub withholds repo secrets from BOTH forked
# PRs and Dependabot PRs (the latter are same-repo, fork==false, but still
# keyless). The job-level `if:` below skips the whole job for those untrusted
# PRs. The Dependabot test keys on the PR AUTHOR (pull_request.user.login), not
# github.actor (the run trigger) — a maintainer reopening a Dependabot PR would
# make github.actor human while the PR is still keyless. When the job runs, the
# secret is expected, so the preflight hard-fails if it's missing (a missing
# secret would otherwise make the self-skipping suite report a false green). A
# job skipped by a job-level `if:` reports as a SUCCESSFUL check, so this
# workflow is safe to use as a required status check if desired.
#
# SECURITY — NEVER change this trigger to `pull_request_target`. `pull_request`
# checks out and runs the PR's HEAD code WITHOUT secrets for forks, which is what
# keeps an untrusted fork from exfiltrating the key. `pull_request_target` runs
# in the BASE repo's context WITH secrets while still able to check out untrusted
# fork code — a textbook key-leak vector, especially once this repo is public.
# The fork/secret model and its public-repo implications are recorded in
# .agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md.
#
# TRIGGERS — manual only. This suite spends real DeepSeek API credit on every
# run, and it used to run on each main push plus a nightly schedule, which for
# a fork that pushes straight to main meant paid runs nobody asked for and a
# failure mail per commit. It is now started deliberately, from the Actions tab
# or `gh workflow run`. The fork/secret rules above stay written down because
# they govern any future restoration of the pull_request trigger, and the
# job-level guard below still enforces them if that happens.
on:
workflow_dispatch:
# Manual runs are never cancelled: each one was started on purpose, and a
# later dispatch does not make an earlier one stale. Upstream 0.1.5 switched
# this to an unconditional `true`, which is right for its push and nightly
# triggers; this fork runs the suite only from workflow_dispatch and each run
# spends real DeepSeek API credit, so cancelling an in-flight run would throw
# away money already spent. The expression stays so a restored pull_request
# trigger would again cancel superseded PR runs.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Least privilege: this job only reads the repo to run tests.
permissions:
contents: read
env:
# CI runs must never report to the production telemetry endpoint baked
# into apps/cli/cordis.yml (AppCLIEntry disables the row when set).
DSH_TELEMETRY_DISABLED: '1'
jobs:
e2e:
runs-on: >-
${{ vars.DSH_CI_FAILOVER_LINUX == 'blacksmith'
&& 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
name: e2e
# Run on every trusted event. Skip untrusted PRs (forks + Dependabot) where
# the secret is withheld — they would otherwise hard-fail the preflight.
if: >-
github.event_name != 'pull_request'
|| !(github.event.pull_request.head.repo.fork || github.event.pull_request.user.login == 'dependabot[bot]')
# Profile e2e files can each own several complete dsh subprocess trees, so
# four file workers preserve process/PTY headroom. Tests retain 120s/test
# and retry 2; 45m still bounds retry storms against a slow API.
timeout-minutes: 45
steps:
- uses: actions/checkout@v7.0.1
- uses: pnpm/action-setup@v5.0.0
- uses: actions/setup-node@v7.0.0
with:
node-version: 24
cache: pnpm
- name: Install (immutable)
run: pnpm install --frozen-lockfile
# The with-key escalation e2e self-skips without a usable runner. Prepare
# bwrap so trusted CI exercises it; the functional probe remains the
# authority on whether the runner can use the sandbox.
- name: Prepare bubblewrap (unrestrict userns)
run: bash scripts/prepare-ci-bubblewrap.sh
# Guard against a false green: the e2e suites self-skip when the key is
# absent, so a missing/misconfigured secret would otherwise pass as
# "all skipped". This job only runs on trusted events (the `if:` above
# excludes keyless PRs), so the secret MUST be present — fail loudly if not.
- name: Preflight (require DEEPSEEK_API_KEY)
env:
# Repo secret DEEPSEEK_API_KEY_EXTERNAL holds the external-API key;
# the tests read process.env.DEEPSEEK_API_KEY, so map it across here.
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
run: |
set -euo pipefail
if [ -z "${DEEPSEEK_API_KEY:-}" ]; then
echo "::error::DEEPSEEK_API_KEY is empty. The e2e suite would self-skip and"
echo "::error::report a false green. Configure the repo secret DEEPSEEK_API_KEY_EXTERNAL."
exit 1
fi
echo "DEEPSEEK_API_KEY present."
# The e2e suites boot the example bins in `lib` mode (DSH_EXAMPLE_MODE=lib):
# the built artifact under plain Node, resolving plugins through real package
# exports — the shape a real consumer runs. That requires a prior build.
- name: Build (lib for the e2e example bins)
run: ${{ github.event_name == 'pull_request' && 'pnpm run build:artifacts --profile official' || 'pnpm run build:official' }}
- name: Prepare Office runtime
run: pnpm run prepare:primary-runtime --target linux-x64 --output .cache/e2e-office
# Real-API end-to-end tests only. The keyless gates (lint/typecheck/
# coverage/snapshot/etc.) already run in ci.yml (pull requests) and
# ci-master.yml (master push standby).
# DEEPSEEK_BASE_URL is pinned to the external API; the secret is scoped to
# this step (and preflight) only — never exposed to checkout/setup/install.
- name: E2E tests (real DeepSeek API)
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
DSH_PRIMARY_RUNTIME: ${{ github.workspace }}/.cache/e2e-office/primary-runtime
DSH_E2E_MAX_WORKERS: 4
DSH_EXAMPLE_MODE: lib
run: pnpm run test:e2e