ReFlex: GitHub CI
Table of Contents
- Workflows
- Pull Request (main)
- Complete workflows
- Test execution and reports
- Pages deployment
- Cleanup PR caches
Workflows
The workflow files under .github/workflows/ define these entry points. Reusable workflows also run when invoked by a parent, even if they have no direct PR or push trigger.
| File | Purpose | Direct triggers |
|---|---|---|
build-test-complete.yml |
Run .NET/server checks, emulator build and Python/gRPC/notebook tests | Manual |
build-test-complete-with-pages.yml |
Same reusable checks plus report caching and Pages deployment | Push to main, manual |
net-build-all.yml |
Build/test .NET solution and call Python/gRPC/notebook tests | PR targeting main, manual |
npm-build-all.yml |
Build, lint and test npm projects | PR targeting main, push to main |
electron-smoke.yml |
Package and start emulator and TrackingServer on native desktop runners | PR (all target branches), push to main, manual |
library-test.yml |
Test ReFlex.sln with coverage and TRX results |
Reusable call, manual |
python-grpc-test.yml |
Test Python extraction, isolated gRPC service and notebooks in Docker | Reusable call, manual |
server-build.yml |
Build TrackingServer Angular frontend | Reusable call, manual |
server-lint.yml |
Lint server | Reusable call, manual |
server-test.yml |
Test Angular server frontend | Reusable call, manual |
emulator-build.yml |
Build emulator Angular frontend | Reusable call, manual |
shared-build.yml |
Build shared TypeScript types | Push to main, reusable call, manual |
shared-components-build.yml |
Build shared Angular components | Push to main, reusable call, manual |
server-release.yml |
Publish server release | Release published |
emulator-release.yml |
Publish emulator release | Release published |
pages-deploy.yml |
Build and deploy documentation | Push to pages, reusable call, manual |
library-readme-packages.yml |
Update library package documentation | Matching paths changed on main, manual |
cache-cleanup.yml |
Remove caches belonging to a closed PR | PR closed |
The .NET and npm PR triggers explicitly cover opened, reopened, edited and synchronize for PRs targeting main. The Electron workflow uses the default pull_request activity types and has no target-branch filter. A feature-branch push without an open PR does not trigger these checks through their push settings.
Pull Request (main)
Three entry-point workflows run for PRs targeting main:
net-build-all.yml: builds the .NET solution onwindows-latest, runs its tests, and invokespython-grpc-test.ymlas a separate job onubuntu-latest.npm-build-all.yml: runsnpm run build-completein one job onwindows-latest, with sequential build, repository lint, lint-configuration checks and workspace test phases.electron-smoke.yml: runs native packaging and startup checks for both desktop applications across five OS/architecture targets. It also runs for PRs targeting other branches.
The manual complete workflow is not the PR entry point. PR checks do not deploy Pages. cache-cleanup.yml runs when the PR closes, rather than as part of its build checks.
Complete workflows
build-test-complete.yml is manually dispatched. It calls the .NET library tests, TrackingServer frontend build/lint/tests, emulator frontend build and Python/gRPC/notebook tests. It does not cache the combined documentation reports or deploy Pages.
These complete workflows are separate from the npm command npm run build-complete: they do not invoke all npm workspace tests or the Electron smoke matrix. The npm and Electron entry-point workflows run independently according to their own triggers.
build-test-complete-with-pages.yml runs on pushes to main or manually. It starts the same checks and adds these dependencies:
flowchart TD
L[Library tests] --> C[Collect and cache library/server reports]
S[Server frontend tests] --> C
C --> P[Deploy Pages]
G[Python extraction, gRPC and notebooks] --> P
update_pages requires both collect-cache_data and test_python_grpc to succeed. Server build, server lint and emulator build are parallel jobs; the current Pages dependency list does not explicitly gate deployment on those three jobs.
Test execution and reports
npm and Angular
npm-build-all.yml uses .github/actions/npm-install, which selects Node.js 22 and caches npm downloads using the root lockfile. Every run installs the locked workspace dependencies at the repository root with npm ci --include=dev; cached node_modules directories do not bypass installation. The workflow then executes:
npm run build-complete
The root script expands to four sequential phases:
npm run build --workspaces --if-present
npm run lint
npm run test:lint
npm run test-ci --workspaces --if-present
The next phase starts only if the preceding command succeeds. --if-present applies to builds and tests and skips workspaces without those scripts; it does not suppress failures of existing scripts. Lint runs once from the repository root and fails on errors or warnings (--max-warnings 0). Builds follow the workspace order in the root package.json, with shared types and Angular components built before their consumers. Angular projects are configured centrally in the root angular.json.
All ten npm workspaces have a build script. Their additional checks are:
| Workspace | Lint | test-ci |
|---|---|---|
packages/reflex-shared-types |
ESLint | No script |
packages/reflex-angular-components |
ESLint | Vitest, headless Chromium, coverage |
tools/ReFlex.TrackingServer/ClientApp |
ESLint | Vitest, headless Chromium, coverage |
tools/emulator |
ESLint | Vitest, headless Chromium, coverage |
tools/gestenrecorder |
ESLint | Vitest, headless Chromium, coverage |
tools/logging |
ESLint | No script |
examples/basic-angular |
ESLint | Vitest, headless Chromium, coverage |
examples/basic-react |
ESLint | No script |
examples/basic-vue |
ESLint (JavaScript and Vue templates) | Vitest with happy-dom |
examples/shared-components-demo |
ESLint | Vitest, headless Chromium, coverage |
The Angular CI commands disable watch mode; the Vue command uses vitest run. Gestenrecorder is included through its own test-ci script. Interactive test scripts are not used by build-complete. Before local execution, run npx playwright install chromium. The shared npm-install action installs Chromium (including required OS dependencies) for CI.
ESLint uses the single root eslint.config.js. Every workspace also has a lint script for local checks; Angular CLI lint targets use the same configuration through @angular-eslint/builder:lint. Repository lint covers application and library sources, specs, mocks, test support files, production environments, Angular external and inline templates, React TSX, Vue single-file components, Electron entry points and repository build scripts. Only generated outputs, dependency directories and build caches are ignored. Per-workspace tsconfig.eslint.json files include tests and build configurations (.ts, .tsx, .mts, .cts) for type-aware linting without adding test helpers to production entry points.
The shared TypeScript rule adapter is scripts/eslint/typescript.config.cjs. It retains the GTV policy, migrates renamed/stylistic TypeScript rules and explicitly restores historical ESLint checks that newer presets omit, including no-undef, no-unreachable and no-redeclare. Necessary decorated Angular test mocks are allowed by the corresponding rule option. Angular template checks include accessibility; Vue templates use eslint-plugin-vue. Unused suppression directives are errors. AngularJS’s eslint-plugin-angular is not required.
npm run test:lint checks source-file coverage, workspace scripts, historical rule severities, Angular template checks and actual React-hook/Vue-template violations. npm run lint-fix applies automatic fixes; remaining findings must be fixed before CI succeeds. Standalone lint commands require the shared packages to have been built, as their consumers resolve the generated declarations. build-complete ensures this ordering. Angular tests use the applications’ zoneless scheduling, including the change-detection regression suites. Zoneless regression cases live in the corresponding component specs; shared typed test contexts and data modules live in test-utils folders.
All six Angular projects use @angular/build:unit-test with Vitest and Playwright Chromium. Application builds use @angular/build:application; the component library uses @angular/build:ng-packagr. The library has a separate test-build target to include its global SCSS in browser tests. Coverage (HTML, text summary, LCOV and Cobertura) is written to separate project folders under test/artifacts/coverage/. JUnit files under test/artifacts/tests/ also have distinct names. npm-build-all.yml verifies these outputs with scripts/verify-angular-reports.cjs and uploads them as angular-test-reports, including available reports on failure.
The separate reusable server-test.yml builds shared types and components, then runs npm run test:server. It uploads coverage as Angular Test Report (ReFlex TrackingServer) and publishes test/artifacts/tests/junit-test-results.xml through the JUnit report action. Report verification checks all coverage formats and a nonempty, successful JUnit report. Coverage upload and JUnit publishing run on success or failure. The complete workflow with Pages collects this server coverage artifact alongside the .NET library report; it does not collect all npm workspace reports.
Electron startup smoke tests
electron-smoke.yml checks the packaged applications independently of Angular unit tests and web builds. Its matrix contains both emulator and server for each target, resulting in ten jobs:
| Target suffix | OS / architecture | Runner |
|---|---|---|
win |
Windows x64 | windows-latest |
osx |
macOS x64 | macos-15-intel |
osx-arm64 |
macOS ARM64 | macos-15 |
linux |
Linux x64 | ubuntu-24.04 |
linux-arm64 |
Linux ARM64 | ubuntu-24.04-arm |
Each job installs Node.js 22 and npm dependencies, then builds an unpacked application on its native OS/architecture:
npm run build:<application>:electron-<target> -- --dir
npm run smoke:electron -- <application>
Server jobs also install the .NET SDK selected by global.json. Linux jobs install display dependencies and run the smoke command through xvfb-run -a. Builds are unsigned and do not publish installers or require signing secrets.
scripts/smoke-electron.cjs starts the packaged executable and waits up to 120 seconds for its Angular UI: the emulator canvas or the TrackingServer home page. For TrackingServer, it also checks that the bundled backend answers /api/versionInfo successfully with a JSON array. It fails on early process exit, startup timeout or recorded renderer exceptions, and checks that the process remains alive for another two seconds after the initial render. The application and its child processes are terminated after the check.
Startup logs and a screenshot, when available, are uploaded even after a failure as electron-smoke-<application>-<target> from test/artifacts/electron/<application>/. Matrix jobs have a 30-minute timeout and fail-fast: false; one target’s failure does not cancel the remaining targets. A newer workflow run for the same ref cancels an older in-progress smoke run.
These are startup checks, not full interaction or sensor tests. They do not publish a release or deploy Pages. See Electron builds and startup checks for local commands and packaging details.
.NET
The shared .NET setup reads the SDK version from global.json and restores workloads and dependencies for ReFlex.sln. On Windows, the MAUI example selects its Windows target rather than also requesting Mac Catalyst.
Both .NET test workflows explicitly execute ReFlex.sln with configuration CI and platform x64. InteractionTests is part of that solution, so its raster, classification and observer-pipeline regressions are included without a separate test command. This includes the SingleTouch regression that retains multiple historical IDs internally while publishing at most one point after smoothing.
net-build-all.ymluploads TRX files as.NET Test Results (Solution).library-test.ymluploads TRX files as.NET Test Results (Library)and produces the existing coverage report as.NET Test Report (ReFlex Library).- The TRX upload steps use
always(), so available result files are uploaded even when tests fail. A failure before test execution can leave no files to upload. - Coverage report generation still depends on successful earlier steps; it is not an unconditional failure-report step.
Python, gRPC and notebooks
python-grpc-test.yml uses ubuntu-latest with a 20-minute job timeout. It builds the notebook target of examples/grpc-service/Dockerfile and runs three separate steps:
docker build -f examples/grpc-service/Dockerfile --target notebook -t reflex-grpc-ci .
docker run --rm --workdir /app reflex-grpc-ci python -m unittest discover -s tests -v
docker run --rm --workdir /app reflex-grpc-ci python -m unittest discover -s /app/example/tests -v
docker run --rm reflex-grpc-ci python /app/example/tests/check_notebook.py
The extraction tests include the unchanged 640 × 480 recording at Skip 0, 1 and 3. The gRPC tests use a private listener. The notebook check executes both demo and observe and verifies rendered plots. No sensor hardware, live service or published container ports are required.
After a successful image build, the gRPC and notebook steps also run if an earlier test step fails, unless the workflow has been cancelled. Failures still fail the job. Results are currently written to the Actions logs; this workflow does not generate a separate Python coverage or JUnit artifact. These tests do not constitute a C#-to-Python end-to-end test or a Compose startup test.
See the gRPC recipe and template for the example and test data.
Pages deployment
pages-deploy.yml copies repository documentation using scripts/copy_docs.sh, downloads the pages-test-reports artifact from the current workflow run under docs/test, builds the site with Jekyll and deploys the Pages artifact. Both before and after Jekyll, it checks the library and server HTML entry points. The existing server URL test/server/report/reflex-tracking-server/index.html is preserved.
In the complete workflow on main, report collection uploads a combined artifact and saves a cache with a unique run-specific key before deployment, and the Python test job must succeed. A direct manual invocation of pages-deploy.yml or a push to pages does not run these tests or impose that dependency. Such a deployment restores the latest compatible test-reports-<run-id>-<attempt> cache (with a legacy test-reports fallback); cached reports can be older than the source documentation. GitHub caches are scoped to branches, with access to eligible default-branch caches.
The Pages dependency graph does not wait for the independent npm-build-all.yml or electron-smoke.yml workflows.
Cleanup PR caches
cache-cleanup.yml removes caches for a PR when it closes. PR-scoped caches are generally no longer useful after that PR has been closed.