From edfc77b5259a886f2fbda6b5ca42be135190ce97 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 15:37:27 +0800 Subject: [PATCH 1/6] chore: add Renovate config Same shape as the rslint/rstest configs (config:recommended + the rstackjs security preset + weekly schedule + pinned action digests), with two local rules: E2E fixture manifests are ignored (they pin the published toolchain versions the suites verify), and the @rslint/core / @rstest/core / rstack devDependencies are excluded because their ranges mirror the runtime SUPPORT_MATRIX and move only with a compatibility decision. --- .github/renovate.json5 | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/renovate.json5 diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 0000000..da318df --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,34 @@ +{ + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', + 'github>rstackjs/renovate:security', + 'schedule:weekly', + 'helpers:pinGitHubActionDigests', + ], + // E2E fixtures deliberately pin the published toolchain versions the suites + // run against; bumping them is a manual, E2E-verified decision. + ignorePaths: ['**/node_modules/**', 'packages/vscode/tests/**'], + packageRules: [ + // Use chore as semantic commit type for commit messages + { + matchPackageNames: ['**'], + semanticCommitType: 'chore', + // always bump package.json + rangeStrategy: 'bump', + }, + { + groupName: 'all patch dependencies', + groupSlug: 'all-patch', + matchUpdateTypes: ['patch'], + matchPackageNames: ['**'], + }, + // The devDependency ranges for the toolchain packages are the type-level + // side of the runtime SUPPORT_MATRIX (shared/versionCheck.ts); moving them + // is a compatibility decision, not routine maintenance. + { + matchPackageNames: ['@rslint/core', '@rstest/core', 'rstack'], + enabled: false, + }, + ], +} From 1d695b26e2102e01887bdf8196a711fac84ace4a Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 16:25:14 +0800 Subject: [PATCH 2/6] ci: run unit tests on Linux only and E2E on Windows and macOS Splits the two per-OS jobs into one Linux check job (build, lint, format, unit tests) and an E2E matrix over windows-latest and macos-latest. Unit tests gain nothing from a second OS: the three places the source branches on `process.platform` (bin names in detection.ts, platform packages in stacks/lint/utils.ts, path casing in stacks/test/projectCoverage.ts) are only reachable when the host is that platform, and the Windows branches stay covered by the Windows E2E job. The E2E matrix drops Linux and gains macOS, matching the platforms upstream rstest runs these suites on. The ported watch-mode suites rewrite a fixture with `writeFile`, which truncates before writing; inotify reports that empty intermediate state as its own event, so the watcher can trigger a run against a zero-length test file and the assertions then inspect a run that never saw the edit (observed on ubuntu-latest: "No test suites found in file", 0 collected). Fixing it means diverging from a verbatim upstream copy; not running Linux does not. The suites pass 5/5 unmodified on macOS locally. --- .github/workflows/ci.yml | 67 ++++++++++++++++++--------------------- packages/vscode/AGENTS.md | 1 + 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc4a955..86fe08f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,16 @@ permissions: contents: read jobs: - # ======== linux ======== - test-linux: - name: Test (ubuntu-latest) + # ======== lint + unit tests: Linux only ======== + # Nothing under test here branches on `process.platform`: the three places + # that do (`detection.ts` bin names, `stacks/lint/utils.ts` platform + # packages, `stacks/test/projectCoverage.ts` path casing) are only reachable + # when the host *is* that platform, so a second OS would re-run identical + # assertions. The Windows branches are covered by the Windows E2E job below. + ut: + name: UT runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 20 steps: - name: Checkout @@ -51,33 +56,21 @@ jobs: - name: Unit Test run: pnpm run test:unit - # `@vscode/test-electron` downloads a full VS Code into - # `packages/vscode/.vscode-test`. Cache only the immutable distribution - # directories, keyed on the extension manifest (which carries - # `engines.vscode`); the restore-keys fallback keeps older downloads - # available while `version: 'stable'` moves forward. - - name: Cache VS Code Download - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: packages/vscode/.vscode-test/vscode-* - key: vscode-test-${{ runner.os }}-${{ hashFiles('packages/vscode/package.json') }} - restore-keys: | - vscode-test-${{ runner.os }}- - - # The E2E fixtures install published npm packages at test time - # (tests/e2e/setupFixtures.mjs, invoked by test:e2e), so this step needs - # network access. The VS Code Extension Host needs a display on Linux, - # hence xvfb (preinstalled on GitHub-hosted Ubuntu runners). - - name: E2E Test - run: xvfb-run -a pnpm run test:e2e - - # ======== windows ======== - # A dedicated GitHub-hosted Windows job: upstream rslint notes the VS Code - # extension E2E suite is unreliable on self-hosted Windows runners. - test-windows: - name: Test (windows-latest) - runs-on: windows-latest + # ======== E2E: Windows + macOS, no Linux ======== + # These are the platforms the extension is actually used on, and the only + # ones upstream rstest runs its ported VS Code suites on. Linux is excluded + # deliberately: the Extension Host needs xvfb there, and inotify reports a + # non-atomic file rewrite as separate truncate/write events, so fixture edits + # in the watch-mode suites race the watcher in a way no user hits. Do not add + # a Linux E2E job back without also making every fixture edit atomic. + e2e: + name: E2E (${{ matrix.os }}) + runs-on: ${{ matrix.os }} timeout-minutes: 40 + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] steps: - name: Checkout @@ -101,12 +94,11 @@ jobs: - name: Build run: pnpm run build - - name: Lint - run: pnpm run lint - - - name: Unit Test - run: pnpm run test:unit - + # `@vscode/test-electron` downloads a full VS Code into + # `packages/vscode/.vscode-test`. Cache only the immutable distribution + # directories, keyed on the extension manifest (which carries + # `engines.vscode`); the restore-keys fallback keeps older downloads + # available while `version: 'stable'` moves forward. - name: Cache VS Code Download uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: @@ -115,5 +107,8 @@ jobs: restore-keys: | vscode-test-${{ runner.os }}- + # The E2E fixtures install published npm packages at test time + # (tests/e2e/setupFixtures.mjs, invoked by test:e2e), so this step needs + # network access. - name: E2E Test run: pnpm run test:e2e diff --git a/packages/vscode/AGENTS.md b/packages/vscode/AGENTS.md index 23c1b18..7abc2df 100644 --- a/packages/vscode/AGENTS.md +++ b/packages/vscode/AGENTS.md @@ -37,3 +37,4 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten - E2E suites ported from upstream keep upstream's assertion semantics; every intentional deviation is documented in a comment in the test itself. A failing ported test is a regression, not a test to adjust. - E2E fixtures install published npm packages (not workspace links): the extension must work against what users actually install. Fixture `node_modules` are disposable and never committed. - Prefer running the E2E slice that covers the change (`test:e2e:*` scripts; `RSTACK_LINT_E2E_SUITES=` filters lint suites) over the full chain. +- CI runs unit tests on Linux only and E2E on Windows + macOS only — the same platforms upstream rstest runs its VS Code suites on, which is what keeps the ported suites viable as verbatim copies. Linux is excluded on purpose: the ported watch-mode suites rewrite a fixture with `writeFile` (truncate, then write), and inotify reports the empty intermediate state as its own event, so the watcher can trigger a run against a zero-length test file. Fixing that means diverging from upstream; keeping Linux out does not. From 12d34875ae1eeacfdc81a5091875b8c4dcd83c82 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 16:29:07 +0800 Subject: [PATCH 3/6] ci: rename the Linux job to UT --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86fe08f..d7ecacc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: # when the host *is* that platform, so a second OS would re-run identical # assertions. The Windows branches are covered by the Windows E2E job below. ut: - name: UT + name: UT (ubuntu-latest) runs-on: ubuntu-latest timeout-minutes: 20 From a980ac83bc2489b0588b24bc5738b1108c8af136 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 16:33:30 +0800 Subject: [PATCH 4/6] chore: take Rstack toolchain updates outside the weekly window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The weekly schedule gates when a PR is opened, so a package that ships every few days would still be picked up at most once a week — the security preset's `minimumReleaseAge: null` only waives the release-age cooldown, not the schedule. Give the Rstack scopes their own group with `schedule: ['at any time']` so a release is proposed on the next run after it publishes; everything else stays weekly. --- .github/renovate.json5 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index da318df..3729dc6 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -23,6 +23,29 @@ matchUpdateTypes: ['patch'], matchPackageNames: ['**'], }, + // The Rstack toolchain is what this repo exists to support and it ships + // every few days, so it opts out of the weekly window entirely: a PR is + // opened on the next run after a release. `schedule:weekly` still governs + // everything else, and `github>rstackjs/renovate:security` already waives + // the release-age cooldown for the same set. Listed after `all-patch` so + // these land in their own PR instead of the weekly patch roll-up. + { + groupName: 'rstack toolchain', + groupSlug: 'rstack', + matchPackageNames: [ + '@rsbuild/**', + '@rsdoctor/**', + '@rslib/**', + '@rslint/**', + '@rspack/**', + '@rspress/**', + '@rstackjs/**', + '@rstest/**', + 'rsbuild-plugin-**', + 'rstack', + ], + schedule: ['at any time'], + }, // The devDependency ranges for the toolchain packages are the type-level // side of the runtime SUPPORT_MATRIX (shared/versionCheck.ts); moving them // is a compatibility decision, not routine maintenance. From 84b320a07728ddc3795f7e37b6280107cbbdde1f Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 16:35:48 +0800 Subject: [PATCH 5/6] chore: let Renovate bump the Rstack toolchain devDependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SUPPORT_MATRIX declares a floor, not a pin, so raising @rslint/core, @rstest/core or rstack in package.json does not move it — the earlier exclusion treated the two as coupled. They now follow the same open-schedule rule as the rest of the Rstack scopes. --- .github/renovate.json5 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 3729dc6..61cf4f2 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -29,6 +29,11 @@ // everything else, and `github>rstackjs/renovate:security` already waives // the release-age cooldown for the same set. Listed after `all-patch` so // these land in their own PR instead of the weekly patch roll-up. + // + // This includes `@rslint/core`, `@rstest/core` and `rstack`, whose ranges + // sit alongside the runtime `SUPPORT_MATRIX` (shared/versionCheck.ts). + // That matrix is a floor, not a pin: raising a devDependency does not move + // it. Lowering the floor stays a deliberate, separate edit. { groupName: 'rstack toolchain', groupSlug: 'rstack', @@ -46,12 +51,5 @@ ], schedule: ['at any time'], }, - // The devDependency ranges for the toolchain packages are the type-level - // side of the runtime SUPPORT_MATRIX (shared/versionCheck.ts); moving them - // is a compatibility decision, not routine maintenance. - { - matchPackageNames: ['@rslint/core', '@rstest/core', 'rstack'], - enabled: false, - }, ], } From 96fea2d4a2f53fe63ba8c2defeda022458e914aa Mon Sep 17 00:00:00 2001 From: fi3ework Date: Wed, 5 Aug 2026 16:38:38 +0800 Subject: [PATCH 6/6] docs: drop the CI platform note from the extension AGENTS.md --- packages/vscode/AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vscode/AGENTS.md b/packages/vscode/AGENTS.md index 7abc2df..23c1b18 100644 --- a/packages/vscode/AGENTS.md +++ b/packages/vscode/AGENTS.md @@ -37,4 +37,3 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten - E2E suites ported from upstream keep upstream's assertion semantics; every intentional deviation is documented in a comment in the test itself. A failing ported test is a regression, not a test to adjust. - E2E fixtures install published npm packages (not workspace links): the extension must work against what users actually install. Fixture `node_modules` are disposable and never committed. - Prefer running the E2E slice that covers the change (`test:e2e:*` scripts; `RSTACK_LINT_E2E_SUITES=` filters lint suites) over the full chain. -- CI runs unit tests on Linux only and E2E on Windows + macOS only — the same platforms upstream rstest runs its VS Code suites on, which is what keeps the ported suites viable as verbatim copies. Linux is excluded on purpose: the ported watch-mode suites rewrite a fixture with `writeFile` (truncate, then write), and inotify reports the empty intermediate state as its own event, so the watcher can trigger a run against a zero-length test file. Fixing that means diverging from upstream; keeping Linux out does not.