Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7fb87e0
Move fonts folder
camdecoster Apr 13, 2026
deca8dc
Move scripts
camdecoster Apr 13, 2026
2efa0d8
Add GH folder to Biome config
camdecoster Apr 13, 2026
3554a61
Split USA choropleths into two mocks
camdecoster Apr 13, 2026
62a8c26
Increase CI browser window size
camdecoster Apr 13, 2026
8c00864
Add sharding script
camdecoster Apr 14, 2026
db2cf3f
Add composite actions
camdecoster Apr 14, 2026
b5e862f
Update image comparison script
camdecoster Apr 14, 2026
e817a04
Refactor mock test script and add multi-threading
camdecoster Apr 14, 2026
4c29f84
Add GHA workflow for CI
camdecoster Apr 14, 2026
621e272
Remove obsolete CI workflows
camdecoster Apr 14, 2026
fbf9a2b
Remove baseline image prefix
camdecoster Apr 14, 2026
0949c23
Update/refactor tests for new CI
camdecoster Apr 14, 2026
d14b701
Add missing execute permissions
camdecoster Apr 14, 2026
eb5ecd1
Linting/formatting
camdecoster Apr 14, 2026
6aafd12
Update image baselines
camdecoster Apr 14, 2026
d6928f9
Refactor image generation scripts
camdecoster Apr 14, 2026
b0348e9
Add virtual-webgl specific baseline images
camdecoster Apr 14, 2026
38bf120
Pin third party actions to commit hashes
camdecoster Apr 14, 2026
7dd54cb
Pin third party actions to commit hashes in composite actions
camdecoster Apr 14, 2026
e1aaebf
Move fonts
camdecoster Apr 15, 2026
36051b8
Add sources info for fonts
camdecoster Apr 15, 2026
c0a4192
Remove extra archive step and make builds available uncompressed
camdecoster Apr 16, 2026
266b454
Add detect changes step to CI workflow
camdecoster Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
606 changes: 0 additions & 606 deletions .circleci/config.yml

This file was deleted.

82 changes: 0 additions & 82 deletions .circleci/download_google_fonts.py

This file was deleted.

21 changes: 0 additions & 21 deletions .circleci/env_image.sh

This file was deleted.

13 changes: 13 additions & 0 deletions .github/actions/run-xvfb/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: 'Run with Xvfb'
description: 'Run a command under Xvfb with a preconfigured screen size'

inputs:
run:
description: 'Command to execute'
required: true

runs:
using: 'composite'
steps:
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x1024x24" ${{ inputs.run }}
shell: bash
25 changes: 25 additions & 0 deletions .github/actions/setup-chrome/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'Setup Chrome'
Comment thread
camdecoster marked this conversation as resolved.
description: 'Install Chrome and set CHROME_BIN environment variable'

inputs:
chrome-version:
description: 'Chrome version to install'
default: '136.0.7103.113'

runs:
using: 'composite'
steps:
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2
id: setup-chrome
with:
chrome-version: ${{ inputs.chrome-version }}

- name: Set Chrome binary path
run: echo "CHROME_BIN=${{ steps.setup-chrome.outputs.chrome-path }}" >> $GITHUB_ENV
shell: bash

- name: Verify Chrome version
run: |
echo "Chrome path: $CHROME_BIN"
$CHROME_BIN --version
shell: bash
35 changes: 35 additions & 0 deletions .github/actions/setup-image-env/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: 'Setup Image Environment'
description: 'Setup Python, uv, and install Kaleido/plotly with required fonts'

inputs:
python-version:
description: 'Python version to use'
default: '3.12'
uv-version:
description: 'uv version to use'
default: '0.11.2'

runs:
using: 'composite'
steps:
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ inputs.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
version: ${{ inputs.uv-version }}

- name: Cache apt font packages
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
id: apt-cache
with:
path: ~/.cache/apt-fonts
key: apt-fonts-${{ runner.os }}-${{ hashFiles('.github/scripts/env_image.sh') }}

- name: Install Kaleido, plotly.io and required fonts
run: .github/scripts/env_image.sh
shell: bash
env:
APT_CACHE_HIT: ${{ steps.apt-cache.outputs.cache-hit }}
26 changes: 26 additions & 0 deletions .github/actions/setup-workspace/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: 'Setup Workspace'
description: 'Setup Node.js, install dependencies, and download build artifacts'

inputs:
node-version:
description: 'Node.js version to use'
default: '18'

runs:
using: 'composite'
steps:
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'

- name: Install dependencies
run: npm ci
shell: bash
env:
NODE_OPTIONS: '--max-old-space-size=4096'

- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: build-output
path: .
14 changes: 14 additions & 0 deletions .github/fonts/SOURCES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Font Sources

These fonts were downloaded from Google Fonts and related repositories for use in image baseline generation during CI. They ensure consistent text rendering across environments.

They can be re-downloaded using the script at `.github/scripts/download_google_fonts.py`.

- **NotoSansMono** (Regular, Bold) — [notofonts/notofonts.github.io](https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSansMono/hinted/ttf/)
- **NotoSans** (Regular, Italic, Bold) — [notofonts/notofonts.github.io](https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSans/hinted/ttf/)
- **NotoSerif** (Regular, Italic, Bold, BoldItalic) — [notofonts/notofonts.github.io](https://cdn.jsdelivr.net/gh/notofonts/notofonts.github.io/fonts/NotoSerif/hinted/ttf/)
- **OldStandard** (Regular, Italic, Bold) — [google/fonts](https://raw.githubusercontent.com/google/fonts/refs/heads/main/ofl/oldstandardtt/)
- **PT Sans Narrow** (Regular, Bold) — [google/fonts](https://raw.githubusercontent.com/google/fonts/refs/heads/main/ofl/ptsansnarrow/)
- **Raleway** (Regular, Regular-Italic, Bold, Bold-Italic) — [impallari/Raleway](https://raw.githubusercontent.com/impallari/Raleway/refs/heads/master/fonts/v3.000%20Fontlab/TTF/)
- **Roboto** (Regular, Italic, Bold, BoldItalic) — [googlefonts/roboto-2](https://raw.githubusercontent.com/googlefonts/roboto-2/refs/heads/main/src/hinted/)
- **GravitasOne** (400Regular) — [expo/google-fonts](https://raw.githubusercontent.com/expo/google-fonts/refs/heads/main/font-packages/gravitas-one/400Regular/)
File renamed without changes.
36 changes: 36 additions & 0 deletions .github/scripts/env_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh
set -e

APT_PACKAGES="fonts-liberation2 fonts-open-sans fonts-noto-cjk fonts-noto-color-emoji fontconfig"
APT_CACHE_DIR="${HOME}/.cache/apt-fonts"

if [ "$APT_CACHE_HIT" = "true" ] && [ -d "$APT_CACHE_DIR" ]; then
echo "Installing font packages from cache..."
sudo dpkg -i "$APT_CACHE_DIR"/*.deb 2>/dev/null || sudo apt-get install -yf
else
echo "Downloading and installing font packages..."
sudo apt-get update -q
sudo apt-get install -y --no-install-recommends $APT_PACKAGES
# Save debs for future cache
mkdir -p "$APT_CACHE_DIR"
for pkg in $APT_PACKAGES; do
cp /var/cache/apt/archives/${pkg}_*.deb "$APT_CACHE_DIR/" 2>/dev/null || true
done
fi

# Rebuild font cache
sudo fc-cache -f

# Install additional fonts (committed in .github/fonts/)
sudo cp -r .github/fonts/ /usr/share/
sudo fc-cache -f

# Install Kaleido & Plotly
uv pip install --system kaleido==0.2.1 plotly==6.6.0 --no-progress

# Install numpy i.e. to convert arrays to typed arrays
uv pip install --system numpy==2.4.3

# Verify version of python and versions of installed python packages
python --version
uv pip freeze --system
19 changes: 19 additions & 0 deletions .github/scripts/split_files.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env node

// Reads lines from stdin and emits only those where index % SHARD_TOTAL == SHARD_INDEX
// Environment variables SHARD_INDEX and SHARD_TOTAL must be set.

import { readFileSync } from 'fs';

const lines = readFileSync('/dev/stdin', 'utf8').trim().split('\n').filter(Boolean);
const index = parseInt(process.env.SHARD_INDEX);
const total = parseInt(process.env.SHARD_TOTAL);

if (isNaN(index) || isNaN(total) || total <= 0) {
console.error('SHARD_INDEX and SHARD_TOTAL environment variables must be set to valid integers');
process.exit(1);
}

lines.forEach((line, i) => {
if (i % total === index) console.log(line);
});
44 changes: 28 additions & 16 deletions .circleci/test.sh → .github/scripts/test.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#!/bin/bash

# override CircleCi's default run settings
# GitHub Actions version of .circleci/test.sh
# Replaces `circleci tests split` with split_files.mjs

set +e
set +o pipefail

ROOT=$(dirname $0)/..
ROOT=$(dirname $0)/../..
SPLIT="$ROOT/.github/scripts/split_files.mjs"
EXIT_STATE=0
MAX_AUTO_RETRY=0

Expand Down Expand Up @@ -33,18 +36,21 @@ retry () {
fi
}

# Ensure output directories exist (not present in fresh GHA checkout)
mkdir -p build/test_images

case $1 in

no-gl-jasmine)
SUITE=$(circleci tests glob "$ROOT/test/jasmine/tests/*" | circleci tests split)
SUITE=$(ls -1 $ROOT/test/jasmine/tests/* | sort | node "$SPLIT")
MAX_AUTO_RETRY=2
retry npm run test-jasmine -- $SUITE --skip-tags=gl,noCI,flaky || EXIT_STATE=$?

exit $EXIT_STATE
;;

webgl-jasmine)
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | circleci tests split))
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | node "$SPLIT"))
for s in ${SHARDS[@]}; do
MAX_AUTO_RETRY=2
retry npm run test-jasmine -- "$s" --tags=gl --skip-tags=noCI --doNotFailOnEmptyTestSuite
Expand All @@ -54,7 +60,7 @@ case $1 in
;;

virtual-webgl-jasmine)
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | circleci tests split))
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=5 --tag=gl | node "$SPLIT"))
for s in ${SHARDS[@]}; do
MAX_AUTO_RETRY=2
retry ./node_modules/karma/bin/karma start test/jasmine/karma.conf.js --virtualWebgl --tags=gl --skip-tags=noCI,noVirtualWebgl --doNotFailOnEmptyTestSuite -- "$s"
Expand All @@ -64,7 +70,7 @@ case $1 in
;;

flaky-no-gl-jasmine)
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=1 --tag=flaky | circleci tests split))
SHARDS=($(node $ROOT/tasks/shard_jasmine_tests.js --limit=1 --tag=flaky | node "$SPLIT"))

for s in ${SHARDS[@]}; do
MAX_AUTO_RETRY=5
Expand Down Expand Up @@ -96,40 +102,46 @@ case $1 in
SUITE=$({\
find $ROOT/test/image/mocks/gl* -type f -printf "%f\n"; \
find $ROOT/test/image/mocks/map* -type f -printf "%f\n"; \
} | sed 's/\.json$//1' | circleci tests split)
sudo python3 test/image/make_baseline.py virtual-webgl $SUITE || EXIT_STATE=$?
} | sed 's/\.json$//1' | sort | node "$SPLIT")
python test/image/make_baseline.py virtual-webgl $SUITE || EXIT_STATE=$?
exit $EXIT_STATE
;;

make-baselines-mathjax3)
sudo python3 test/image/make_baseline.py mathjax3 legend_mathjax_title_and_items mathjax parcats_grid_subplots table_latex_multitrace_scatter table_plain_birds table_wrapped_birds ternary-mathjax ternary-mathjax-title-place-subtitle || EXIT_STATE=$?
MATHJAX3_MOCKS=$(jq -r '.compare_mathjax3 | join(" ")' test/image/compare_pixels_collections.json)
python test/image/make_baseline.py mathjax3 $MATHJAX3_MOCKS || EXIT_STATE=$?
exit $EXIT_STATE
;;

make-baselines-b64)
SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split)
sudo python3 test/image/make_baseline.py b64 $SUITE || EXIT_STATE=$?
SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | sort | node "$SPLIT")
python test/image/make_baseline.py b64 $SUITE || EXIT_STATE=$?
exit $EXIT_STATE
;;

make-baselines)
SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split)
sudo python3 test/image/make_baseline.py $SUITE || EXIT_STATE=$?
SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | sort | node "$SPLIT")
python test/image/make_baseline.py $SUITE || EXIT_STATE=$?
exit $EXIT_STATE
;;

make-exports)
python test/image/make_exports.py || EXIT_STATE=$?
exit $EXIT_STATE
;;

test-image)
node test/image/compare_pixels_test.js || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$?
node test/image/compare_pixels_test.mjs || EXIT_STATE=$?
exit $EXIT_STATE
;;

test-image-mathjax3)
node test/image/compare_pixels_test.js mathjax3 || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$?
node test/image/compare_pixels_test.mjs mathjax3 || EXIT_STATE=$?
exit $EXIT_STATE
;;

test-image-virtual-webgl)
node test/image/compare_pixels_test.js virtual-webgl || { tar -cvf build/baselines.tar build/test_images/*.png ; exit 1 ; } || EXIT_STATE=$?
node test/image/compare_pixels_test.mjs virtual-webgl || EXIT_STATE=$?
exit $EXIT_STATE
;;

Expand Down
Loading
Loading