Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/merge-upstream-vscode/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ further by hand, then confirm with `npm ci --dry-run`.

#### Check for new postinstall artifacts

The `test / unit` CI job fails with `npm install created N files outside
The `test / vscode` CI job fails with `npm install created N files outside
node_modules/` if postinstall writes a file that isn't in
`.github/cache-scripts/cache-paths.sh`. On a cache hit postinstall is skipped, so
an uncached artifact would be missing. Upstream changes to the install scripts
Expand Down
2 changes: 1 addition & 1 deletion .github/cache-scripts/log-cache-status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ fi

# Only emit a summary for restore operations, and only when the caller opts in
# via EMIT_CACHE_SUMMARY=true. Cache state is identical across jobs in a
# workflow, so we opt in from one job (test/unit) to avoid duplication.
# workflow, so we opt in from one job (test/vscode) to avoid duplication.
# Save events are redundant with restore misses (a save only happens because
# the matching restore missed), so we never emit a save summary.
if [[ -z "${GITHUB_STEP_SUMMARY:-}" \
Expand Down
28 changes: 21 additions & 7 deletions .github/workflows/test-e2e-windows-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ name: "Test: E2E (Windows) — Sharded"
# Cache Save Strategy:
# This workflow has a save_cache parameter to prevent race conditions when multiple jobs
# try to save the same cache key simultaneously. Only ONE job per calling workflow should
# set save_cache: true, and only shard 1 actually saves. Examples:
# - test-pull-request.yml: windows e2e saves (save_cache: true, shard 1 only)
# set save_cache: true, and only shard 1 actually saves. The uv, R, and Graphviz caches
# save from shard 1 on any non-PR run instead. PRs never save: a cache saved on a PR
# ref is readable only by that PR, so it just evicts main's caches from the 10 GB repo
# limit. Examples:
# - test-pull-request.yml: only restores (save_cache: false)
# - test-merge.yml: windows e2e saves (save_cache: true, shard 1 only)
# - test-full-suite.yml: only restores (save_cache: false or omitted)
# - test-full-suite.yml: saves uv/R/Graphviz only (save_cache omitted)

on:
workflow_call:
Expand Down Expand Up @@ -341,7 +344,7 @@ jobs:
uv pip install --system trcli

- name: Save Python packages cache
if: ${{ matrix.shard == 1 && steps.cache-python.outputs.cache-hit != 'true' }}
if: ${{ github.event_name != 'pull_request' && matrix.shard == 1 && steps.cache-python.outputs.cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@v6
with:
Expand Down Expand Up @@ -427,7 +430,7 @@ jobs:
Rscript -e "options(repos = c(CRAN = Sys.getenv('PPM_REPO'))); pak::local_install_dev_deps(ask = FALSE, upgrade = FALSE)"

- name: Save R packages cache
if: ${{ matrix.shard == 1 && steps.cache-r.outputs.cache-hit != 'true' }}
if: ${{ github.event_name != 'pull_request' && matrix.shard == 1 && steps.cache-r.outputs.cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@v6
with:
Expand All @@ -445,8 +448,10 @@ jobs:
rig add ${{ steps.r-versions.outputs.r_alt_version }}
rig ls

- name: Cache Graphviz
uses: actions/cache@v6
- name: Restore Graphviz cache
id: cache-graphviz
continue-on-error: true
uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}\graphviz
key: graphviz-win64-${{ env.GRAPHVIZ_VERSION }}
Expand Down Expand Up @@ -518,6 +523,15 @@ jobs:
throw "dot -V failed with exit code $LASTEXITCODE"
}

# Fails (and is ignored) when the Chocolatey fallback left nothing to cache.
- name: Save Graphviz cache
if: ${{ github.event_name != 'pull_request' && matrix.shard == 1 && steps.cache-graphviz.outputs.cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}\graphviz
key: graphviz-win64-${{ env.GRAPHVIZ_VERSION }}

- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-full-suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -556,5 +556,5 @@ jobs:
tailscale_oauth_secret: ${{ secrets.POSITRON_TAILSCALE_OAUTH_SECRET }}
repo_id: "positron"
title: "E2E Full Suite Tests"
filter_jobs: "(e2e / ([^0-9]*)|unit|ext-host(-win)?)$"
filter_jobs: "(e2e / ([^0-9]*)|vscode|vitest|ext-host(-win)?)$"
notify_on: ${{ inputs.notify_on }}
2 changes: 1 addition & 1 deletion .github/workflows/test-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ jobs:
repo_id: "positron"
# A docs-only push runs only unit-tests; leave out the skipped jobs.
hide_skipped: ${{ needs.docs-only.outputs.docs_only == 'true' }}
filter_jobs: "(e2e / ([^0-9]*)|unit|ext-host(-win)?)$"
filter_jobs: "(e2e / ([^0-9]*)|vscode|vitest|ext-host(-win)?)$"

analyze-e2e-failures:
if: ${{ always() && contains(needs.*.result, 'failure') }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
name: e2e
uses: ./.github/workflows/test-e2e-windows-run.yml
with:
save_cache: true
save_cache: false # PRs restore from main only; test-merge.yml saves
grep: ${{ needs.pr-tags.outputs.tags }}
display_name: "electron-win"
upload_logs: false
Expand Down Expand Up @@ -421,7 +421,7 @@ jobs:
pull_request: true

# Single always-reported check for every test job in this workflow, meant to
# be the one required check in the branch ruleset (in place of test / unit,
# be the one required check in the branch ruleset (in place of test / vscode,
# test / ext-host and e2e / electron).
#
# The tag-driven lanes can't be required directly: when a lane's tag is
Expand Down
61 changes: 44 additions & 17 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,21 @@ permissions:
packages: read

jobs:
unit-tests:
name: unit
vscode-tests:
name: vscode
runs-on: ubuntu-latest-amd64-4-core
# The three test steps only account for ~5m; almost all of the wall clock is
# npm install plus the cache save, and both scale with how many npm caches
# missed. A warm-ish run lands at ~35m, but a run that misses all three npm
# caches (e.g. run 33655268940) pays ~14m of cold install *and* ~30m of
# cache save (npm-core 8m, extensions-volatile 4m, extensions-stable 18m),
# reaching the Electron tests at ~57m. 60m cancelled that run 3m into the
# first test step. 75m covers the fully-cold ~62m path with headroom while
# still capping a genuinely hung run.
timeout-minutes: 75
# missed. A run that misses all three npm caches (e.g. run 33655268940) pays
# ~14m of cold install *and* ~30m of cache save (npm-core 8m,
# extensions-volatile 4m, extensions-stable 18m). With Vitest moved to its
# own job, that fully-cold path is ~52m; 65m leaves headroom while still
# capping a genuinely hung run.
timeout-minutes: 65
# Run inside the shared CI image, which bakes in the system dependencies,
# Node (matching .nvmrc), and R 4.6.1 + test-files deps that this
# job used to install at runtime. Built from docker/images/ubuntu24_04.
container:
container: &container
image: ghcr.io/posit-dev/positron-ubuntu24:24.22.1-py3.14.7-r4.6.1
# --shm-size: the Electron unit runner loads each test file's full module
# graph over file://. A wide graph (>~256 parallel fetches) exhausts the
Expand All @@ -58,7 +57,7 @@ jobs:
credentials:
username: ${{ secrets.POSITRON_GITHUB_RO_USER }}
password: ${{ secrets.POSITRON_GITHUB_RO_PAT }}
env:
env: &env
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
POSITRON_BUILD_NUMBER: 0 # CI skips building releases
_R_CHECK_FUTURE_FILE_TIMESTAMPS_: false # this check can be flaky in the R pkg tests
Expand All @@ -71,11 +70,13 @@ jobs:
# reticulate 1.47 cannot run on Python 3.14 (rstudio/reticulate#1930); bind the 3.13
RETICULATE_PYTHON: /root/.pyenv/versions/3.13.0/bin/python
steps:
- uses: actions/checkout@v7
- &checkout
uses: actions/checkout@v7
with:
submodules: recursive

- name: Checkout specific commit
- &checkout-commit
name: Checkout specific commit
if: ${{ inputs.commit != '' }}
run: |
git checkout ${{ inputs.commit }}
Expand All @@ -87,7 +88,8 @@ jobs:
with:
emit-summary: 'true'

- name: Install node dependencies
- &install-node-deps
name: Install node dependencies
if: steps.restore-caches.outputs.cache-npm-core-hit != 'true' ||
steps.restore-caches.outputs.cache-npm-extensions-volatile-hit != 'true' ||
steps.restore-caches.outputs.cache-npm-extensions-stable-hit != 'true'
Expand Down Expand Up @@ -139,9 +141,6 @@ jobs:
find . -type f -o -type l | grep -v "node_modules/" | sort > /tmp/files-after-npm-install.txt
.github/cache-scripts/check-uncached-artifacts.sh /tmp/files-before-npm-install.txt /tmp/files-after-npm-install.txt

- name: 🧪 Run Vitest Tests
run: npm run test:positron

- name: Compile Positron and Download Electron
run: npm exec -- npm-run-all --max-old-space-size=8192 -p compile "electron x64"

Expand Down Expand Up @@ -207,3 +206,31 @@ jobs:
env:
PLAYWRIGHT_BROWSERS_PATH: .playwright-browsers
run: DISPLAY=:10 npm run test-browser-no-install -- --browser chromium

# Vitest needs only node_modules (no compile, Electron, or browsers), so it
# runs beside the unit job instead of adding ~10m to its critical path. Same
# container as the unit job so the restored npm caches match. Never saves
# caches; the unit job owns that.
vitest:
name: vitest
runs-on: ubuntu-latest-amd64-4-core
# Container + restore (~6m), a cold install (~15m), and Vitest (~12m), plus
# room for one install retry after a hung 30m attempt.
timeout-minutes: 70
container: *container
env: *env
steps:
- *checkout
- *checkout-commit

- name: 📦 Restore caches
id: restore-caches
uses: ./.github/actions/restore-build-caches
with:
restore-builtins: 'false'
restore-playwright: 'false'

- *install-node-deps

- name: 🧪 Run Vitest Tests
run: npm run test:positron
Loading