Skip to content

fix(sbom): add devextreme-extra-deps #10380

fix(sbom): add devextreme-extra-deps

fix(sbom): add devextreme-extra-deps #10380

name: Demos Visual Tests
concurrency:
group: wf-${{github.event.pull_request.number || github.sha}}-${{github.workflow}}
cancel-in-progress: true
on:
pull_request:
paths-ignore:
- 'apps/**/*.md'
merge_group:
workflow_dispatch:
env:
NX_SKIP_NX_CACHE: ${{ contains(github.event.pull_request.labels.*.name, 'skip-cache') && 'true' || 'false' }}
RUN_TESTS: true
jobs:
check-should-run:
name: Check if tests should run
runs-on: ubuntu-slim
outputs:
should-run: ${{ steps.check.outputs.should-run }}
steps:
- name: Check RUN_TESTS flag
id: check
run: echo "should-run=${{ env.RUN_TESTS }}" >> $GITHUB_OUTPUT
build-devextreme:
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
name: Build DevExtreme
needs: [check-should-run]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true'
env:
NODE_OPTIONS: --max-old-space-size=8192
timeout-minutes: 30
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v5
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Lookup nx cache
id: lookup-nx-cache
uses: actions/cache/restore@v5
with:
path: |
.nx/cache
.nx/workspace-data/*.db
.nx/workspace-data/*.db-wal
.nx/workspace-data/*.db-shm
key: ${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}-
${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-
${{ runner.os }}-nx-v2-
lookup-only: true
- name: Show nx cache status
shell: bash
run: |
echo "Nx cache lookup exact hit: ${{ steps.lookup-nx-cache.outputs.cache-hit }}"
echo "Nx cache primary key: ${{ steps.lookup-nx-cache.outputs.cache-primary-key }}"
echo "Nx cache matched key: ${{ steps.lookup-nx-cache.outputs.cache-matched-key }}"
if [ -z "${{ steps.lookup-nx-cache.outputs.cache-matched-key }}" ]; then
echo "Nx cache lookup: miss"
elif [ "${{ steps.lookup-nx-cache.outputs.cache-hit }}" = "true" ]; then
echo "Nx cache lookup: exact hit"
else
echo "Nx cache lookup: partial hit via restore-keys"
fi
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup nx cache
id: setup-nx-cache
uses: actions/cache@v5
with:
path: |
.nx/cache
.nx/workspace-data/*.db
.nx/workspace-data/*.db-wal
.nx/workspace-data/*.db-shm
key: ${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}-
${{ runner.os }}-nx-v2-${{ github.workflow }}-${{ github.job }}-
${{ runner.os }}-nx-v2-
- name: DevExtreme - Build-all
run: pnpm nx all:build-testing workflows
- name: Zip artifacts (for jQuery tests)
working-directory: ./packages/devextreme
run: |
7z a -tzip -mx3 -mmt2 artifacts.zip artifacts ../devextreme-scss/scss/bundles
- name: Upload build artifacts (for jQuery tests)
uses: actions/upload-artifact@v7
with:
name: devextreme-artifacts-jquery
path: ./packages/devextreme/artifacts.zip
retention-days: 1
- name: Move packages
run: |
mv ./packages/devextreme/artifacts/npm/devextreme/*.tgz ./devextreme-installer.tgz
mv ./packages/devextreme/artifacts/npm/devextreme-dist/*.tgz ./devextreme-dist-installer.tgz
mv ./packages/devextreme-angular/npm/dist/*.tgz ./devextreme-angular-installer.tgz
mv ./packages/devextreme-react/npm/*.tgz ./devextreme-react-installer.tgz
mv ./packages/devextreme-vue/npm/*.tgz ./devextreme-vue-installer.tgz
- name: Copy build artifacts
uses: actions/upload-artifact@v7
with:
name: devextreme-sources
path: |
devextreme-installer.tgz
devextreme-dist-installer.tgz
devextreme-angular-installer.tgz
devextreme-react-installer.tgz
devextreme-vue-installer.tgz
retention-days: 1
build-vendor-bundles:
name: Build vendor bundles (React, Vue)
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
timeout-minutes: 15
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
env:
NODE_OPTIONS: --max-old-space-size=8192
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
- name: Build vendor bundles
working-directory: apps/demos
run: node utils/server/build-vendor-bundles.js
- name: Copy vendor bundles
uses: actions/upload-artifact@v7
with:
name: devextreme-vendor-bundles
path: apps/demos/bundles/vendor
retention-days: 1
build-demos:
name: ${{ matrix.SHARD_TOTAL == 1 && format('Build demos ({0})', matrix.FRAMEWORK) || format('Build demos ({0} {1}/{2})', matrix.FRAMEWORK, matrix.SHARD_INDEX, matrix.SHARD_TOTAL) }}
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
timeout-minutes: 30
needs: [check-should-run, build-devextreme, build-vendor-bundles]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success' &&
needs.build-vendor-bundles.result == 'success'
env:
NODE_OPTIONS: --max-old-space-size=8192
strategy:
fail-fast: false
matrix:
include:
- { FRAMEWORK: React, SHARD_INDEX: 1, SHARD_TOTAL: 1 }
- { FRAMEWORK: Vue, SHARD_INDEX: 1, SHARD_TOTAL: 1 }
- { FRAMEWORK: Angular, SHARD_INDEX: 1, SHARD_TOTAL: 3 }
- { FRAMEWORK: Angular, SHARD_INDEX: 2, SHARD_TOTAL: 3 }
- { FRAMEWORK: Angular, SHARD_INDEX: 3, SHARD_TOTAL: 3 }
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
# - name: Build wrappers
# run: pnpm exec nx run-many -t pack -p devextreme-angular devextreme-react devetreme-vue
# - name: Link wrappers packages
# run: pnpm install --frozen-lockfile
# No-op for Angular (no manifest — see build-vendor-bundles).
- name: Download vendor bundles
uses: actions/download-artifact@v8
with:
name: devextreme-vendor-bundles
path: apps/demos/bundles/vendor
- name: Build demo bundles
working-directory: apps/demos
env:
CSP_SHARD_INDEX: ${{ matrix.SHARD_INDEX }}
CSP_SHARD_TOTAL: ${{ matrix.SHARD_TOTAL }}
run: node utils/server/csp-bundle.js --framework=${{ matrix.FRAMEWORK }}
- name: Demos - Run tsc
if: matrix.FRAMEWORK == 'React' && matrix.SHARD_INDEX == 1 # global check, only needs to run once
working-directory: apps/demos
run: pnpm exec tsc --noEmit
# Packed into one tarball — a glob path would lose the Demos/ prefix on extract.
- name: Pack demo bundles
working-directory: apps/demos
run: |
find Demos \( -name 'bundle.js' -o -name 'bundle.css' -o -path '*/_chunks/*.js' -o -path '*/media/*' \) -print0 \
| tar -czf demo-bundles.tgz --null --files-from -
tar -tzf demo-bundles.tgz | head -3
- name: Copy build artifacts
uses: actions/upload-artifact@v7
with:
name: devextreme-bundles-${{ matrix.FRAMEWORK }}${{ matrix.SHARD_TOTAL != 1 && format('-shard{0}', matrix.SHARD_INDEX) || '' }}
path: apps/demos/demo-bundles.tgz
retention-days: 1
lint:
name: Check TS and lint code base
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
- name: Demos - Check Vue TS
id: ts_vue
continue-on-error: true
working-directory: apps/demos
run: pnpm run ts-check-vue
# - name: Run check TS for Angular
# working-directory: apps/demos
# run: pnpm run ts-check-ng
- name: Demos - Check React TS
id: ts_react
continue-on-error: true
working-directory: apps/demos
run: pnpm run ts-check-react
- name: Run lint on all demos
id: lint_all
continue-on-error: true
working-directory: apps/demos
env:
DEBUG: 'eslint:cli-engine,stylelint:standalone'
NODE_OPTIONS: --max-old-space-size=8192
run: pnpm exec nx lint
- name: Check TS and lint results
if: always()
env:
TS_VUE_OUTCOME: ${{ steps.ts_vue.outcome }}
TS_REACT_OUTCOME: ${{ steps.ts_react.outcome }}
LINT_ALL_OUTCOME: ${{ steps.lint_all.outcome }}
run: |
failed=0
for check in \
"Demos - Check Vue TS:$TS_VUE_OUTCOME" \
"Demos - Check React TS:$TS_REACT_OUTCOME" \
"Run lint on all demos:$LINT_ALL_OUTCOME"; do
name="${check%%:*}"
outcome="${check#*:}"
if [ "$outcome" = "failure" ]; then
echo "::error::$name failed"
failed=1
fi
done
exit "$failed"
check-generated-demos-all:
name: Check generated demos
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
timeout-minutes: 20
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
steps:
- name: Get sources
uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
- name: Check generated JS demos
working-directory: apps/demos
run: |
echo "Running convert-to-js"
pnpm run convert-to-js
git add ./Demos -N
if git diff --exit-code . ':!package.json' ; then
echo "Generated JS demos are up-to-date"
else
echo "Generated JS demos are outdated. Execute 'pnpm run convert-to-js' and commit changes."
echo "If you see another diff, ensure that extra listed files have LF endings."
exit 1
fi
testcafe-jquery:
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
strategy:
fail-fast: false
matrix:
include:
- { STRATEGY: screenshots, THEME: material.blue.light, CONSTEL: jquery(1/3) }
- { STRATEGY: screenshots, THEME: material.blue.light, CONSTEL: jquery(2/3) }
- { STRATEGY: screenshots, THEME: material.blue.light, CONSTEL: jquery(3/3) }
- { STRATEGY: screenshots, THEME: fluent.blue.light, CONSTEL: jquery(1/3) }
- { STRATEGY: screenshots, THEME: fluent.blue.light, CONSTEL: jquery(2/3) }
- { STRATEGY: screenshots, THEME: fluent.blue.light, CONSTEL: jquery(3/3) }
- { STRATEGY: accessibility, THEME: material.blue.light, CONSTEL: jquery }
- { STRATEGY: accessibility, THEME: fluent.blue.light, CONSTEL: jquery }
- { STRATEGY: accessibility, THEME: material.blue.dark, CONSTEL: jquery }
- { STRATEGY: accessibility, THEME: fluent.blue.dark, CONSTEL: jquery }
env:
ACCESSIBILITY_TESTCAFE_REPORT_PATH: "accessibility_testcafe_report"
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
name: ${{ matrix.CONSTEL }}-${{ matrix.STRATEGY }}-${{ matrix.THEME }}
timeout-minutes: 30
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: devextreme-artifacts-jquery
path: ./packages/devextreme
- name: Unpack artifacts
working-directory: ./packages/devextreme
run: 7z x artifacts.zip -aoa
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
with:
chrome-version: '150.0.7871.181'
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set Roboto font directory
if: contains(matrix.THEME, 'material')
run: echo "ROBOTO_FONT_DIR=${HOME}/.local/share/fonts/roboto" >> $GITHUB_ENV
- name: Cache Roboto font
if: contains(matrix.THEME, 'material')
id: cache-roboto
uses: actions/cache@v5
with:
path: ${{ env.ROBOTO_FONT_DIR }}
key: roboto-font-${{ runner.os }}-8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5
- name: Install Roboto font for Material theme
if: contains(matrix.THEME, 'material') && steps.cache-roboto.outputs.cache-hit != 'true'
env:
GOOGLE_FONTS_COMMIT: 8b0a1d0f5983c89bc2b93f1b5fb55f9e252744b5
ROBOTO_SHA256: d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134
run: |
echo "Installing Roboto font from google/fonts (pinned commit)..."
mkdir -p "$ROBOTO_FONT_DIR"
BASE_URL="https://raw.githubusercontent.com/google/fonts/${GOOGLE_FONTS_COMMIT}/ofl/roboto"
curl -fsSL "${BASE_URL}/Roboto%5Bwdth%2Cwght%5D.ttf" -o /tmp/Roboto.ttf
echo "${ROBOTO_SHA256} /tmp/Roboto.ttf" | sha256sum -c -
mv /tmp/Roboto.ttf "$ROBOTO_FONT_DIR/"
echo "Roboto font installed"
- name: Refresh font cache
if: contains(matrix.THEME, 'material')
run: fc-cache -f > /dev/null 2>&1
- name: Run Web Server
run: python -m http.server 8080 &
- name: Set Chrome flags
id: chrome-flags
run: |
BASE_FLAGS="chrome:headless --window-size=1200,800 --disable-gpu --no-sandbox --disable-dev-shm-usage --disable-partial-raster --disable-skia-runtime-opts --run-all-compositor-stages-before-draw --disable-new-content-rendering-timeout --disable-threaded-animation --disable-threaded-scrolling --disable-checker-imaging --disable-image-animation-resync --use-gl=swiftshader --disable-features=PaintHolding --js-flags=--random-seed=2147483647"
# For Material theme, enable better font rendering to avoid instability
if [[ "${{ matrix.THEME }}" != *"material"* ]]; then
BASE_FLAGS="$BASE_FLAGS --font-render-hinting=none --disable-font-subpixel-positioning"
fi
echo "flags=$BASE_FLAGS" >> $GITHUB_OUTPUT
- name: Run TestCafe tests (jQuery)
shell: bash
working-directory: apps/demos
env:
NODE_OPTIONS: --max-old-space-size=8192
STRATEGY: ${{ matrix.STRATEGY }}
CHANGEDFILEINFOSPATH: changed-files.json
BROWSERS: ${{ steps.chrome-flags.outputs.flags }}
#DEBUG: hammerhead:*,testcafe:*
CONCURRENCY: 4
CONSTEL: ${{ matrix.CONSTEL }}
THEME: ${{ matrix.THEME }}
# DISABLE_DEMO_TEST_SETTINGS: all # Uncomment to ignore all the visualtestrc.json settings
# DISABLE_DEMO_TEST_SETTINGS: ignore # Uncomment to ignore the `ignore` field
# DISABLE_DEMO_TEST_SETTINGS: comparison-options # Uncomment to ignore the `comparison-options` field
CI_ENV: true # The `ignore` field in the visualtestrc.json should be disabled when running test locally
run: pnpm exec nx test-testcafe
- name: Show accessibility warnings
if: matrix.STRATEGY == 'accessibility'
working-directory: apps/demos
run: |
if [ -f "$ACCESSIBILITY_TESTCAFE_REPORT_PATH" ]; then
message=$(cat $ACCESSIBILITY_TESTCAFE_REPORT_PATH)
echo "::warning ::$message"
else
echo "Accessibility report not found. Skipping warning generation."
fi
- name: Sanitize job name
if: ${{ failure() }}
run: echo "JOB_NAME=$(echo "${{ matrix.CONSTEL }}-${{ matrix.THEME }}" | tr '/' '-')" >> $GITHUB_ENV
- name: Copy screenshots artifacts
if: failure() && matrix.STRATEGY == 'screenshots'
uses: actions/upload-artifact@v7
with:
name: screenshots-${{ env.JOB_NAME }}
path: ${{ github.workspace }}/apps/demos/testing/artifacts/compared-screenshots/**/*
if-no-files-found: ignore
- name: Copy accessibility report
if: matrix.STRATEGY == 'accessibility'
uses: actions/upload-artifact@v7
with:
name: accessibility-reports-${{ env.JOB_NAME }}-${{ matrix.THEME }}
path: apps/demos/testing/artifacts/axe-reports/*
if-no-files-found: ignore
testcafe-frameworks-all:
needs: [check-should-run, build-demos, build-vendor-bundles]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-demos.result == 'success'
strategy:
fail-fast: false
matrix:
CONSTEL: [
react(1/3),
react(2/3),
react(3/3),
vue(1/5),
vue(2/5),
vue(3/5),
vue(4/5),
vue(5/5),
angular(1/10),
angular(2/10),
angular(3/10),
angular(4/10),
angular(5/10),
angular(6/10),
angular(7/10),
angular(8/10),
angular(9/10),
angular(10/10),
]
THEME: ['fluent.blue.light']
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
name: ${{ matrix.CONSTEL }}-screenshots-${{ matrix.THEME }}
timeout-minutes: 60
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
with:
chrome-version: '150.0.7871.181'
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
- name: Download demo bundles (React)
if: startsWith(matrix.CONSTEL, 'react')
uses: actions/download-artifact@v8
with:
name: devextreme-bundles-React
path: apps/demos/.demo-bundles/react
- name: Download demo bundles (Vue)
if: startsWith(matrix.CONSTEL, 'vue')
uses: actions/download-artifact@v8
with:
name: devextreme-bundles-Vue
path: apps/demos/.demo-bundles/vue
# Angular is built in 3 shards but tested in 10 — every test shard needs all 3.
- name: Download demo bundles (Angular shard 1)
if: startsWith(matrix.CONSTEL, 'angular')
uses: actions/download-artifact@v8
with:
name: devextreme-bundles-Angular-shard1
path: apps/demos/.demo-bundles/angular-shard1
- name: Download demo bundles (Angular shard 2)
if: startsWith(matrix.CONSTEL, 'angular')
uses: actions/download-artifact@v8
with:
name: devextreme-bundles-Angular-shard2
path: apps/demos/.demo-bundles/angular-shard2
- name: Download demo bundles (Angular shard 3)
if: startsWith(matrix.CONSTEL, 'angular')
uses: actions/download-artifact@v8
with:
name: devextreme-bundles-Angular-shard3
path: apps/demos/.demo-bundles/angular-shard3
# No-op for angular demos (no manifest — see build-vendor-bundles.js).
- name: Download vendor bundles
uses: actions/download-artifact@v8
with:
name: devextreme-vendor-bundles
path: apps/demos/bundles/vendor
- name: Unpack demo bundles
run: |
shopt -s nullglob
archives=(apps/demos/.demo-bundles/*/demo-bundles.tgz)
if [ ${#archives[@]} -eq 0 ]; then
echo "No demo bundle archives downloaded for constellation '${{ matrix.CONSTEL }}'" >&2
exit 1
fi
for archive in "${archives[@]}"; do
tar -xzf "$archive" -C apps/demos
done
rm -rf apps/demos/.demo-bundles
echo "Unpacked $(find apps/demos/Demos -name 'bundle.js' | wc -l) demo bundle(s)"
- name: Run Web Server
run: |
python -m http.server 8080 &
- name: Check runner resources
run: |
echo "CPU cores: $(nproc)"
echo "Memory: $(free -h)"
echo "Disk: $(df -h)"
- name: Set concurrency based on framework
id: set-concurrency
run: |
if [[ "${{ matrix.CONSTEL }}" == react* ]]; then
echo "concurrency=4" >> $GITHUB_OUTPUT
elif [[ "${{ matrix.CONSTEL }}" == vue* ]]; then
echo "concurrency=3" >> $GITHUB_OUTPUT
elif [[ "${{ matrix.CONSTEL }}" == angular* ]]; then
echo "concurrency=3" >> $GITHUB_OUTPUT
else
echo "concurrency=2" >> $GITHUB_OUTPUT
fi
- name: Run TestCafe tests
shell: bash
working-directory: apps/demos
env:
NODE_OPTIONS: --max-old-space-size=8192
BROWSERS: chrome:headless --window-size=1200,800 --disable-gpu --no-sandbox --disable-dev-shm-usage --disable-partial-raster --disable-skia-runtime-opts --run-all-compositor-stages-before-draw --disable-new-content-rendering-timeout --disable-threaded-animation --disable-threaded-scrolling --disable-checker-imaging --disable-image-animation-resync --use-gl=swiftshader --disable-features=PaintHolding --js-flags=--random-seed=2147483647 --font-render-hinting=none --disable-font-subpixel-positioning
# DEBUG: hammerhead:*,testcafe:*
CONCURRENCY: ${{ steps.set-concurrency.outputs.concurrency }}
CONSTEL: ${{ matrix.CONSTEL }}
THEME: ${{ matrix.THEME }}
# DISABLE_DEMO_TEST_SETTINGS: all # Uncomment to ignore all the visualtestrc.json settings
# DISABLE_DEMO_TEST_SETTINGS: ignore # Uncomment to ignore the `ignore` field
# DISABLE_DEMO_TEST_SETTINGS: comparison-options # Uncomment to ignore the `comparison-options` field
CI_ENV: true # The `ignore` field in the visualtestrc.json should be disabled when running test locally
run: pnpm exec nx test-testcafe
- name: Sanitize job name
if: ${{ failure() }}
run: echo "JOB_NAME=$(echo "${{ matrix.CONSTEL }}-${{ matrix.THEME }}" | tr '/' '-')" >> $GITHUB_ENV
- name: Copy screenshots artifacts
if: ${{ failure() }}
uses: actions/upload-artifact@v7
with:
name: screenshots-${{ env.JOB_NAME }}
path: ${{ github.workspace }}/apps/demos/testing/artifacts/compared-screenshots/**/*
if-no-files-found: ignore
merge-artifacts:
runs-on: ubuntu-latest
needs: [testcafe-jquery, testcafe-frameworks-all]
if: ${{ !cancelled() && (needs.testcafe-jquery.result == 'failure' || needs.testcafe-frameworks-all.result == 'failure') }}
steps:
- name: Merge jQuery screenshot artifacts
if: needs.testcafe-jquery.result == 'failure'
uses: actions/upload-artifact/merge@v7
continue-on-error: true
with:
name: screenshots-jquery
pattern: screenshots-jquery*
delete-merged: true
- name: Merge React screenshot artifacts
if: needs.testcafe-frameworks-all.result == 'failure'
uses: actions/upload-artifact/merge@v7
continue-on-error: true
with:
name: screenshots-react
pattern: screenshots-react*
delete-merged: true
- name: Merge Vue screenshot artifacts
if: needs.testcafe-frameworks-all.result == 'failure'
uses: actions/upload-artifact/merge@v7
continue-on-error: true
with:
name: screenshots-vue
pattern: screenshots-vue*
delete-merged: true
- name: Merge Angular screenshot artifacts
if: needs.testcafe-frameworks-all.result == 'failure'
uses: actions/upload-artifact/merge@v7
continue-on-error: true
with:
name: screenshots-angular
pattern: screenshots-angular*
delete-merged: true
- name: Merge jQuery accessibility reports
if: needs.testcafe-jquery.result == 'failure'
uses: actions/upload-artifact/merge@v7
continue-on-error: true
with:
name: accessibility-reports-jquery
pattern: accessibility-reports-*
delete-merged: true
csp-check-jquery:
name: CSP check (jQuery)
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
timeout-minutes: 60
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Download artifacts
uses: actions/download-artifact@v8
with:
name: devextreme-artifacts-jquery
path: ./packages/devextreme
- name: Unpack artifacts
working-directory: ./packages/devextreme
run: 7z x artifacts.zip -aoa
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
with:
chrome-version: '150.0.7871.181'
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Start CSP Server
run: node apps/demos/utils/server/csp-server.js 8080 &
- name: Run CSP Check
working-directory: apps/demos
env:
CSP_FRAMEWORKS: jQuery
CHROME_PATH: google-chrome-stable
run: node utils/server/csp-check.js
- name: Upload CSP report
if: always()
uses: actions/upload-artifact@v7
with:
name: csp-violations-jquery
path: apps/demos/csp-reports/
if-no-files-found: ignore
csp-check-frameworks:
name: ${{ matrix.SHARD_TOTAL == 1 && format('CSP check ({0})', matrix.FRAMEWORK) || format('CSP check ({0} {1}/{2})', matrix.FRAMEWORK, matrix.SHARD_INDEX, matrix.SHARD_TOTAL) }}
needs: [check-should-run, build-devextreme]
if: |
!cancelled() &&
needs.check-should-run.outputs.should-run == 'true' &&
needs.build-devextreme.result == 'success'
strategy:
fail-fast: false
# Angular AOT bundling is slow, so it is split into parallel shards
# (CSP_SHARD_*, round-robin in csp-bundle-angular.js). React/Vue stay light.
matrix:
include:
- { FRAMEWORK: React, SHARD_INDEX: 1, SHARD_TOTAL: 2 }
- { FRAMEWORK: React, SHARD_INDEX: 2, SHARD_TOTAL: 2 }
- { FRAMEWORK: Vue, SHARD_INDEX: 1, SHARD_TOTAL: 2 }
- { FRAMEWORK: Vue, SHARD_INDEX: 2, SHARD_TOTAL: 2 }
- { FRAMEWORK: Angular, SHARD_INDEX: 1, SHARD_TOTAL: 3 }
- { FRAMEWORK: Angular, SHARD_INDEX: 2, SHARD_TOTAL: 3 }
- { FRAMEWORK: Angular, SHARD_INDEX: 3, SHARD_TOTAL: 3 }
runs-on: ${{ github.event_name == 'merge_group' && 'devextreme-shr2-ondemand' || 'devextreme-shr2' }}
timeout-minutes: 60
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Download devextreme sources
uses: actions/download-artifact@v8
with:
name: devextreme-sources
- name: Setup Chrome
uses: ./.github/actions/setup-chrome
with:
chrome-version: '150.0.7871.181'
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- uses: pnpm/action-setup@v6
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache/restore@v5
name: Restore pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-cache
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install tgz
working-directory: apps/demos
run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml
# Rebuilt independently rather than depending on build-demos, to keep timing decoupled.
- name: Bundle demos for CSP check
working-directory: apps/demos
env:
CSP_SHARD_INDEX: ${{ matrix.SHARD_INDEX }}
CSP_SHARD_TOTAL: ${{ matrix.SHARD_TOTAL }}
run: node utils/server/csp-bundle.js --framework=${{ matrix.FRAMEWORK }}
- name: Start CSP Server
run: node apps/demos/utils/server/csp-server.js 8080 &
- name: Run CSP Check
working-directory: apps/demos
env:
CSP_FRAMEWORKS: ${{ matrix.FRAMEWORK }}
CHROME_PATH: google-chrome-stable
CSP_SHARD_INDEX: ${{ matrix.SHARD_INDEX }}
CSP_SHARD_TOTAL: ${{ matrix.SHARD_TOTAL }}
run: node utils/server/csp-check.js
- name: Upload CSP report
if: always()
uses: actions/upload-artifact@v7
with:
name: csp-violations-${{ matrix.FRAMEWORK }}${{ matrix.SHARD_TOTAL != 1 && format('-shard{0}', matrix.SHARD_INDEX) || '' }}
path: apps/demos/csp-reports/
if-no-files-found: ignore
csp-report-summary:
name: CSP Violations Summary
runs-on: ubuntu-latest
needs: [check-should-run, csp-check-jquery, csp-check-frameworks]
if: ${{ !cancelled() && needs.check-should-run.outputs.should-run == 'true' }}
timeout-minutes: 5
steps:
- name: Get sources
uses: actions/checkout@v6
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
- name: Download all CSP reports
uses: actions/download-artifact@v8
with:
pattern: csp-violations-*
path: csp-reports-all
merge-multiple: true
continue-on-error: true
- name: Summarize CSP violations
run: |
mkdir -p apps/demos/csp-reports
echo "## CSP Violations Report" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
GRAND_TOTAL=0
for report in csp-reports-all/csp-violations-*.jsonl; do
[ -f "$report" ] || continue
FRAMEWORK=$(basename "$report" | sed 's/csp-violations-//;s/\.jsonl//')
cp "$report" "apps/demos/csp-reports/"
if [ -s "$report" ]; then
COUNT=$(wc -l < "$report" | tr -d ' ')
GRAND_TOTAL=$((GRAND_TOTAL + COUNT))
echo "### ⚠️ ${FRAMEWORK}: ${COUNT} violation(s)" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '<details>' >> $GITHUB_STEP_SUMMARY
echo '<summary>Show detailed report</summary>' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
CSP_REPORT_FILE="$report" node apps/demos/utils/server/csp-report-summary.js >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '</details>' >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
else
echo "### ✅ ${FRAMEWORK}: No violations" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
fi
done
if [ "$GRAND_TOTAL" -eq 0 ]; then
echo "✅ No CSP violations detected across all frameworks."
else
echo "⚠️ Total: $GRAND_TOTAL CSP violation(s)"
fi
- name: Upload merged CSP reports
if: always()
uses: actions/upload-artifact@v7
with:
name: csp-violations-report
path: apps/demos/csp-reports/
if-no-files-found: ignore