Skip to content

build

build #1836

Workflow file for this run

name: build
on:
push:
branches:
- main
pull_request:
schedule:
- cron: "15 2,10,18 * * *"
# GitHub disables a scheduled workflow after 60 days of repository
# inactivity, which stops it running on pull requests too. Dispatch is
# how to re-run it after that without pushing to a branch.
workflow_dispatch:
jobs:
test:
name: Build with Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22, latest]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- uses: pnpm/action-setup@v4
with:
version: 10.12.4
run_install: true
- name: Linting
run: pnpm eslint
# `pnpm format` writes; nothing verified the result, so formatting drifted
# into files no other gate reads.
- name: Formatting
run: pnpm format:check
- name: Type-checking
run: pnpm typecheck
# This contract uses DOM libraries with no @types/node and covers only
# the source graph shipped in @questdb/browser-client.
- name: Type-checking (browser)
run: pnpm typecheck:qwp-browser
# `pnpm typecheck` covers src plus a single contract file, so nothing
# type-checked test/** at all: a test could reference a deleted export
# and still pass, because vitest strips types without checking them.
- name: Type-checking (tests)
run: pnpm typecheck:test
- name: Type-checking (benchmarks)
run: pnpm typecheck:bench
- name: Linting (benchmarks)
run: pnpm lint:bench
- name: Tests
run: pnpm test
# Loads the built bundles through package.json `exports`, the way a
# consumer does, and verifies the two npm package boundaries.
- name: Built package tests
run: pnpm test:dist
# test:dist asserts runtime behaviour only. The compiled writers promise
# per-column row typing, which lives entirely in the emitted .d.ts files
# and is therefore invisible to source-level tests, where the public
# entries share one private-core module instance.
- name: Type-checking (built package consumer)
run: pnpm typecheck:dist
# The same gate publish.yml runs. Keeping it here too means a chunk that
# escapes `files`, or a regression in the check itself, fails a pull
# request instead of first being discovered during a release.
- name: Check for build artifacts
run: node scripts/check-build-artifacts.mjs
# Drives the built browser bundle in real Chromium against a local mock
# server. Tests that need a live QuestDB belong in the server repositories,
# where the topology and authentication fixtures already exist.
qwp-browser:
name: QWP browser bundle
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: 10.12.4
run_install: true
- name: Install Chromium
run: pnpm exec playwright install --with-deps chromium
- name: Browser bundle tests
run: pnpm test:qwp-browser
enterprise-qwp-e2e:
name: Dispatch Enterprise QWP E2E
runs-on: ubuntu-latest
# Azure credentials are not exposed to fork PRs. The repository variable
# keeps this dormant until the Enterprise pipeline and PAT are configured.
if: >-
vars.ENTERPRISE_E2E_ENABLED == 'true' &&
(github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository)
steps:
- name: Queue Enterprise TypeScript-client E2E
env:
ENT_DISPATCH_PAT: ${{ secrets.ENT_DISPATCH_PAT }}
CLIENT_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
CLIENT_PR_NUMBER: ${{ github.event.pull_request.number || '' }}
CLIENT_BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
set -euo pipefail
if [ -z "${ENT_DISPATCH_PAT:-}" ]; then
echo "ENT_DISPATCH_PAT is not configured" >&2
exit 1
fi
ORG_URL="https://dev.azure.com/questdb/"
PROJECT="questdb-enterprise"
PIPELINE_NAME="build-and-test-e2e-javascript-client"
# Azure explains a rejection in the response body. --fail-with-body
# keeps it, but on stdout, which $(...) captures; under set -e the
# step would exit before anything printed it. Each call therefore
# reports the captured body itself when curl fails: Azure's JSON
# `message` when it is a non-empty string, otherwise the start of the
# body. The filter outputs nothing for anything else, so jq -e fails
# without first printing `null` or another non-string value.
# An empty body means curl failed before Azure answered (DNS, TLS,
# connection) or Azure sent no explanation; curl's own -sS line
# above then names the cause, so don't blame Azure for it.
azure_failure() {
if [ -z "$2" ]; then
echo "The Azure DevOps $1 failed without a response body; see curl's error above." >&2
else
echo "Azure DevOps rejected the $1: $(
jq -er '.message | strings | select(length > 0)' <<<"$2" 2>/dev/null ||
head -c 500 <<<"$2"
)" >&2
fi
}
if ! PIPELINES=$(curl --fail-with-body -sS -u ":${ENT_DISPATCH_PAT}" \
"${ORG_URL}${PROJECT}/_apis/pipelines?api-version=7.0"); then
azure_failure "pipeline lookup" "$PIPELINES"
exit 1
fi
PIPELINE_ID=$(echo "$PIPELINES" | jq -r --arg name "$PIPELINE_NAME" \
'.value[] | select(.name == $name) | .id' | head -1)
if [ -z "$PIPELINE_ID" ] || [ "$PIPELINE_ID" = "null" ]; then
echo "Enterprise pipeline '$PIPELINE_NAME' is not registered" >&2
exit 1
fi
BODY=$(jq -n \
--arg commit "$CLIENT_COMMIT" \
--arg pr "$CLIENT_PR_NUMBER" \
--arg branch "$CLIENT_BRANCH" \
'{
templateParameters: (
{
javascriptClientCommit: $commit,
clientBranch: $branch
}
# Azure rejects an empty string for a string parameter
# ("is not a valid String", HTTP 400), which broke every
# push, schedule and workflow_dispatch run. Omitting it
# applies the pipeline default, which skips the GitHub
# status post.
+ (if $pr == "" then {} else {javascriptClientPrNumber: $pr} end)
)
}')
if ! RESPONSE=$(curl --fail-with-body -sS -u ":${ENT_DISPATCH_PAT}" \
-H "Content-Type: application/json" \
-X POST \
-d "$BODY" \
"${ORG_URL}${PROJECT}/_apis/pipelines/${PIPELINE_ID}/runs?api-version=7.0"); then
azure_failure "dispatch" "$RESPONSE"
exit 1
fi
echo "Enterprise E2E queued: $(echo "$RESPONSE" | jq -r '._links.web.href')"