+
Skip to content

Add global front controller with security hardening #10825

Add global front controller with security hardening

Add global front controller with security hardening #10825

Workflow file for this run

##
# 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: FrontController testing
if: ${{ success() || failure() }}
run: |
. ci/ciLibrary.source
build_test frontcontroller
- name: Upload FrontController 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
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载