Skip to content

Actions: Migrate to newer versions of macos #475

Actions: Migrate to newer versions of macos

Actions: Migrate to newer versions of macos #475

Workflow file for this run

name: MHKiT-MATLAB Unix Unit Tests
# Only run tests when MATLAB files or this workflow file changes
on:
push:
branches: [ master, develop ]
paths:
- 'mhkit/**/*.m'
- 'examples/**/*.m'
- '.github/workflows/unix_unit_tests.yml'
pull_request:
branches: [ master, develop ]
paths:
- 'mhkit/**/*.m'
- 'examples/**/*.m'
- '.github/workflows/unix_unit_tests.yml'
jobs:
cache_population:
strategy:
fail-fast: false
matrix:
os: ["ubuntu-22.04"]
python-version: ["3.11"]
matlab-version: [R2023b]
runs-on: ${{ matrix.os }}
env:
mhkit-python-dir: "MHKiT-Python"
MHKIT_PYTHON_VERSION: '1.0.1'
steps:
- name: Install & Setup Miniconda
uses: conda-incubator/setup-miniconda@v3
with:
miniconda-version: latest
auto-update-conda: true
python-version: ${{ matrix.python-version }}
activate-environment: mhkit_conda_env
channels: conda-forge,defaults
# - name: "Conda install netcdf4, hdf5"
# shell: bash -l {0}
# run: |
# conda activate mhkit_conda_env
# conda install numpy cython pip pytest hdf5 libnetcdf cftime netcdf4
#
# - name: Fix libstdc++ compatibility
# if: matrix.os == 'ubuntu-22.04'
# run: |
# # Force MATLAB to use system libstdc++ instead of its bundled (older) version
# # LD_PRELOAD forces the library to be loaded first
# # LD_LIBRARY_PATH ensures symbol resolution finds the system library
# echo "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6" >> "$GITHUB_ENV"
# echo "LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH" >> "$GITHUB_ENV"
- name: Check out MHKiT-MATLAB
uses: actions/checkout@v4
- name: Conda install mhkit
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
conda install -c conda-forge mhkit==$MHKIT_PYTHON_VERSION
- name: pip install mhkit-python-utils module from source
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip install -e .
- name: Pin scipy version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip uninstall -y scipy
pip install scipy==1.14.0
- name: List installed pip modules
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip freeze
# Create the cache and add a dummy file
# The dummy file ensures that the artifact download
- name: Setup mhkit_webread_cache
shell: bash -l {0}
run: |
mkdir mhkit_webread_cache
touch mhkit_webread_cache/test.txt
echo "Hello World" > mhkit_webread_cache/test.txt
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2
with:
release: ${{ matrix.matlab-version }}
# Generate run.m using Python to match Windows workflow approach
# Using InProcess mode for cache_population job
- name: Generate run.m
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
python -c "
import sys
python_exe = sys.executable
content = f'''pyenv(Version='{python_exe}', ExecutionMode='InProcess')
% Verify Python environment
pe = pyenv;
disp(pe);
% Warm up numpy - must call function to force resolution
py.importlib.import_module('numpy');
dummy = py.numpy.array([1,2,3]);
disp(class(dummy));
clear dummy;
disp('py.numpy.array: resolved');
% Warm up mhkit submodules - force resolution by calling functions
py.importlib.import_module('mhkit');
py.importlib.import_module('mhkit.wave.io.ndbc');
py.importlib.import_module('mhkit.wave.io.swan');
py.importlib.import_module('mhkit.river.resource');
disp('mhkit submodules: imported');
version
pyenv
addpath(genpath('mhkit'))
import matlab.unittest.TestSuite
import matlab.unittest.TestRunner
testFolder = ['mhkit' filesep 'tests'];
suite = TestSuite.fromFolder(testFolder);
runner = TestRunner.withTextOutput;
results = runner.run(suite);
assertSuccess(results)
'''
with open('run.m', 'w', encoding='utf-8', newline='\\n') as f:
f.write(content)
"
- name: Output run.m
shell: bash
run: cat run.m
- name: Run MATLAB Unit Tests
uses: matlab-actions/run-command@v2
with:
command: run
startup-options: -noFigureWindows
- name: Save mhkit_webread_cache directory as an artifact
uses: actions/upload-artifact@v4
with:
# GitHub Action "Name" of artifact
name: mhkit_webread_cache
# Filesystem path of directory
path: mhkit_webread_cache
main:
needs: cache_population
strategy:
fail-fast: false
matrix:
os: [macos-15, macos-15-intel, "ubuntu-22.04"]
python-version: ["3.10", 3.11, 3.12]
# Note: It is preferred to use an actual release name as opposed to 'latest'
# macos-15 is ARM (Apple Silicon) - R2023b+ run natively
# macos-15-intel is x64 - R2022b/R2023a run natively (no Rosetta needed)
matlab-version: [R2022b, R2023a, R2023b, R2024a, R2024b, R2025a]
mhkit-python-version: ["1.0.1"]
exclude:
# Python 3.12 + MATLAB incompatibility
- matlab-version: R2024a
python-version: 3.12
- matlab-version: R2023b
python-version: 3.12
- matlab-version: R2023a
python-version: 3.12
- matlab-version: R2022b
python-version: 3.12
- matlab-version: R2025a
python-version: 3.12
# Python 3.11 + MATLAB incompatibility
- matlab-version: R2023a
python-version: 3.11
- matlab-version: R2022b
python-version: 3.11
# Architecture-specific macOS exclusions
# Intel MATLAB (R2022b, R2023a) should only run on macos-15-intel
- matlab-version: R2022b
os: macos-15
- matlab-version: R2023a
os: macos-15
# ARM MATLAB (R2023b+) should only run on macos-15 (ARM)
- matlab-version: R2023b
os: macos-15-intel
- matlab-version: R2024a
os: macos-15-intel
- matlab-version: R2024b
os: macos-15-intel
- matlab-version: R2025a
os: macos-15-intel
# Specific MATLAB/Python/OS Errors
# Error using _cythonized_array_utils>init
# scipy.linalg._cythonized_array_utils (line 1)
# Python Error: ValueError: numpy.dtype size changed, may indicate binary
# incompatibility. Expected 96 from C header, got 88 from PyObject
# - matlab-version: R2024b
# python-version: 3.12
# os: macos-15
# Unable to resolve the name 'py.mhkit.wave.resource.jonswap_spectrum'
# - matlab-version: R2023a
# python-version: 3.10
# os: "ubuntu-22.04"
runs-on: ${{ matrix.os }}
env:
mhkit-python-dir: "MHKiT-Python"
MHKIT_PYTHON_VERSION: ${{ matrix.mhkit-python-version }}
steps:
- name: Check out MHKiT-MATLAB
uses: actions/checkout@v4
- name: Install & Setup Miniconda
uses: conda-incubator/setup-miniconda@v3
with:
miniconda-version: latest
auto-update-conda: true
python-version: ${{ matrix.python-version }}
activate-environment: mhkit_conda_env
channels: conda-forge,defaults
# This is necessary to fix any issues with netcdf4 and hdf5 headers
- name: "Conda install netcdf4, hdf5"
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
conda install numpy cython pip pytest hdf5 libnetcdf cftime netcdf4
- name: Fix libstdc++ compatibility
if: matrix.os == 'ubuntu-22.04'
run: |
# Force MATLAB to use system libstdc++ instead of its bundled (older) version
# LD_PRELOAD forces the library to be loaded first
# LD_LIBRARY_PATH ensures symbol resolution finds the system library
echo "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6" >> "$GITHUB_ENV"
echo "LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH" >> "$GITHUB_ENV"
# Pin compatible ICU version based on MATLAB's bundled libstdc++ capabilities
# Older MATLAB versions bundle older libstdc++ missing newer GLIBCXX symbols
- name: Pin compatible ICU version for MATLAB
if: matrix.os == 'ubuntu-22.04'
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
# Lookup table: MATLAB version -> max compatible ICU version
# Based on GLIBCXX symbols available in MATLAB's bundled libstdc++
declare -A ICU_COMPAT=(
["R2022a"]="70"
["R2022b"]="70"
["R2023a"]="70"
["R2023b"]="" # empty = use latest (has modern libstdc++)
["R2024a"]=""
["R2024b"]=""
["R2025a"]=""
)
MATLAB_VERSION="${{ matrix.matlab-version }}"
ICU_VERSION="${ICU_COMPAT[$MATLAB_VERSION]}"
if [ -n "$ICU_VERSION" ]; then
echo "MATLAB $MATLAB_VERSION requires older ICU. Installing icu=$ICU_VERSION"
conda install -y "icu<=$ICU_VERSION"
else
echo "MATLAB $MATLAB_VERSION has modern libstdc++, using default ICU"
fi
# Diagnostic: Check what GLIBC symbols are required by key libraries
- name: Diagnose library dependencies (Ubuntu)
if: matrix.os == 'ubuntu-22.04'
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
echo "=== System libstdc++ GLIBCXX versions ==="
strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX | tail -10
echo ""
echo "=== Conda libicuuc location and GLIBCXX requirements ==="
ICUUC=$(find $CONDA_PREFIX -name "libicuuc.so*" 2>/dev/null | head -1)
if [ -n "$ICUUC" ]; then
echo "Found: $ICUUC"
strings "$ICUUC" | grep GLIBCXX || echo "No GLIBCXX requirements"
else
echo "libicuuc not found in conda env"
fi
echo ""
echo "=== MATLAB libstdc++ location (if accessible) ==="
ls -la /opt/hostedtoolcache/MATLAB/*/x64/sys/os/glnxa64/libstdc++.so.6 2>/dev/null || echo "MATLAB libstdc++ not found"
- name: Print Python executable
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
python -c "import sys; print(sys.executable)"
- name: Print Python Version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
python --version
- name: pip install mhkit from pypi
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
export PATH="${CONDA_PREFIX}/bin:${CONDA_PREFIX}/Library/bin:$PATH" # so setup.py finds nc-config
pip install mhkit==$MHKIT_PYTHON_VERSION
- name: pip upgrade netcdf4
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
export PATH="${CONDA_PREFIX}/bin:${CONDA_PREFIX}/Library/bin:$PATH" # so setup.py finds nc-config
pip install --upgrade netcdf4
- name: pip install mhkit-python-utils module from source
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip install -e .
- name: Pin scipy version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip uninstall -y scipy
pip install scipy==1.14.0
- name: List installed pip modules
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip freeze
- name: Print MHKiT-Python Version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
python -c "import mhkit; print(mhkit.__version__)"
- name: Verify MHKiT-Python Operation
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
python -c "import mhkit; [ED, AP] = mhkit.river.performance.circular(30); print(ED); print(AP);"
- name: Download mhkit_webread_cache artifact
uses: actions/download-artifact@v4
with:
name: mhkit_webread_cache
path: mhkit_webread_cache
- name: Display structure mhkit_webread_cache
run: ls -R
working-directory: .
- name: Install Amazon Corretto 11 for ARM MATLAB
if: runner.os == 'macOS'
run: |
# ARM MATLAB (R2023b+) requires Java 11
brew install --cask corretto@11
echo "JAVA_HOME=$(/usr/libexec/java_home -v 11)" >> $GITHUB_ENV
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2
with:
release: ${{ matrix.matlab-version }}
- name: Configure OpenSSL for MacOS/MATLAB
if: runner.os == 'macOS'
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
conda install openssl=="3.0.*"
CONDA_LIB_PATH=$(python -c "import sys; import os; print(os.path.join(os.path.dirname(sys.executable), 'lib'))")
export DYLD_LIBRARY_PATH="$CONDA_LIB_PATH:$DYLD_LIBRARY_PATH"
echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Add Python Dir to Path
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
printf "getenv('path')\nsetenv('path', ['%s;', getenv('path')])\ngetenv('path')\n" $(python -c "import sys; import os; print(os.path.dirname(sys.executable))") >> run.m
# OutOfProcess is required to properly call netcdf/hdf5 and works reliably on macos and ubuntu
# Note: LD_PRELOAD and LD_LIBRARY_PATH are set via GITHUB_ENV earlier for Linux
- name: Configure MATLAB Python Environment
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
printf 'pyenv(Version="%s", ExecutionMode="OutOfProcess")\n' $(python -c "import sys; print(sys.executable)") >> run.m
# Pass dynamic library paths to MATLAB only on macOS
if [[ $OSTYPE == "darwin"* ]]; then
echo "setenv('DYLD_LIBRARY_PATH', getenv('DYLD_LIBRARY_PATH'));" >> run.m
fi
# Warm up Python OutOfProcess worker to prevent intermittent "Unable to resolve" errors
- name: Add Python environment verification
shell: bash
run: |
echo "% Verify Python environment and warm up OutOfProcess worker" >> run.m
echo "pe = pyenv;" >> run.m
echo "disp(pe);" >> run.m
echo "py.importlib.import_module('numpy');" >> run.m
echo "py.importlib.import_module('mhkit');" >> run.m
echo "disp('Python environment verified: numpy and mhkit imported successfully');" >> run.m
- name: Add MATLAB test commands
shell: bash
run: echo "version,
addpath(genpath('mhkit')),
import matlab.unittest.TestSuite,
import matlab.unittest.TestRunner,
testFolder = ['mhkit' filesep 'tests'],
suite = TestSuite.fromFolder(testFolder),
runner = TestRunner.withTextOutput,
results = runner.run(suite),
assertSuccess(results)" >> run.m
- name: Output run.m
shell: bash
run: cat run.m
# This is a good idea but does not work because you cannot explicitly set the python execution mode
# - name: Run MHKiT-MATLAB Unit Tests
# uses: matlab-actions/run-tests@v1
# with:
# select-by-folder: mhkit/tests
- name: Run MATLAB Unit Tests
uses: matlab-actions/run-command@v2
with:
command: run
startup-options: -noFigureWindows