Feat openemr 9035 9036 patient observations #10916
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# OpenEMR has two different kinds of tests: | |
# - "Isolated tests": Tests that run without secondary services, a data layer or significant initialization requirements | |
# - "Tests": Tests that require a database and initialization before they can run or pass. | |
# This workflow runs the kind that requires initialization. | |
name: Test | |
on: | |
push: | |
branches: | |
- master | |
- rel-* | |
pull_request: | |
branches: | |
- master | |
- rel-* | |
permissions: | |
contents: read | |
jobs: | |
collect: | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v5 | |
- name: Collect Docker Dirs | |
id: docker-dirs | |
## | |
# Parse out the test parameters from the ci subdirectories that contain | |
# docker-compose.yml files. We'll use that output as a test matrix | |
# in github actions. | |
run: | | |
shopt -s nullglob | |
dirs=( ci/*/docker-compose.yml ) | |
# Remove compose-shared-* from the list | |
filtered_dirs=() | |
for dir in "${dirs[@]}"; do | |
dirname=$(basename "$(dirname "$dir")") | |
if [[ ! $dirname == compose-shared-* ]]; then | |
filtered_dirs+=("$dir") | |
fi | |
done | |
dirs=("${filtered_dirs[@]}") | |
if (( "${#dirs[@]}" == 0 )); then | |
echo 'No docker-compose.yml files found in ci subdirectories.' >&2 | |
exit 1 | |
fi | |
node_version=22 | |
# Avoid trying to save the cache in duplicate for every matrix element. | |
declare -A seen_composer_cache_keys | |
declare -A seen_npm_cache_keys | |
dirs=( "${dirs[@]%/docker-compose.yml}" ) | |
dirs=( "${dirs[@]#ci/}" ) | |
# Everything inside this for loop that goes to stdout ends up | |
# in the github output, so it must be valid json. | |
for docker_dir in "${dirs[@]}"; do | |
IFS=: read -r database db < <(yq '.services.mysql.image' "ci/${docker_dir}/docker-compose.yml") | |
IFS=_ read -r webserver php _ <<< "${docker_dir}" | |
# collect docker-compose.yml templates that will be used in the docker compose up merge | |
selenium_template=$(yq '.x-includes.selenium-template' "ci/${docker_dir}/docker-compose.yml") | |
webserver_template=$(yq '.x-includes.webserver-template' "ci/${docker_dir}/docker-compose.yml") | |
database_template=$(yq '.x-includes.database-template' "ci/${docker_dir}/docker-compose.yml") | |
# If docker_dir ends with "_no-e2e", we want to skip the e2e tests for this configuration. | |
[[ $docker_dir = *_no-e2e ]] && e2e_enabled=false || e2e_enabled=true | |
printf -v php '%d.%d' "${php::1}" "${php:1}" | |
if [[ -z ${seen_composer_cache_keys[$php]} ]]; then | |
seen_composer_cache_keys[$php]=true | |
save_composer_cache=true | |
else | |
save_composer_cache=false | |
fi | |
if [[ -z ${seen_npm_cache_keys[$node_version]} ]]; then | |
seen_npm_cache_keys[$node_version]=true | |
save_node_cache=true | |
else | |
save_node_cache=false | |
fi | |
case "$webserver" in | |
apache) | |
openemr_dir=/var/www/localhost/htdocs/openemr | |
;; | |
nginx) | |
openemr_dir=/usr/share/nginx/html/openemr | |
;; | |
*) | |
echo "Unknown webserver: $webserver" >&2; | |
exit 1 | |
;; | |
esac | |
# Determine if this job should save caches (only first job with each version) | |
[[ $docker_dir = apache_84_114 ]] && save_cache=true || save_cache="false" | |
printf '{ | |
"database": "%s", | |
"database_template": "%s", | |
"db": "%s", | |
"docker_dir": "%s", | |
"e2e_enabled": "%s", | |
"node_version": "%s", | |
"openemr_dir": "%s", | |
"php": "%s", | |
"save_composer_cache": "%s", | |
"save_node_cache": "%s", | |
"selenium_template": "%s", | |
"webserver": "%s", | |
"webserver_template": "%s" | |
}\n' "$database" \ | |
"$database_template" \ | |
"$db" \ | |
"$docker_dir" \ | |
"$e2e_enabled" \ | |
"$node_version" \ | |
"$openemr_dir" \ | |
"$php" \ | |
"$save_composer_cache" \ | |
"$save_node_cache" \ | |
"$selenium_template" \ | |
"$webserver" \ | |
"$webserver_template" | |
done | { | |
printf 'configurations=' | |
jq -cs . | |
} >> "$GITHUB_OUTPUT" | |
outputs: | |
configurations: ${{ steps.docker-dirs.outputs.configurations }} | |
build: | |
name: PHP ${{ matrix.configurations.php }} - ${{ matrix.configurations.webserver }} - ${{ matrix.configurations.database }} ${{ matrix.configurations.db }} | |
runs-on: ubuntu-24.04 | |
needs: collect | |
strategy: | |
fail-fast: false | |
matrix: | |
configurations: ${{ fromJson(needs.collect.outputs.configurations) }} | |
env: | |
DOCKER_DIR: ${{ matrix.configurations.docker_dir }} | |
ENABLE_COVERAGE: ${{ matrix.configurations.docker_dir == 'apache_84_114' && 'true' || 'false' }} | |
OPENEMR_DIR: ${{ matrix.configurations.openemr_dir }} | |
# Note that the first entry in the COMPOSE_FILE needs to be in ci/ (if it has a subdirectory then it breaks things) | |
COMPOSE_FILE: "ci/${{ matrix.configurations.webserver_template }}:ci/${{ matrix.configurations.database_template }}:ci/${{ matrix.configurations.selenium_template }}:ci/${{ matrix.configurations.docker_dir }}/docker-compose.yml" | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v5 | |
- name: Setup PHP | |
uses: shivammathur/setup-php@v2 | |
with: | |
php-version: ${{ matrix.configurations.php }} | |
- name: Report PHP Version | |
run: php -v | |
- name: Get composer cache directory | |
id: composer-cache | |
run: | | |
{ | |
printf 'dir=' | |
composer config cache-files-dir | |
} >> $GITHUB_OUTPUT | |
- name: Composer Cache | |
uses: actions/cache/restore@v4 | |
with: | |
path: ${{ steps.composer-cache.outputs.dir }} | |
key: ${{ runner.os }}-composer-${{ matrix.configurations.php }}-${{ hashFiles('**/composer.lock') }} | |
restore-keys: | | |
${{ runner.os }}-composer-${{ matrix.configurations.php }}- | |
${{ runner.os }}-composer- | |
- name: Install npm package | |
uses: actions/setup-node@v5 | |
with: | |
node-version: ${{ matrix.configurations.node_version }} | |
- name: Get NPM Cache Directory | |
id: npm-cache-dir | |
run: | | |
{ | |
printf 'dir=' | |
npm config get cache | |
} >> "$GITHUB_OUTPUT" | |
- name: Cache node modules | |
# Use explicit restore and save actions to control | |
# precisely when in the workflow the cache is | |
# restored and saved. | |
uses: actions/cache/restore@v4 | |
with: | |
path: ${{ steps.npm-cache-dir.outputs.dir }} | |
key: ${{ runner.os }}-node-${{ matrix.configurations.node_version }}-${{ hashFiles('**/package-lock.json') }} | |
restore-keys: | | |
${{ runner.os }}-node-${{ matrix.configurations.node_version }}- | |
${{ runner.os }}-node- | |
- name: Main build | |
run: | | |
. ci/ciLibrary.source | |
composer_github_auth | |
main_build | |
- name: CCDA build | |
run: | | |
. ci/ciLibrary.source | |
ccda_build | |
- name: Save node cache | |
if: ${{ matrix.configurations.save_node_cache == 'true' }} | |
uses: actions/cache/save@v4 | |
with: | |
path: ${{ steps.npm-cache-dir.outputs.dir }} | |
key: ${{ runner.os }}-node-${{ matrix.configurations.node_version }}-${{ hashFiles('**/package-lock.json') }} | |
- name: Save composer cache | |
if: ${{ matrix.configurations.save_composer_cache == 'true' }} | |
uses: actions/cache/save@v4 | |
with: | |
path: ${{ steps.composer-cache.outputs.dir }} | |
key: ${{ runner.os }}-composer-${{ matrix.configurations.php }}-${{ hashFiles('**/composer.lock') }} | |
- name: Docker Compose Config Output | |
run: | | |
. ci/ciLibrary.source | |
dc config | |
- name: Dockers environment start | |
run: | | |
. ci/ciLibrary.source | |
dockers_env_start | |
- name: Wait for MySQL to initialize | |
if: ${{ matrix.configurations.database == 'mysql' }} | |
run: | | |
echo "Waiting 60 seconds for MySQL to initialize..." | |
sleep 60 | |
- name: Install and configure | |
run: | | |
. ci/ciLibrary.source | |
install_configure | |
- name: Prepare for coverage reporting | |
if: ${{ env.ENABLE_COVERAGE == 'true' }} | |
run: | | |
. ci/ciLibrary.source | |
configure_coverage | |
- name: Unit testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test unit | |
- name: Upload unit test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
## | |
# To skip E2E tests for specific docker directories, | |
# rename the docker directory to end with "_no-e2e". | |
- name: E2e testing | |
if: ${{ (success() || failure()) && matrix.configurations.e2e_enabled == 'true' }} | |
env: | |
# Change this to just 'selenium' to disable video recording. | |
COMPOSE_PROFILES: video-recording | |
run: | | |
. ci/ciLibrary.source | |
build_test e2e | |
- name: Upload E2e test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Upload E2E test videos | |
if: ${{ (success() || failure()) && matrix.configurations.e2e_enabled == 'true' && hashFiles('selenium-videos/video.mp4') != '' }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: e2e-test-videos-${{ matrix.configurations.docker_dir }} | |
path: selenium-videos/ | |
- name: Api testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test api | |
- name: Upload Api test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Fixtures testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test fixtures | |
- name: Upload Fixtures test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Services testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test services | |
- name: Upload Services test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Validators testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test validators | |
- name: Upload Validators test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Controllers testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test controllers | |
- name: Upload Controllers test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Common testing | |
if: ${{ success() || failure() }} | |
run: | | |
. ci/ciLibrary.source | |
build_test common | |
- name: Upload Common test results to Codecov | |
if: ${{ !cancelled() }} | |
uses: codecov/test-results-action@v1 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
- name: Combine coverage | |
if: ${{ env.ENABLE_COVERAGE == 'true' && (success() || failure()) }} | |
run: | | |
. ci/ciLibrary.source | |
merge_coverage | |
- name: Check if coverage files exist | |
if: ${{ env.ENABLE_COVERAGE == 'true' && (success() || failure()) }} | |
id: check-files | |
run: | | |
echo "clover_exists=$(test -f coverage.clover.xml && echo true || echo false)" >> $GITHUB_OUTPUT | |
echo "htmlcov_exists=$(test -d ./htmlcov && echo true || echo false)" >> $GITHUB_OUTPUT | |
- uses: actions/upload-artifact@v4 | |
if: ${{ env.ENABLE_COVERAGE == 'true' && steps.check-files.outputs.clover_exists == 'true' && (success() || failure()) }} | |
with: | |
name: coverage.clover.xml | |
path: coverage.clover.xml | |
- uses: actions/upload-artifact@v4 | |
if: ${{ env.ENABLE_COVERAGE == 'true' && steps.check-files.outputs.htmlcov_exists == 'true' && (success() || failure()) }} | |
with: | |
name: htmlcov | |
path: ./htmlcov/ | |
- name: Upload coverage reports to Codecov | |
if: ${{ env.ENABLE_COVERAGE == 'true' && steps.check-files.outputs.clover_exists == 'true' && (success() || failure()) }} | |
uses: codecov/codecov-action@v5 | |
with: | |
token: ${{ secrets.CODECOV_TOKEN }} | |
files: coverage.clover.xml |