Skip to content

Merge branch 'main' into feat/topic-ribbon-example #84

Merge branch 'main' into feat/topic-ribbon-example

Merge branch 'main' into feat/topic-ribbon-example #84

Workflow file for this run

name: Build and Test
on:
workflow_call:
inputs:
python-version:
required: true
type: string
matplotlib-version:
required: true
type: string
test-mode:
required: false
type: string
default: full
test-nodeids:
required: false
type: string
default: ""
env:
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
jobs:
build-ultraplot:
name: Test Python ${{ inputs.python-version }} with MPL ${{ inputs.matplotlib-version }}
runs-on: ubuntu-latest
timeout-minutes: 60
defaults:
run:
shell: bash -el {0}
env:
TEST_MODE: ${{ inputs.test-mode }}
TEST_NODEIDS: ${{ inputs.test-nodeids }}
PYTEST_WORKERS: 4
steps:
- name: Set up swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Show system memory
run: |
echo "=== System Memory ==="
free -h
echo ""
echo "=== CPU Info ==="
nproc
cat /proc/cpuinfo | grep "model name" | head -1
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: mamba-org/setup-micromamba@v2.0.7
with:
environment-file: ./environment.yml
init-shell: bash
condarc-file: ./.github/micromamba-condarc.yml
post-cleanup: none
create-args: >-
--verbose
python=${{ inputs.python-version }}
matplotlib=${{ inputs.matplotlib-version }}
cache-environment: true
cache-downloads: false
- name: Build Ultraplot
run: |
pip install --no-build-isolation --no-deps .
compare-baseline:
name: Compare baseline Python ${{ inputs.python-version }} with MPL ${{ inputs.matplotlib-version }}
runs-on: ubuntu-latest
continue-on-error: true
env:
IS_PR: ${{ github.event_name == 'pull_request' }}
TEST_MODE: ${{ inputs.test-mode }}
TEST_NODEIDS: ${{ inputs.test-nodeids }}
PYTEST_WORKERS: 4
defaults:
run:
shell: bash -el {0}
steps:
- name: Set up swap space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Show system memory
run: |
echo "=== System Memory ==="
free -h
echo ""
echo "=== CPU Info ==="
nproc
cat /proc/cpuinfo | grep "model name" | head -1
- uses: actions/checkout@v6
- uses: mamba-org/setup-micromamba@v2.0.7
with:
environment-file: ./environment.yml
init-shell: bash
condarc-file: ./.github/micromamba-condarc.yml
post-cleanup: none
create-args: >-
--verbose
python=${{ inputs.python-version }}
matplotlib=${{ inputs.matplotlib-version }}
cache-environment: true
cache-downloads: false
# Cache Baseline Figures (Restore step)
- name: Cache Baseline Figures
id: cache-baseline
uses: actions/cache@v5
if: ${{ env.IS_PR }}
with:
path: ./ultraplot/tests/baseline # The directory to cache
# Key is based on OS, Python/Matplotlib versions, and the base commit SHA
key: ${{ runner.os }}-baseline-base-v2-${{ github.event.pull_request.base.sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}
restore-keys: |
${{ runner.os }}-baseline-base-v2-${{ github.event.pull_request.base.sha }}-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-
# Conditional Baseline Generation (Only runs on cache miss)
- name: Generate baseline from main
# Skip this step if the cache was found (cache-hit is true)
if: steps.cache-baseline.outputs.cache-hit != 'true' || !env.IS_PR
run: |
mkdir -p ultraplot/tests/baseline
echo "TEST_MODE=${TEST_MODE}"
echo "IS_PR=${IS_PR}"
echo "PR_BASE_SHA=${{ github.event.pull_request.base.sha }}"
echo "TEST_NODEIDS=${TEST_NODEIDS}"
# Save PR-selected nodeids for reuse after checkout (if provided)
if [ "${TEST_MODE}" = "selected" ] && [ -n "${TEST_NODEIDS}" ]; then
python -c 'import json, os
raw = os.environ.get("TEST_NODEIDS", "").strip()

Check failure on line 139 in .github/workflows/build-ultraplot.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/build-ultraplot.yml

Invalid workflow file

You have an error in your yaml syntax on line 139
nodeids = []
if raw and raw != "[]":
try:
parsed = json.loads(raw)
except json.JSONDecodeError:
parsed = raw.split()
if isinstance(parsed, str):
parsed = [parsed]
if isinstance(parsed, list):
nodeids = [item for item in parsed if isinstance(item, str) and item]
with open("/tmp/pr_selected_nodeids.txt", "w", encoding="utf-8") as fh:
for nodeid in nodeids:
fh.write(f"{nodeid}\n")
print(f"Selected nodeids parsed: {len(nodeids)}")'
else
: > /tmp/pr_selected_nodeids.txt
fi
# Checkout the base commit for PRs; otherwise regenerate from current ref
if [ -n "${{ github.event.pull_request.base.sha }}" ]; then
git fetch origin ${{ github.event.pull_request.base.sha }}
git checkout ${{ github.event.pull_request.base.sha }}
fi
# Install the Ultraplot version from the base branch's code
pip install --no-build-isolation --no-deps .
# Generate the baseline images and hash library
python -c "import ultraplot as plt; plt.config.Configurator()._save_yaml('ultraplot.yml')"
if [ "${TEST_MODE}" = "selected" ] && [ -s /tmp/pr_selected_nodeids.txt ]; then
status=0
mapfile -t FILTERED_NODEIDS < <(
while IFS= read -r nodeid; do
[ -z "$nodeid" ] && continue
path="${nodeid%%::*}"
[ -f "$path" ] && printf '%s\n' "$nodeid"
done < /tmp/pr_selected_nodeids.txt
)
echo "FILTERED_NODEIDS_BASE_COUNT=${#FILTERED_NODEIDS[@]}"
if [ "${#FILTERED_NODEIDS[@]}" -eq 0 ]; then
echo "No valid nodeids found on base; skipping baseline generation."
else
echo "=== Memory before baseline generation ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl-generate-path=./ultraplot/tests/baseline/ \
--mpl-default-style="./ultraplot.yml" \
"${FILTERED_NODEIDS[@]}" || status=$?
echo "=== Memory after baseline generation ===" && free -h
if [ "$status" -eq 4 ] || [ "$status" -eq 5 ]; then
echo "No tests collected from selected nodeids on base; skipping baseline generation."
status=0
fi
fi
# Return to the PR branch before continuing
if [ -n "${{ github.event.pull_request.base.sha }}" ]; then
echo "Checking out PR branch: ${{ github.sha }}"
git checkout ${{ github.sha }} || echo "Warning: git checkout failed, but continuing"
fi
if [ "$status" -ne 0 ]; then
echo "Baseline generation failed with status $status"
exit "$status"
fi
else
echo "=== Memory before baseline generation ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl-generate-path=./ultraplot/tests/baseline/ \
--mpl-default-style="./ultraplot.yml" \
ultraplot/tests
echo "=== Memory after baseline generation ===" && free -h
# Return to the PR branch for the rest of the job
if [ -n "${{ github.event.pull_request.base.sha }}" ]; then
echo "Checking out PR branch: ${{ github.sha }}"
git checkout ${{ github.sha }} || echo "Warning: git checkout failed, but continuing"
fi
fi
# Image Comparison (Uses cached or newly generated baseline)
- name: Image Comparison Ultraplot
run: |
# Re-install the Ultraplot version from the current PR branch
pip install --no-build-isolation --no-deps .
mkdir -p results
python -c "import ultraplot as plt; plt.config.Configurator()._save_yaml('ultraplot.yml')"
echo "TEST_MODE=${TEST_MODE}"
echo "TEST_NODEIDS=${TEST_NODEIDS}"
if [ "${TEST_MODE}" = "selected" ] && [ -s /tmp/pr_selected_nodeids.txt ]; then
status=0
mapfile -t FILTERED_NODEIDS < <(
while IFS= read -r nodeid; do
[ -z "$nodeid" ] && continue
path="${nodeid%%::*}"
[ -f "$path" ] && printf '%s\n' "$nodeid"
done < /tmp/pr_selected_nodeids.txt
)
echo "FILTERED_NODEIDS_PR_COUNT=${#FILTERED_NODEIDS[@]}"
if [ "${#FILTERED_NODEIDS[@]}" -eq 0 ]; then
echo "No valid nodeids found on PR branch; skipping image comparison."
exit 0
else
echo "=== Memory before image comparison ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl \
--mpl-baseline-path=./ultraplot/tests/baseline \
--mpl-results-path=./results/ \
--mpl-generate-summary=html \
--mpl-default-style="./ultraplot.yml" \
"${FILTERED_NODEIDS[@]}" || status=$?
echo "=== Memory after image comparison ===" && free -h
if [ "$status" -eq 4 ] || [ "$status" -eq 5 ]; then
echo "No tests collected from selected nodeids; skipping image comparison."
status=0
fi
fi
exit "$status"
else
echo "=== Memory before image comparison ===" && free -h
pytest -n ${PYTEST_WORKERS} --dist loadfile --tb=short --disable-warnings -W ignore \
--mpl \
--mpl-baseline-path=./ultraplot/tests/baseline \
--mpl-results-path=./results/ \
--mpl-generate-summary=html \
--mpl-default-style="./ultraplot.yml" \
ultraplot/tests
echo "=== Memory after image comparison ===" && free -h
fi
# Return the html output of the comparison even if failed
- name: Upload comparison failures
if: always()
uses: actions/upload-artifact@v6
with:
name: failed-comparisons-${{ inputs.python-version }}-${{ inputs.matplotlib-version }}-${{ github.sha }}
path: results/*
if-no-files-found: ignore