Skip to content

[pull] develop from ehrbase:develop #82

[pull] develop from ehrbase:develop

[pull] develop from ehrbase:develop #82

name: "Build & Test"
# we have multiple workflows - this helps to distinguish for them
run-name: "${{ github.event.pull_request.title && github.event.pull_request.title || github.ref_name }} - Build & Test"
on:
push:
branches: [ master, develop, release/* ]
pull_request:
branches: [ develop ]
workflow_dispatch:
env:
JAVA_VERSION: 21
JAVA_DISTRIBUTION: 'temurin'
jobs:
#
# Performs maven build and check as well as junit test result collection. Finally, creates the ehrbase docker image
# and saves it, as an archive, for later usage.
#
build-maven:
name: Build-Maven
runs-on: ubuntu-latest
outputs:
# Map the step outputs to job outputs
ehrbase-version: ${{ steps.get_version.outputs.ehrbase-version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup - Java 21
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: ${{ env.JAVA_DISTRIBUTION }}
cache: 'maven'
- name: Setup - Dependency Cache
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: deps-${{ runner.os }}-m2-${{ github.head_ref }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
deps-${{ runner.os }}-m2-${{ github.head_ref }}-
deps-${{ runner.os }}-m2-
deps-${{ runner.os }}-
deps-
- name: Maven - Verify and Package
run: mvn --batch-mode --update-snapshots --activate-profiles full -Dmaven.test.failure.ignore=true verify package
- name: Maven - Get Version
id: "get_version"
run: |
# evaluate project version
version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
echo "EHRbase version [$version]"
echo "ehrbase-version=${version}" >> $GITHUB_OUTPUT
- name: Upload - Jar
uses: actions/upload-artifact@v4
with:
name: ehrbase-jar
path: ./application/target/ehrbase.jar
if-no-files-found: error
retention-days: 1
# Upload created class files that are needed for the merged jacoco coverage in a later step
- name: Upload - Class Files
if: always()
uses: actions/upload-artifact@v4
with:
name: java-class-files
path: "**/target/classes/**/*.class"
if-no-files-found: error
- name: Upload - Jacoco Coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: jacoco-coverage-partial-build-maven
path: "**/target/jacoco*.exec"
if-no-files-found: error
- name: Upload - Junit reports
uses: actions/upload-artifact@v4 # upload test results
if: success() || failure() # run this step even if previous step failed
with:
name: junit-test-results
path: '**/target/surefire-reports/*.xml'
#
# Performs docker test image builds.
#
docker-test-image:
name: Docker Test-Image
runs-on: ubuntu-latest
needs: [
build-maven
]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download - Jar
uses: actions/download-artifact@v4
with:
name: ehrbase-jar
path: ./application/target/
- name: Docker - Build Base Image
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
load: true
tags: ehrbase/ehrbase:build
- name: Docker - Build Test Image
run: |
docker build \
--tag ehrbase/ehrbase:test \
--build-arg EHRBASE_IMAGE=ehrbase/ehrbase:build \
--file tests/DockerfileTest .
- name: Docker - Save Test Image
run: docker save --output ${{ runner.temp }}/ehrbase-test.tar ehrbase/ehrbase:test
- name: Upload - Test Image
uses: actions/upload-artifact@v4
with:
name: ehrbase-image-test
path: ${{ runner.temp }}/ehrbase-test.tar
if-no-files-found: error
retention-days: 1
#
# Uses the ehrbase docker image from [build] to run the robot integrations against it.
#
integration-test-run:
runs-on: ubuntu-latest
needs: [
docker-test-image
]
strategy:
fail-fast: false # ensure all tests run
matrix:
test-suite: [
# Swagger EHRBase API endpoints checks
{ path: 'SWAGGER_TESTS', name: 'SWAGGER_TESTS', tags: 'SWAGGER_EHRBASE', env: { 'AUTH_TYPE': 'NONE' } },
# Basic and OUATH authorization type checks
{ path: 'AUTH_TYPE_TESTS', name: 'BASIC_AUTH_TYPE_TESTS', tags: 'AUTH_TYPE_TESTS_BASIC', env: { 'AUTH_TYPE': 'BASIC' } },
{ path: 'AUTH_TYPE_TESTS', name: 'OAUTH_AUTH_TYPE_TESTS', tags: 'AUTH_TYPE_TESTS_OAUTH', env: { 'AUTH_TYPE': 'OAUTH' } },
# sanity checks
{ path: 'SANITY_TESTS', name: 'SANITY', tags: 'Sanity', env: { 'AUTH_TYPE': 'NONE' } },
# rest/openehr/v1/definition
{ path: 'TEMPLATE_TESTS', name: 'TEMPLATE', tags: 'Template', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'STORED_QUERY_TESTS', name: 'STORED_QUERY', tags: 'stored_query', suite: 'TEST', env: { 'AUTH_TYPE': 'NONE' } },
## rest/openehr/v1/ehr
{ path: 'EHR_SERVICE_TESTS', name: 'EHR_SERVICE', tags: 'EHR_SERVICE', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'EHR_STATUS_TESTS', name: 'EHR_STATUS', tags: 'EHR_STATUS', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'DIRECTORY_TESTS', name: 'DIRECTORY', tags: 'directory', env: { 'AUTH_TYPE': 'NONE' } },
## rest/openehr/v1/ehr/{ehr_id}/contribution
{ path: 'CONTRIBUTION_TESTS', name: 'CONTRIBUTION', tags: 'CONTRIBUTION', env: { 'AUTH_TYPE': 'NONE' } },
## rest/openehr/v1/ehr/{ehr_id}/composition
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_CREATE_1', tags: 'compositionANDcomposition_create_1', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_CREATE_2', tags: 'compositionANDcomposition_create_2', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_CREATE_3', tags: 'compositionANDcomposition_create_3', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_CREATE_4', tags: 'compositionANDcomposition_create_4', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_CREATE_5', tags: 'compositionANDcomposition_create_5', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_GET', tags: 'compositionANDcomposition_get', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_UPDATE', tags: 'compositionANDcomposition_update', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_DELETE', tags: 'compositionANDcomposition_delete', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_GET_VERSIONED', tags: 'compositionANDcomposition_get_versioned', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_VALIDATION', tags: 'COMPOSITION_validation', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_HEADERS_CHECKS', tags: 'HeadersChecks', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_ISM_TRANSITIONS', tags: 'compositionANDcomposition_ism_transitions', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'COMPOSITION_TESTS', name: 'COMPOSITION_WITH_DIFFERENT_TIME_ZONES', tags: 'COMPOSITION_dtz', env: { 'AUTH_TYPE': 'NONE' } },
## rest/openehr/v1/query/aql - could be split into individual sub-suite
{ path: 'AQL_TESTS', name: 'AQL', tags: 'AQL_TESTS_PACKAGE', env: { 'AUTH_TYPE': 'NONE' } },
## rest/rest/ecis
{ path: 'EHRSCAPE_TESTS', name: 'EHRSCAPE', tags: 'EhrScapeTag', env: { 'AUTH_TYPE': 'NONE' } },
{ path: 'ADMIN_TESTS', name: 'ADMIN', tags: 'ADMIN', env: { 'AUTH_TYPE': 'NONE' } },
# TODO Still missing
# FHIR_TERMINOLOGY
# SECURITY_TESTS
]
name: Robot (${{ matrix.test-suite.name }})
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
tests
.env.ehrbase
docker-compose.yml
- name: Download - Test Image
uses: actions/download-artifact@v4
with:
name: ehrbase-image-test
path: ${{ runner.temp }}
- name: Docker - Load Image
run: docker load --input ${{ runner.temp }}/ehrbase-test.tar
# image used by the docker-compose-int-test.yml
- name: Docker Compose - Setup env
run: |
echo "EHRBASE_IMAGE=ehrbase/ehrbase:test" >> $GITHUB_ENV
echo "JACOCO_RESULT_PATH=/app/coverage/jacoco-${{ matrix.test-suite.path }}-${{ matrix.test-suite.name }}.exec" >> $GITHUB_ENV
- name: Modify .env.ehrbase file
run: |
echo "SECURITY_AUTHTYPE=${{ matrix.test-suite.env['AUTH_TYPE'] }}" >> .env.ehrbase
if [ "${{ matrix.test-suite.env['AUTH_TYPE'] }}" == "OAUTH" ]; then
echo "SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUERURI=http://keycloak:8080/auth/realms/ehrbase" >> .env.ehrbase
fi
cat .env.ehrbase
- name: Docker Compose - Starting
run: |
docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml up -d
- name: Wait for Keycloak to be ready
run: |
if [ "${{ matrix.test-suite.env['AUTH_TYPE'] }}" == "OAUTH" ]; then
until curl -s http://localhost:8081/auth/realms/ehrbase; do
echo "Waiting for Keycloak..."
sleep 30
done
#curl -s http://localhost:8081/auth/realms/ehrbase/.well-known/openid-configuration
fi
docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml ps
- name: Run - Robot Test-Suite
run: |
docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml run --remove-orphans --rm ehrbase-integration-tests runRobotTest \
--name ${{ matrix.test-suite.name }} \
--path ${{ matrix.test-suite.path }} \
--tags ${{ matrix.test-suite.tags }} \
--env ${{ matrix.test-suite.env['AUTH_TYPE'] }}
- name: Docker Compose - Logs ehrbase
if: always()
run: docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml logs ehrbase
- name: Docker Compose - Logs keycloak
if: always()
run: docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml logs keycloak
- name: Docker Compose - Stopping
if: always()
run: |
docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml down --remove-orphans
docker compose -f docker-compose.yml -f tests/docker-compose-int-test.yml rm --force --volumes
- name: Upload - Jacoco Coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: jacoco-coverage-partial-robot-${{ matrix.test-suite.path }}-${{ matrix.test-suite.name }}
path: ./tests/coverage/jacoco*.exec
if-no-files-found: error
- name: Upload - Robot results
if: always()
uses: actions/upload-artifact@v4
with:
name: robot-result-${{ matrix.test-suite.name }}
path: ./tests/results/${{ matrix.test-suite.name }}/output.xml
if-no-files-found: error
#
# Collect all Robot result from [integration-test-run] and generated the final report.
#
integration-test-collect:
name: Robot-Collect
if: ${{ always() }}
needs: [
integration-test-run
]
runs-on: ubuntu-latest
# allow to write comments to the issue
permissions:
issues: write
pull-requests: write
steps:
- name: Download - Robot results
uses: actions/download-artifact@v4
with:
pattern: robot-result-*
path: ./tests/results/
- name: Generate - Robot Tests-Report
run: |
docker run \
-v ./tests/results:/integration-tests/results \
-v ./tests/report:/integration-tests/report \
ehrbase/integration-tests:latest collectRebotResults
- name: Archive - Robot Report
if: always()
uses: actions/upload-artifact@v4
with:
name: robot-report-final
path: ./tests/report
- name: Cleanup - Test Folder
if: always()
run: |
rm -rf ./tests/result | true
rm -rf ./tests/report | true
#
# Collect all Robot result from [integration-test-run] and generated the final report.
#
coverage-collect:
name: Jacoco-Collect
if: ${{ always() }}
needs: [
integration-test-run
]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download - Class files
uses: actions/download-artifact@v4
with:
name: java-class-files
path: ./
- name: Download - Robot results
uses: actions/download-artifact@v4
with:
pattern: jacoco-coverage-partial-*
path: ./tests/coverage
- name: Docker - Build Jacoco-CLI
uses: docker/build-push-action@v6
env:
DOCKER_BUILD_NO_SUMMARY: true
with:
context: .
file: tests/DockerfileJacocoCLI
load: true
tags: jacoco-cli:local
# create merged.exec
- name: Jacoco - Merge
run: |
cd ./tests/coverage
docker run --rm -v ./:/workspace -w /workspace --pull never jacoco-cli:local merge $(find . -type f -name '*.exec' | tr '\n' ' ') --destfile jacoco-merged.exec
# it is easier to copy over .java and .class and pass them later as a bundle to the jacoco report generation
- name: Collect - Sources & Classes
run: |
mkdir -p ./tests/coverage/ehrbase
mkdir -p ./tests/coverage/ehrbase/src
find . -type d -path '*/src/main/java' | xargs -0 sh -c 'cp -prnv $0 ./tests/coverage/ehrbase/src' | true
find . -type d -path '*/generated-sources' | xargs -0 sh -c 'cp -prnv $0 ./tests/coverage/ehrbase/src' | true
find . -type d -path '*/target/classes' | xargs -0 sh -c 'cp -prnv $0 ./tests/coverage/ehrbase' | true
# create final jacoco report
- name: Jacoco - Report
run: |
cd ./tests/coverage
mkdir -p jacoco-report-final
docker run --rm -v $(pwd)/:/workspace -w /workspace --pull never jacoco-cli:local report jacoco-merged.exec --classfiles ehrbase/classes/ --sourcefiles ehrbase/src/java --sourcefiles ehrbase/src/generated-sources --encoding utf-8 --name Merged --html ./jacoco-report-final --xml ./jacoco-report-final/jacoco.xml
- name: Archive - Jacoco Report
if: always()
uses: actions/upload-artifact@v4
with:
name: jacoco-report-final
path: ./tests/coverage/jacoco-report-final
#
# Build and push docker image
#
docker-build-push:
name: Docker
uses: ./.github/workflows/job-docker-build-push.yml
secrets: inherit
# ignore dependabot here.
if: ${{ github.actor != 'dependabot[bot]' }}
needs: [
build-maven, # needed to obtain ehrbase-version from
integration-test-run
]
with:
ehrbase-version: ${{ needs.build-maven.outputs.ehrbase-version }}
ehrbase-jar-artifact: ehrbase-jar
#
# Maven publish
#
maven-publish:
name: Maven
uses: ./.github/workflows/job-maven-publish.yml
secrets: inherit
# ignore dependabot here.
if: ${{ github.actor != 'dependabot[bot]' }}
needs: [
build-maven, # only to have them in the same UI group as docker-build-push
integration-test-run
]
#
# Cleanup serialized oci image as well as intermediate robot results
#
cleanup:
name: Cleanup
if: ${{ always() }}
needs: [
coverage-collect,
integration-test-collect,
docker-build-push,
maven-publish
]
runs-on: ubuntu-latest
steps:
- name: Delete - Temp Artifacts
uses: geekyeggo/delete-artifact@v5
with:
name: |
java-class-files
ehrbase-jar
ehrbase-image-test
robot-result-*
jacoco-coverage-*
failOnError: false