Skip to content

Yaml writer addition #5803

Yaml writer addition

Yaml writer addition #5803

Workflow file for this run

# CI.yml
# This file contains the script used by GitHub actions to execute the Continuous Integration (CI)
# for RMG-Py. This includes building RMG and its dependencies, executing the unit tests,
# functional tests, database tests, and regression tests.
#
# This will run automatically on any push to any branch, but will only run one instance of
# itself at a time per branch (to avoid spawning tons of runners, which prevents them from
# executing).
#
# In the regression testing section of the action the term "Stable" or "Reference" refers to
# the 'correct answers' to the regression tests, i.e. the way that the main branch executes
# them. These 'answers' are re-generated daily, or on any push to main, and retrieved whenever
# a push is made to a non-main branch. The new proposed changes are referred to as "Dynamic".
#
#
# Changelog:
# 2023-04 - Jackson Burns - Added this header, regression tests, cleanup of action in
# in general, and documentation throughout the file.
# 2023-05 - added Docker build steps
# 2023-05-12 - added changes to allow running on forks
# 2023-06-06 - added matrix build for libstdcxx-ng versions 12 and 13 on ubuntu. Only expect 12 to work.
# 2023-06-07 - updated regression testing. Now fails if significant changes are detected.
# 2023-06-15 - revert changes from 06-06, both now work
# 2023-06-27 - add option to run from RMG-database with GitHub resuable workflows
# 2023-07-17 - made it pass by default
# 2023-07-21 - upload the regression results summary as artifact (for use as a comment on PRs)
# 2023-07-31 - removed option to run from RMG-database with GitHub resuable workflows
# 2024-10-01 - deprecated Mambaforge with Miniforge3 for environment creation
name: Continuous Integration
on:
schedule:
# * is a special character in YAML so you have to quote this string
- cron: "0 8 * * *"
# allow running on RMG-Py on a pushed branch, only if triggered manually
workflow_dispatch:
# runs on PRs against RMG-Py (and anywhere else, but we add this for RMG-Py)
pull_request:
# runs on pushes to main branch
push:
branches:
- main
# this prevents one PR from simultaneously running multiple runners, which will clog up the queue
# and prevent other PRs from running the CI
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# if running on RMG-Py but requiring changes on an un-merged branch of RMG-database, replace
# main with the name of the branch
RMG_DATABASE_BRANCH: main
# RMS branch to use for ReactionMechanismSimulator installation
RMS_BRANCH: for_rmg
# julia parallel pre-compilation leads to race conditions and hangs, so we limit it to run in serial
JULIA_NUM_PRECOMPILE_TASKS: 1
jobs:
build-and-test:
strategy:
fail-fast: false
matrix:
python-version: ["3.9"]
os: [macos-13, macos-latest, ubuntu-latest]
include-rms: ["", "with RMS"]
exclude:
- os: macos-13 # GitHub's runners just aren't up to the task of installing Julia
include-rms: 'with RMS'
runs-on: ${{ matrix.os }}
name: Python ${{ matrix.python-version }} ${{ matrix.os }} Build and Test ${{ matrix.include-rms }}
# skip scheduled runs from forks
if: ${{ !( github.repository != 'ReactionMechanismGenerator/RMG-Py' && github.event_name == 'schedule' ) }}
defaults:
run:
shell: bash -l {0}
steps:
- name: Checkout RMG-Py
uses: actions/checkout@v4
- name: Setup Miniforge Python ${{ matrix.python-version }}
uses: conda-incubator/setup-miniconda@v3
with:
environment-file: environment.yml
miniforge-variant: Miniforge3
miniforge-version: latest
python-version: ${{ matrix.python-version }}
activate-environment: rmg_env
auto-update-conda: true
show-channel-urls: true
conda-remove-defaults: "true"
# list the environment for debugging purposes
- name: conda info
run: |
conda info
conda list
# Clone RMG-database
- name: Clone RMG-database
run: |
cd ..
git clone -b $RMG_DATABASE_BRANCH https://github.com/ReactionMechanismGenerator/RMG-database.git
# RMG build step
- run: make install
- name: Setup Juliaup
if: matrix.include-rms == 'with RMS'
uses: julia-actions/install-juliaup@v2
with:
channel: '1.10'
# RMS installation and linking to Julia
- name: Install and link Julia dependencies
if: matrix.include-rms == 'with RMS'
timeout-minutes: 120 # this usually takes 20-45 minutes (or hangs for 6+ hours).
run: . install_rms.sh
- name: Install Q2DTor
run: echo "" | make q2dtor
# non-regression testing
- name: Run Unit, Functional, and Database Tests
run: make test-all
regression-test:
runs-on: ubuntu-latest
name: Regression Test
# skip scheduled runs from forks
if: ${{ !( github.repository != 'ReactionMechanismGenerator/RMG-Py' && github.event_name == 'schedule' ) }}
env:
# This is true only if this is a reference case for the regression testing:
REFERENCE_JOB: ${{ github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py' }}
defaults:
run:
shell: bash -l {0}
steps:
- name: Checkout RMG-Py
uses: actions/checkout@v4
- name: Setup Miniforge Python 3.9
uses: conda-incubator/setup-miniconda@v3
with:
environment-file: environment.yml
miniforge-variant: Miniforge3
miniforge-version: latest
python-version: 3.9
activate-environment: rmg_env
auto-update-conda: true
show-channel-urls: true
conda-remove-defaults: "true"
# list the environment for debugging purposes
- name: conda info
run: |
conda info
conda list
# Clone RMG-database
- name: Clone RMG-database
run: |
cd ..
git clone -b $RMG_DATABASE_BRANCH https://github.com/ReactionMechanismGenerator/RMG-database.git
# RMG build step
- run: make install
- name: Make separate No-RMS conda env
run: |
conda create --name rmg_env_without_rms --clone rmg_env
- name: Setup Juliaup
uses: julia-actions/install-juliaup@v2
with:
channel: '1.10'
# RMS installation and linking to Julia
- name: Install and link Julia dependencies
timeout-minutes: 120 # this usually takes 20-45 minutes (or hangs for 6+ hours).
run: |
. install_rms.sh
- name: Install Q2DTor
run: echo "" | make q2dtor
# Regression Testing - Test Execution
- name: Regression Tests - Execution
id: regression-execution
run: |
for regr_test in aromatics liquid_oxidation nitrogen oxidation sulfur superminimal RMS_constantVIdealGasReactor_superminimal RMS_CSTR_liquid_oxidation RMS_liquidSurface_ch4o2cat fragment RMS_constantVIdealGasReactor_fragment minimal_surface;
do
if python rmg.py test/regression/"$regr_test"/input.py; then
echo "$regr_test" "Executed Successfully"
else
echo "$regr_test" "Failed to Execute" | tee -a $GITHUB_STEP_SUMMARY
export FAILED=Yes
fi
done
if [[ ${FAILED} ]]; then
echo "One or more regression tests could not be executed." | tee -a $GITHUB_STEP_SUMMARY
echo "Please download the failed results or check the above log to see why." | tee -a $GITHUB_STEP_SUMMARY
exit 1
fi
# Upload Regression Results as Failed if above step failed
- name: Upload Failed Results
if: ${{ failure() && steps.regression-execution.conclusion == 'failure' }}
uses: actions/upload-artifact@v4
with:
name: failed_regression_results
path: |
test/regression
# Upload Regression Results as Stable if Scheduled or Push to Main
- name: Upload Results as Reference
# upload the results for scheduled CI (on main) and pushes to main
if: ${{ env.REFERENCE_JOB == 'true' }}
uses: actions/upload-artifact@v4
with:
name: stable_regression_results
path: |
test/regression
# Upload Regression Results as Dynamic if Push to non-main Branch
- name: Upload Results as Dynamic
if: ${{ env.REFERENCE_JOB == 'false' }}
uses: actions/upload-artifact@v4
with:
name: dynamic_regression_results
path: |
test/regression
- name: mkdir stable_regression_results
if: ${{ env.REFERENCE_JOB == 'false' }}
run: mkdir stable_regression_results
# Retrieve Stable Results for reference
- name: Find ID of Reference Results
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# this will search for the last successful execution of CI on main
run: |
run_id=$(gh run list -R ReactionMechanismGenerator/RMG-Py --workflow="Continuous Integration" --branch main --limit 15 --json databaseId,conclusion --jq 'map(select(.conclusion == "success")) | .[0].databaseId')
if [ -z "$run_id" ] || [ "$run_id" = "null" ]; then
echo "::error::No successful reference run found"
exit 1
fi
echo "CI_RUN_ID=$run_id" >> $GITHUB_ENV
- name: Retrieve Stable Regression Results
if: ${{ env.REFERENCE_JOB == 'false' }}
uses: actions/download-artifact@v4
with:
# download stable regression results
run-id: ${{ env.CI_RUN_ID }}
repository: ReactionMechanismGenerator/RMG-Py
github-token: ${{ secrets.GITHUB_TOKEN }}
name: stable_regression_results
path: stable_regression_results
# Regression Testing - Actual Comparisons
- name: Regression Tests - Compare to Baseline
id: regression-comparison
if: ${{ env.REFERENCE_JOB == 'false' }}
env:
REFERENCE: stable_regression_results
run: |
conda activate rmg_env_without_rms
exec 2> >(tee -a regression.stderr >&2) 1> >(tee -a regression.stdout)
mkdir -p "test/regression-diff"
for regr_test in aromatics liquid_oxidation nitrogen oxidation sulfur superminimal RMS_constantVIdealGasReactor_superminimal RMS_CSTR_liquid_oxidation fragment RMS_constantVIdealGasReactor_fragment minimal_surface;
do
echo ""
echo "### Regression test $regr_test:"
# Memory Usage and Execution Time
echo -n 'Reference: '
grep "Execution time" $REFERENCE/"$regr_test"/RMG.log | tail -1
echo -n 'Current: '
grep "Execution time" test/regression/"$regr_test"/RMG.log | tail -1
echo -n 'Reference: '
grep "Memory used:" $REFERENCE/"$regr_test"/RMG.log | tail -1
echo -n 'Current: '
grep "Memory used:" test/regression/"$regr_test"/RMG.log | tail -1
echo "<details>"
# Compare the edge and core
if python scripts/checkModels.py \
"$regr_test-core" \
$REFERENCE/"$regr_test"/chemkin/chem_annotated.inp \
$REFERENCE/"$regr_test"/chemkin/species_dictionary.txt \
test/regression/"$regr_test"/chemkin/chem_annotated.inp \
test/regression/"$regr_test"/chemkin/species_dictionary.txt &> checkModels.err
then
echo "<summary>$regr_test Passed Core Comparison ✅</summary>"
else
echo "<summary>$regr_test Failed Core Comparison ❌</summary>"
cp "$regr_test-core.log" test/regression-diff/
export FAILED=Yes
fi
echo "" # blank line so next block is interpreted as markdown
cat "$regr_test-core.log" || (echo "Dumping the whole log failed, please download it from GitHub actions. Here are the first 100 lines:" && head -n100 "$regr_test-core.log")
echo "</details>"
if [ -s checkModels.err ]; then
echo "<details>"
echo "<summary>Errors occurred during core comparison ⚠️</summary>"
cat checkModels.err
echo "</details>"
fi
echo "<details>"
if python scripts/checkModels.py \
"$regr_test-edge" \
$REFERENCE/"$regr_test"/chemkin/chem_edge_annotated.inp \
$REFERENCE/"$regr_test"/chemkin/species_edge_dictionary.txt \
test/regression/"$regr_test"/chemkin/chem_edge_annotated.inp \
test/regression/"$regr_test"/chemkin/species_edge_dictionary.txt &> checkModels.err
then
echo "<summary>$regr_test Passed Edge Comparison ✅</summary>"
else
echo "<summary>$regr_test Failed Edge Comparison ❌</summary>"
cp "$regr_test-edge.log" test/regression-diff/
export FAILED=Yes
fi
echo "" # blank line so next block is interpreted as markdown
cat "$regr_test-edge.log" || (echo "Dumping the whole log failed, please download it from GitHub actions. Here are the first 100 lines:" && head -n100 "$regr_test-edge.log")
echo "</details>"
if [ -s checkModels.err ]; then
echo "<details>"
echo "<summary>Errors occurred during edge comparison ⚠️</summary>"
cat checkModels.err
echo "</details>"
fi
# Check for Regression between Reference and Dynamic (skip superminimal)
if [ -f test/regression/"$regr_test"/regression_input.py ];
then
echo "<details>"
if python rmgpy/tools/regression.py \
test/regression/"$regr_test"/regression_input.py \
$REFERENCE/"$regr_test"/chemkin \
test/regression/"$regr_test"/chemkin 2> regression.py.err
then
echo "<summary>$regr_test Passed Observable Testing ✅</summary>"
else
echo "<summary>$regr_test Failed Observable Testing ❌</summary>"
export FAILED=Yes
fi
echo "</details>"
if [ -s regression.py.err ]; then
echo "<details>"
echo "<summary>Errors occurred during observable testing ⚠️</summary>"
cat regression.py.err
echo "</details>"
fi
fi
echo ""
done
if [[ ${FAILED} ]]; then
echo "⚠️ One or more regression tests failed." | tee -a $GITHUB_STEP_SUMMARY >&2
echo "Please download the failed results and run the tests locally or check the log to see why." | tee -a $GITHUB_STEP_SUMMARY >&2
fi
- name: Prepare Results for PR Comment
if: ${{ env.REFERENCE_JOB == 'false' }}
env:
PR_NUMBER: ${{ github.event.number || github.event.after || github.event_name }}
run: |
echo $PR_NUMBER > summary.txt
echo "## Regression Testing Results" >> summary.txt
cat regression.stderr >> summary.txt
echo "<details>" >> summary.txt
echo "<summary>Detailed regression test results.</summary>" >> summary.txt
cat regression.stdout >> summary.txt
echo "</details>" >> summary.txt
echo "" >> summary.txt
echo "_beep boop this comment was written by a bot_ :robot:" >> summary.txt
cat summary.txt > $GITHUB_STEP_SUMMARY
- name: Upload regression summary artifact
# the annotate workflow uses this artifact to add a comment to the PR
uses: actions/upload-artifact@v4
if: ${{ github.event_name == 'pull_request' }}
with:
name: regression_summary
path: summary.txt
- name: Upload Comparison Results
uses: actions/upload-artifact@v4
with:
name: regression_test_comparison_results
path: |
test/regression-diff
# Install and Call codecov only if the tests were successful (permitting failures in the regression comparison tests)
- name: Code coverage install and run
if: success() || ( failure() && steps.regression-execution.conclusion == 'success' )
run: |
conda install -y -c conda-forge codecov
codecov
build-and-push-docker:
# after testing and on pushes to main, build and push docker image
#
# taken from https://github.com/docker/build-push-action
needs: [build-and-test, regression-test]
runs-on: ubuntu-latest
# Run on push to main or on pull requests from official fork.
if: |
(github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py') ||
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'ReactionMechanismGenerator/RMG-Py')
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
if: github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build (and Push if on main)
uses: docker/build-push-action@v6
env:
BUILDKIT_PROGRESS: plain
with:
push: ${{ github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py' }}
tags: reactionmechanismgenerator/rmg:latest
build-args: |
RMG_Py_Branch=${{ github.head_ref || github.ref_name }}
RMG_Database_Branch=${{ env.RMG_DATABASE_BRANCH }}
RMS_Branch=${{ env.RMS_BRANCH }}