240 lines
8.3 KiB
YAML
240 lines
8.3 KiB
YAML
name: Frontend
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
skip:
|
|
type: boolean
|
|
default: false
|
|
skip-lint:
|
|
type: boolean
|
|
default: false
|
|
skip-custom-viz:
|
|
type: boolean
|
|
default: false
|
|
# ID of this run's test-plan.json artifact, or '' to run the full suite.
|
|
test-plan-artifact-id:
|
|
type: string
|
|
default: ""
|
|
skip-ci-scripts:
|
|
type: boolean
|
|
default: false
|
|
workflow_dispatch:
|
|
inputs:
|
|
skip:
|
|
description: "Skip main frontend jobs (type-check, unit tests, timezone tests)"
|
|
type: boolean
|
|
default: false
|
|
skip-lint:
|
|
description: "Skip linting jobs (oxfmt, oxlint, css variables check)"
|
|
type: boolean
|
|
default: false
|
|
skip-custom-viz:
|
|
description: "Skip custom-viz package tests"
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}-frontend
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
# Consumed by the report-failures-to-ci-conductor step in fe-tests-unit. Absent
|
|
# (e.g. fork PRs without secret access) the reporter no-ops. Matches the other
|
|
# suite workflows (backend, drivers, e2e, app-db, semantic-search).
|
|
CI_CONDUCTOR_BASE_URL: ${{ vars.CI_CONDUCTOR_BASE_URL }}
|
|
CI_CONDUCTOR_WEBHOOK_SECRET: ${{ secrets.CI_CONDUCTOR_WEBHOOK_SECRET }}
|
|
|
|
jobs:
|
|
fe-lint:
|
|
if: ${{ !inputs.skip-lint }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare front-end environment
|
|
uses: ./.github/actions/prepare-frontend
|
|
- name: Prepare back-end environment
|
|
uses: ./.github/actions/prepare-backend
|
|
|
|
- name: Compile CLJS
|
|
run: bun run build-pure:cljs
|
|
- name: Find CSS variables that are used but never defined
|
|
run: bun run find-never-defined-css-variables
|
|
- name: Run oxfmt formatting linter
|
|
run: bun run lint-format-pure
|
|
- name: Run oxlint linter (including module boundaries)
|
|
run: bun run lint-oxlint-pure
|
|
- name: Verify lint configuration and behavior
|
|
run: bun run test-oxlint
|
|
- name: Run CSS linter
|
|
run: bun run lint-css
|
|
|
|
fe-type-check:
|
|
if: ${{ !inputs.skip }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare front-end environment
|
|
uses: ./.github/actions/prepare-frontend
|
|
- name: Prepare back-end environment
|
|
uses: ./.github/actions/prepare-backend
|
|
- run: bun run build-pure:cljs
|
|
name: Compile CLJS
|
|
- run: bun run type-check-pure
|
|
name: Check types
|
|
|
|
fe-tests-unit:
|
|
if: ${{ !inputs.skip }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2] # 2 shards for parallel execution
|
|
env:
|
|
SHARDS: 2 # should match matrix above
|
|
JEST_JUNIT_OUTPUT_DIR: ./target/junit
|
|
JEST_JUNIT_OUTPUT_NAME: test-report-frontend-unit.xml
|
|
UNIT_TEST_RESULTS_FILE: unit-test-results.json
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare front-end environment
|
|
uses: ./.github/actions/prepare-frontend
|
|
- name: Prepare back-end environment
|
|
uses: ./.github/actions/prepare-backend
|
|
- name: Resolve current job id
|
|
# Resolve JOB_ID before the long test step so the ci-conductor reporter
|
|
# below can link failures to this exact job. Best-effort.
|
|
uses: ./.github/actions/resolve-job-id
|
|
- name: Prepare unit test selection
|
|
id: unit-selection
|
|
if: ${{ inputs.test-plan-artifact-id != '' }}
|
|
uses: ./.github/actions/prepare-test-selection
|
|
with:
|
|
suite: unit
|
|
test-plan-artifact-id: ${{ inputs.test-plan-artifact-id }}
|
|
- name: Run frontend unit tests
|
|
id: fe-tests
|
|
env:
|
|
JEST_TEST_PATHS_FILE: ${{ steps.unit-selection.outputs.paths-file }}
|
|
# Jest exits non-zero on a shard with no files unless --passWithNoTests is set.
|
|
run: |
|
|
rm -f "$UNIT_TEST_RESULTS_FILE" "$JEST_JUNIT_OUTPUT_DIR/$JEST_JUNIT_OUTPUT_NAME"
|
|
bun run test-unit --silent --shard=${{ matrix.shard }}/${{ env.SHARDS }} --json --outputFile="$UNIT_TEST_RESULTS_FILE" ${{ steps.unit-selection.outputs.paths-file && '--passWithNoTests' || '' }}
|
|
# Ordinary assertion failures can be quarantined, but runner failures cannot.
|
|
continue-on-error: true
|
|
- name: Check unit test results
|
|
if: ${{ !cancelled() && (steps.fe-tests.outcome == 'failure' || steps.unit-selection.outputs.paths-file) }}
|
|
env:
|
|
TEST_OUTCOME: ${{ steps.fe-tests.outcome }}
|
|
JEST_TEST_PATHS_FILE: ${{ steps.unit-selection.outputs.paths-file }}
|
|
run: bun .github/scripts/check-unit-test-results.ts
|
|
- name: Report failed tests to ci-conductor
|
|
if: ${{ !cancelled() && ( steps.fe-tests.outcome == 'failure' || github.run_attempt != '1' ) }}
|
|
# This step should NEVER fail the job!
|
|
continue-on-error: true
|
|
uses: ./.github/actions/report-failures-to-ci-conductor
|
|
with:
|
|
test-suite: fe-tests-unit
|
|
adapter: frontend
|
|
- name: Quarantine gate
|
|
if: ${{ !cancelled() && steps.fe-tests.outcome == 'failure' }}
|
|
continue-on-error: ${{ vars.QUARANTINE_ENGINE != 'ci-conductor' }}
|
|
timeout-minutes: 3
|
|
uses: ./.github/actions/quarantine-gate
|
|
with:
|
|
adapter: frontend
|
|
test-suite: fe-tests-unit
|
|
enforce-quarantine: ${{ vars.QUARANTINE_ENGINE == 'ci-conductor' }}
|
|
|
|
fe-tests-timezones:
|
|
if: ${{ !inputs.skip }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare front-end environment
|
|
uses: ./.github/actions/prepare-frontend
|
|
- name: Run frontend timezones tests
|
|
run: bun run test-timezones
|
|
|
|
# No CLJS compile: the CI-script specs don't import app code.
|
|
fe-tests-ci-scripts:
|
|
if: ${{ !inputs.skip-ci-scripts }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare front-end environment
|
|
uses: ./.github/actions/prepare-frontend
|
|
- name: Run CI script tests
|
|
run: bun run test-ci-scripts
|
|
|
|
# The custom-viz package is an isolated Bun workspace under
|
|
# enterprise/frontend/src/custom-viz
|
|
fe-custom-viz:
|
|
if: ${{ !inputs.skip-custom-viz }}
|
|
runs-on: ${{ vars.DEFAULT_RUNNER_KEY }}
|
|
timeout-minutes: 10
|
|
defaults:
|
|
run:
|
|
working-directory: enterprise/frontend/src/custom-viz
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Prepare Node + Bun environment
|
|
uses: ./.github/actions/prepare-node-bun
|
|
- name: Install dependencies
|
|
run: bun install --frozen-lockfile
|
|
- name: Type-check
|
|
run: bun run type-check
|
|
- name: Lint
|
|
run: bun run lint
|
|
- name: Build CLI
|
|
run: bun run build
|
|
- name: Run tests
|
|
run: bun run test
|
|
|
|
fe-tests-result:
|
|
needs:
|
|
- fe-lint
|
|
- fe-type-check
|
|
- fe-tests-unit
|
|
- fe-tests-timezones
|
|
- fe-tests-ci-scripts
|
|
- fe-custom-viz
|
|
if: always() && !cancelled()
|
|
runs-on: ${{ vars.SLIM_RUNNER_KEY }}
|
|
timeout-minutes: 5
|
|
env:
|
|
needs: ${{ toJson(needs) }}
|
|
steps:
|
|
- name: Check frontend job status
|
|
uses: actions/github-script@v9
|
|
env:
|
|
needs: ${{ toJson(needs) }}
|
|
with:
|
|
script: | # js
|
|
const needs = JSON.parse(process.env.needs);
|
|
const jobs = Object.entries(needs).map(
|
|
([jobName, jobValues]) => ({
|
|
name: jobName,
|
|
result: jobValues.result
|
|
}));
|
|
|
|
// are all jobs skipped or successful?
|
|
if (jobs.every(job => (job.result === 'skipped' || job.result === 'success'))) {
|
|
console.log('All jobs are skipped or successful');
|
|
process.exit(0);
|
|
}
|
|
|
|
// otherwise, something failed
|
|
console.log('Some frontend jobs failed');
|
|
jobs.forEach((job) => {
|
|
if (job.result !== 'success') {
|
|
console.log(`${job.name} - ${job.result}`);
|
|
}
|
|
});
|
|
process.exit(1);
|