From 906e17ff98440a5f6470e9e6cd3c836adee47028 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Mon, 28 Sep 2026 10:03:09 +0200 Subject: [PATCH 1/8] Use the python scripts instead of the bash-based scripts --- .github/workflows/CI_build_and_test.yml | 84 +++++++++++++++++++------ 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 6bdef27983d..6f85df8e09a 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -103,7 +103,7 @@ jobs: echo "Workspace folder for this build will be $WORKSPACE" - - name: Clone SOFA and CI + - name: Clone SOFA, CI and Regression id: clone shell: bash run: | @@ -170,6 +170,33 @@ jobs: cd $WORKSPACE + ## Clone Regression and use ci-depends-on structure + regression_branch=${{ inputs.sofa-branch-name }} + regression_repo_url="https://www.github.com/sofa-framework/Regression" + + + # check if ci has a ci-depends-on + regression_ci_depends_on=$(echo "${{ inputs.ci-depends-on }}" | jq .Regression) + if [ -n "${{ inputs.ci-depends-on }}" ] && [ "$regression_ci_depends_on" != "null" ]; then + echo "ci-depends-on for Regression repository detected." + regression_repo_url=$(echo "${{ inputs.ci-depends-on }}" | jq .Regression.repo_url) + regression_branch=$(echo "${{ inputs.ci-depends-on }}" | jq .Regression.branch_name) + fi + echo "REGRESSION_BRANCH=$regression_branch" >> $GITHUB_ENV + + + echo "Cloning Regression from remote ${regression_repo_url}, selecting branch ${regression_branch}" + + if [ -d $WORKSPACE/Regression ]; then + rm -rf $WORKSPACE/Regression + fi + REGRESSION_DIR=$WORKSPACE/Regression + echo "REGRESSION_DIR=$REGRESSION_DIR" >> $GITHUB_ENV + + git clone -b ${regression_branch//\"} --single-branch ${regression_repo_url//\"} + + cd $WORKSPACE + ## Setup build folder if [ "${{ inputs.force-full-build }}" == "true" ] && [ -d $WORKSPACE/build ]; then rm -rf $WORKSPACE/build @@ -286,11 +313,13 @@ jobs: DOCKER_SRC_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $SRC_DIR)" DOCKER_BUILD_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $BUILD_DIR)" DOCKER_CI_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $CI_DIR)" + DOCKER_REGRESSION_DIR="/workspace/$(realpath --relative-to=$WORKSPACE $REGRESSION_DIR)" DOCKER_LOG_DIR="/workspace/$(realpath --relative-to=$WORKSPACE ${{ env.LOG_DIR }})" echo "DOCKER_SRC_DIR=$DOCKER_SRC_DIR" >> $GITHUB_ENV echo "DOCKER_BUILD_DIR=$DOCKER_BUILD_DIR" >> $GITHUB_ENV echo "DOCKER_CI_DIR=$DOCKER_CI_DIR" >> $GITHUB_ENV + echo "DOCKER_REGRESSION_DIR=$DOCKER_REGRESSION_DIR" >> $GITHUB_ENV echo "DOCKER_LOG_DIR=$DOCKER_LOG_DIR" >> $GITHUB_ENV export XDG_CACHE_HOME="/cache" @@ -320,17 +349,24 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} UNIT" + /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_unit_tests.py --build-dir ${DOCKER_BUILD_DIR} --results-dir \${RESULTS_DIR}/unit -j \${VM_MAX_PARALLEL_TESTS}" else echo "Launching unit test suite." - bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "UNIT" + . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + env + ${VM_PYTHON3_EXECUTABLE} ${CI_DIR}/scripts/tests/sofa_unit_tests.py --build-dir "${BUILD_DIR}" --results-dir "${RESULTS_DIR}/unit" -j ${VM_MAX_PARALLEL_TESTS} fi - if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"unit-tests_"* ]]; then + if [[ ! "$(cd "${{ env.BUILD_DIR }}/tests_results/unit" && find . -maxdepth 1 -type f)" == *"summary"* ]]; then exit 1 + else + if [[ "$(awk -F= '$1=="crashes" {print $2}' ${{ env.BUILD_DIR }}/tests_results/unit/summary.txt)" != '0' || "$(awk -F= '$1=="failures" {print $2}' ${{ env.BUILD_DIR }}/tests_results/unit/summary.txt)" != "0" ]]; then + exit 1 + fi fi + - name: Scene tests id: scene-tests-step if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true @@ -344,15 +380,21 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} SCENE" + /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_scene_tests.py --src-dir "${DOCKER_SRC_DIR}" --build-dir \"${DOCKER_BUILD_DIR}\" --results-dir \"\${RESULTS_DIR}/scene\" -j \${VM_MAX_PARALLEL_TESTS}" else echo "Launching scene test suite." - bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "SCENE" + . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + env + ${VM_PYTHON3_EXECUTABLE} ${CI_DIR}/scripts/tests/sofa_scene_tests.py --src-dir "${SRC_DIR}" --build-dir "${BUILD_DIR}" --results-dir "${RESULTS_DIR}/scene" -j ${VM_MAX_PARALLEL_TESTS} fi - if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"scene-tests_"* ]]; then + if [[ ! "$(cd "${{ env.BUILD_DIR }}/tests_results/scene" && find . -maxdepth 1 -type f)" == *"summary"* ]]; then exit 1 + else + if [[ "$(awk -F= '$1=="crashes" {print $2}' ${{ env.BUILD_DIR }}/tests_results/scene/summary.txt)" != '0' || "$(awk -F= '$1=="failures" {print $2}' ${{ env.BUILD_DIR }}/tests_results/scene/summary.txt)" != "0" ]]; then + exit 1 + fi fi - name: Regression tests @@ -368,15 +410,21 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "env ; cd /workspace; /bin/bash $DOCKER_CI_DIR/scripts/test.sh $DOCKER_BUILD_DIR $DOCKER_SRC_DIR $DOCKER_CI_DIR/scripts/ ${NODE_NAME} ${{ inputs.python-version }} REGRESSION" + /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --output-logs-errors "\${RESULTS_DIR}/regression" --legacy-regression --jobs \${VM_MAX_PARALLEL_TESTS} " else echo "Launching regression test suite" - bash ${CI_DIR}/scripts/test.sh "${BUILD_DIR}" "${SRC_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" "REGRESSION" + . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + env + ${VM_PYTHON3_EXECUTABLE} ${REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${SRC_DIR}/examples" --input "${SRC_DIR}/applications" --input "${BUILD_DIR}/external_directories/fetched" --output-logs-errors "${RESULTS_DIR}/regression" --legacy-regression -j ${VM_MAX_PARALLEL_TESTS} fi - if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" == *"regression-tests_"* ]]; then - exit 1 + if [[ ! "$(cd "${{ env.BUILD_DIR }}/tests_results/regression" && find . -maxdepth 1 -type f)" == *"summary"* ]]; then + exit 1 + else + if [[ "$(awk -F= '$1=="crashes" {print $2}' ${{ env.BUILD_DIR }}/tests_results/regression/summary.txt)" != '0' || "$(awk -F= '$1=="failures" {print $2}' ${{ env.BUILD_DIR }}/tests_results/regression/summary.txt)" != "0" ]]; then + exit 1 + fi fi @@ -433,7 +481,7 @@ jobs: fi if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then - if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -maxdepth 1 -type f)" != "" ]]; then + if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -mindepth 2 -maxdepth 2 -type f -name 'summary.txt')" != "" ]]; then echo "❌ Tests have resulted in : " echo "" python3 ${{ env.CI_DIR }}/scripts/generate-tests-table.py "${{ env.BUILD_DIR }}/tests_results/" @@ -443,18 +491,18 @@ jobs: echo "#----------------------------------------#" for testType in 'unit' 'scene' 'regression'; do + capitalized="$(echo "${testType:0:1}" | tr '[:lower:]' '[:upper:]')${testType:1}" + echo -e "${bold}${capitalized}:${normal}" for errorType in 'errors' 'crashes' 'failures'; do - if [ -f "${{ env.BUILD_DIR }}/tests_results/${testType}-tests_${errorType}" ]; then - capitalized="$(echo "${testType:0:1}" | tr '[:lower:]' '[:upper:]')${testType:1}" - echo "" - echo -e "${bold}${capitalized} ${errorType}:${normal}" + if [ -f "${{ env.BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" ]; then echo "" - cat "${{ env.BUILD_DIR }}/tests_results/${testType}-tests_${errorType}" + cat "${{ env.BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" echo "" - echo "#----------------------------------------#" fi done + echo "#----------------------------------------#" + echo "" done echo -e "${bold}You can download all logs here : ${{ steps.publish-tests-logs.outputs.artifact-url }}${normal}" exit 1 From c875728e597676c4815be88bc9f1336ef143cc05 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Mon, 28 Sep 2026 10:03:43 +0200 Subject: [PATCH 2/8] TO_REVERT --- .../workflows/CI_trigger_build_and_tests.yml | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CI_trigger_build_and_tests.yml b/.github/workflows/CI_trigger_build_and_tests.yml index da2efb038be..92b77c9e147 100644 --- a/.github/workflows/CI_trigger_build_and_tests.yml +++ b/.github/workflows/CI_trigger_build_and_tests.yml @@ -28,10 +28,10 @@ on: preset: type: choice description: Which preset to use for compilation - options: + options: - standard - supported-plugins - - full + - full - standard-dev - supported-plugins-dev - full-dev @@ -39,16 +39,16 @@ on: builder-os: type: choice description: On which OS run the binaries generation - options: + options: - '["sh-ubuntu_gcc_release"]' - '["sh-macos_clang_release"]' - '["sh-ubuntu_gcc_release","sh-macos_clang_release"]' default: '["sh-ubuntu_gcc_release","sh-macos_clang_release"]' - + # PR-related build (open, labels, push) pull_request: types: [opened, synchronize] - + # CI for dashboard master push: branches: @@ -79,7 +79,7 @@ jobs: PR_BRANCH_NAME: ${{ steps.export-vars.outputs.PR_BRANCH_NAME }} PR_COMMIT_SHA: ${{ steps.export-vars.outputs.PR_COMMIT_SHA }} BUILDER_OS: ${{ steps.export-vars.outputs.BUILDER_OS }} - + steps: - name: Default values of environment variables run: | @@ -97,7 +97,7 @@ jobs: echo "PR_BRANCH_NAME=" >> $GITHUB_ENV # PR_BRANCH_NAME: "" echo "PR_COMMIT_SHA=HEAD" >> $GITHUB_ENV # PR_COMMIT_SHA: "HEAD" echo 'BUILDER_OS=["sh-ubuntu_gcc_release"]' >> $GITHUB_ENV # BUILDER_OS: ["sh-ubuntu_gcc_release"] - + - name: Run on dispatch if: ${{ github.event_name == 'workflow_dispatch' }} run: | @@ -122,7 +122,7 @@ jobs: fi fi echo "Python version $PYTHON is valid." - + # Save all information in environment variables echo "SOFA_BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_ENV echo "SOFA_COMMIT_SHA=HEAD" >> $GITHUB_ENV @@ -170,11 +170,11 @@ jobs: PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} run: | echo "This step runs only when a PR is opened or synchronized." - + # Trigger the Build action # Warning ! This script also changes ENV variables python ci/scripts/checkPRInfoBeforeBuild.py - + - name: Export environment variables as outputs id: export-vars run: | @@ -184,7 +184,7 @@ jobs: echo "Branch name should be either master or any release branch (e.g., v25.06)" exit 1 fi - + echo "SOFA_BRANCH_NAME=${SOFA_BRANCH_NAME}" >> $GITHUB_OUTPUT echo "SOFA_COMMIT_SHA=${SOFA_COMMIT_SHA}" >> $GITHUB_OUTPUT echo "PRESET=${PRESET}" >> $GITHUB_OUTPUT @@ -192,8 +192,8 @@ jobs: echo "CI_DEPENDS_ON=${CI_DEPENDS_ON}" >> $GITHUB_OUTPUT echo "WITH_ALL_TESTS=${WITH_ALL_TESTS}" >> $GITHUB_OUTPUT echo "FORCE_FULL_BUILD=${FORCE_FULL_BUILD}" >> $GITHUB_OUTPUT - echo "EXTERNAL_PLUGINS=${EXTERNAL_PLUGINS}" >> $GITHUB_OUTPUT - echo "ADDITIONNAL_CMAKE_FLAGS=${ADDITIONNAL_CMAKE_FLAGS}" >> $GITHUB_OUTPUT + echo "EXTERNAL_PLUGINS=${EXTERNAL_PLUGINS}" >> $GITHUB_OUTPUT + echo "ADDITIONNAL_CMAKE_FLAGS=${ADDITIONNAL_CMAKE_FLAGS}" >> $GITHUB_OUTPUT echo "GENERATE_BINARIES=${GENERATE_BINARIES}" >> $GITHUB_OUTPUT echo "PR_OWNER_URL=${PR_OWNER_URL}" >> $GITHUB_OUTPUT echo "PR_BRANCH_NAME=${PR_BRANCH_NAME}" >> $GITHUB_OUTPUT @@ -208,7 +208,7 @@ jobs: build-on: needs: filter_build if: ${{ github.repository_owner == 'sofa-framework' }} - uses: sofa-framework/sofa/.github/workflows/CI_build_and_test.yml@master + uses: bakpaul/sofa/.github/workflows/CI_build_and_test.yml@26_09_use_python_based_ci_script_on_unix_ci with: sofa-branch-name: ${{ needs.filter_build.outputs.SOFA_BRANCH_NAME }} sofa-commit-sha: ${{ needs.filter_build.outputs.SOFA_COMMIT_SHA }} From 4dfffca05127b00e5bf14c281a4502ac7cb5269c Mon Sep 17 00:00:00 2001 From: Paul Baksic <30337881+bakpaul@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:55:28 +0200 Subject: [PATCH 3/8] Update CI_build_and_test.yml --- .github/workflows/CI_build_and_test.yml | 35 +++++++++++++------------ 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 6f85df8e09a..5e94e2f9e7a 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -349,11 +349,11 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_unit_tests.py --build-dir ${DOCKER_BUILD_DIR} --results-dir \${RESULTS_DIR}/unit -j \${VM_MAX_PARALLEL_TESTS}" + /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_unit_tests.py --build-dir ${DOCKER_BUILD_DIR} --results-dir \${RESULTS_DIR}/unit -j \${VM_MAX_PARALLEL_TESTS}" else echo "Launching unit test suite." - . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + . ${CI_DIR}/scripts/tests/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" env ${VM_PYTHON3_EXECUTABLE} ${CI_DIR}/scripts/tests/sofa_unit_tests.py --build-dir "${BUILD_DIR}" --results-dir "${RESULTS_DIR}/unit" -j ${VM_MAX_PARALLEL_TESTS} fi @@ -380,11 +380,11 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_scene_tests.py --src-dir "${DOCKER_SRC_DIR}" --build-dir \"${DOCKER_BUILD_DIR}\" --results-dir \"\${RESULTS_DIR}/scene\" -j \${VM_MAX_PARALLEL_TESTS}" + /bin/bash -c "cd /workspace; . ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_CI_DIR}/scripts/tests/sofa_scene_tests.py --src-dir "${DOCKER_SRC_DIR}" --build-dir \"${DOCKER_BUILD_DIR}\" --results-dir \"\${RESULTS_DIR}/scene\" -j \${VM_MAX_PARALLEL_TESTS}" else echo "Launching scene test suite." - . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + . ${CI_DIR}/scripts/tests/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" env ${VM_PYTHON3_EXECUTABLE} ${CI_DIR}/scripts/tests/sofa_scene_tests.py --src-dir "${SRC_DIR}" --build-dir "${BUILD_DIR}" --results-dir "${RESULTS_DIR}/scene" -j ${VM_MAX_PARALLEL_TESTS} fi @@ -410,11 +410,11 @@ jobs: docker run --rm \ --user $(id -u):$(id -g) --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --output-logs-errors "\${RESULTS_DIR}/regression" --legacy-regression --jobs \${VM_MAX_PARALLEL_TESTS} " + /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --output-logs-errors "\${RESULTS_DIR}/regression" --legacy-regression --jobs \${VM_MAX_PARALLEL_TESTS} " else echo "Launching regression test suite" - . ${CI_DIR}/scripts/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + . ${CI_DIR}/scripts/tests/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" env ${VM_PYTHON3_EXECUTABLE} ${REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${SRC_DIR}/examples" --input "${SRC_DIR}/applications" --input "${BUILD_DIR}/external_directories/fetched" --output-logs-errors "${RESULTS_DIR}/regression" --legacy-regression -j ${VM_MAX_PARALLEL_TESTS} fi @@ -481,39 +481,40 @@ jobs: fi if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then - if [[ "$(cd "${{ env.BUILD_DIR }}/tests_results/" && find . -mindepth 2 -maxdepth 2 -type f -name 'summary.txt')" != "" ]]; then + if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && + [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ]]; then echo "❌ Tests have resulted in : " echo "" - python3 ${{ env.CI_DIR }}/scripts/generate-tests-table.py "${{ env.BUILD_DIR }}/tests_results/" + python3 ${{ env.CI_DIR }}/scripts/tests/generate-tests-table.py "${{ env.SOFA_BUILD_DIR }}/tests_results/" echo "" echo "#----------------------------------------#" echo "#---------------Quick Logs---------------#" echo "#----------------------------------------#" for testType in 'unit' 'scene' 'regression'; do - capitalized="$(echo "${testType:0:1}" | tr '[:lower:]' '[:upper:]')${testType:1}" - echo -e "${bold}${capitalized}:${normal}" for errorType in 'errors' 'crashes' 'failures'; do - if [ -f "${{ env.BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" ]; then + if [ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" ]; then + capitalized="$(echo "${testType:0:1}" | tr '[:lower:]' '[:upper:]')${testType:1}" echo "" - cat "${{ env.BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" + echo -e "${bold}${capitalized} ${errorType}:${normal}" echo "" + cat "${{ env.SOFA_BUILD_DIR }}/tests_results/${testType}/${testType}_${errorType}.txt" + echo "" + echo "#----------------------------------------#" fi done - echo "#----------------------------------------#" - echo "" done echo -e "${bold}You can download all logs here : ${{ steps.publish-tests-logs.outputs.artifact-url }}${normal}" exit 1 elif [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]]; then - echo "❌ Something whent wrong during one of the tests steps" + echo "❌ Something went wrong during one of the tests steps" echo -e "${bold}Please check this action logs, and download tests logs here: ${{ steps.publish-tests-logs.outputs.artifact-url }}${normal}" exit 1 else - echo "✅ Tests where successful : " + echo "✅ Tests were successful : " echo "" - python3 ${{ env.CI_DIR }}/scripts/generate-tests-table.py "${{ env.BUILD_DIR }}/tests_results/" + python3 ${{ env.CI_DIR }}/scripts/tests/generate-tests-table.py "${{ env.SOFA_BUILD_DIR }}/tests_results/" echo "" fi fi From c463eea14d01a2b45b6ae3eb67faf6f8f73c044a Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Wed, 7 Oct 2026 16:53:18 +0200 Subject: [PATCH 4/8] fix quick summary --- .github/workflows/CI_build_and_test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 5e94e2f9e7a..e116d0ec214 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -481,8 +481,9 @@ jobs: fi if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then - if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && - [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ]]; then + if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && + [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" ]] && + [[ "${{ inputs.with-all-tests }}" == "false" || "${{ inputs.with-all-tests }}" == "true" && ( -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ) ]]; then echo "❌ Tests have resulted in : " echo "" python3 ${{ env.CI_DIR }}/scripts/tests/generate-tests-table.py "${{ env.SOFA_BUILD_DIR }}/tests_results/" From b4e7310df6a6ab420766b485d73b40fc1c42302c Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Tue, 27 Oct 2026 17:30:19 +0100 Subject: [PATCH 5/8] Don t use legacy, use new references instead --- .github/workflows/CI_build_and_test.yml | 4 ++-- .github/workflows/CI_macos_linux_windows_pixi.yml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 81dcd0c82e1..9e886fa1262 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -409,13 +409,13 @@ jobs: docker run --rm \ --user 1000:1000 --network=host -v $WORKSPACE:/workspace \ ${DOCKER_IMAGE} \ - /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --output-logs-errors "\${RESULTS_DIR}/regression" --legacy-regression --jobs \${VM_MAX_PARALLEL_TESTS} " + /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --output-logs-errors "\${RESULTS_DIR}/regression" --jobs \${VM_MAX_PARALLEL_TESTS} " else echo "Launching regression test suite" . ${CI_DIR}/scripts/tests/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" env - ${VM_PYTHON3_EXECUTABLE} ${REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${SRC_DIR}/examples" --input "${SRC_DIR}/applications" --input "${BUILD_DIR}/external_directories/fetched" --output-logs-errors "${RESULTS_DIR}/regression" --legacy-regression -j ${VM_MAX_PARALLEL_TESTS} + ${VM_PYTHON3_EXECUTABLE} ${REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${SRC_DIR}/examples" --input "${SRC_DIR}/applications" --input "${BUILD_DIR}/external_directories/fetched" --output-logs-errors "${RESULTS_DIR}/regression" -j ${VM_MAX_PARALLEL_TESTS} fi if [[ ! "$(cd "${{ env.BUILD_DIR }}/tests_results/regression" && find . -maxdepth 1 -type f)" == *"summary"* ]]; then diff --git a/.github/workflows/CI_macos_linux_windows_pixi.yml b/.github/workflows/CI_macos_linux_windows_pixi.yml index 334185561cf..17790c480c7 100644 --- a/.github/workflows/CI_macos_linux_windows_pixi.yml +++ b/.github/workflows/CI_macos_linux_windows_pixi.yml @@ -379,7 +379,6 @@ jobs: --input "$env:SRC_DIR\applications" ` --input "$env:BUILD_DIR\external_directories\fetched" ` --output-logs-errors "$RegResultsDir" ` - --legacy-regression ` -j $env:VM_MAX_PARALLEL_TESTS if (-not (Test-Path "$RegResultsDir\*summary*")) { exit 1 } From 4be9d8c48ad102998772ae181943d273ad7bfb3b Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Wed, 28 Oct 2026 16:19:53 +0100 Subject: [PATCH 6/8] Fix summary --- .github/workflows/CI_build_and_test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 9e886fa1262..4c4dbf7053b 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -480,9 +480,8 @@ jobs: fi if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then - if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && - [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" ]] && - [[ "${{ inputs.with-all-tests }}" == "false" || "${{ inputs.with-all-tests }}" == "true" && ( -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ) ]]; then + if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && + [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ]]; then echo "❌ Tests have resulted in : " echo "" python3 ${{ env.CI_DIR }}/scripts/tests/generate-tests-table.py "${{ env.SOFA_BUILD_DIR }}/tests_results/" From ec12526beedb8dce5cba210c01167b2d08707904 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Thu, 29 Oct 2026 15:58:14 +0100 Subject: [PATCH 7/8] Increase SofaScene timeout so that scene tests passes --- examples/.scene-tests | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/.scene-tests b/examples/.scene-tests index ce795d51776..14a1fb4daf9 100644 --- a/examples/.scene-tests +++ b/examples/.scene-tests @@ -53,3 +53,7 @@ ignore "Benchmark/Performance/Bar16-spring-rk4-CudaVec3f.py" # Crash on windows ignore "Component/Constraint/Lagrangian/PrecomputedConstraintCorrection.scn" + + +# Slow on windows github-hosted machines +timeout "Demos/SofaScene.scn" 60 From 01b38b01ee0cd3db2479781ae315c786ef92e639 Mon Sep 17 00:00:00 2001 From: Paul Baksic Date: Mon, 2 Nov 2026 19:46:15 +0100 Subject: [PATCH 8/8] Add CI_generate_regression_references workflow and ci-update-ref steps in build workflows - New workflow_dispatch workflow (input: pr_number) that parses the [ci-update-ref {json}] tag of the SOFA-PR, creates the ci/add_sofa_prXXXX_references branches, calls both build workflows and opens the REF-PRs plus a single summary comment at the end. - CI_build_and_test and CI_macos_linux_windows_pixi: new ci-update-ref input (empty by default), bot token exposed as GH_TOKEN, unit/scene tests skipped and reference update steps run when the input is set. All changes are wrapped in CI-UPDATE-REF BEGIN/END markers. --- .github/workflows/CI_build_and_test.yml | 92 +++++++++- .../CI_generate_regression_references.yml | 157 ++++++++++++++++++ .../workflows/CI_macos_linux_windows_pixi.yml | 91 +++++++++- 3 files changed, 334 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/CI_generate_regression_references.yml diff --git a/.github/workflows/CI_build_and_test.yml b/.github/workflows/CI_build_and_test.yml index 4c4dbf7053b..f2b5f290730 100644 --- a/.github/workflows/CI_build_and_test.yml +++ b/.github/workflows/CI_build_and_test.yml @@ -53,6 +53,16 @@ on: required: false default: 'NONE' description: 'Json scructure with three parameters {"COMMIT_HASH":"fullhsashofthecommit", }' +# >>> CI-UPDATE-REF BEGIN + ci-update-ref: + type: string + required: false + default: '' + description: 'When not empty, only build and generate the regression references (JSON given by CI_generate_regression_references)' + secrets: + SOFA_BOT_TOKEN: + required: false +# <<< CI-UPDATE-REF END jobs: display-inputs: @@ -76,6 +86,9 @@ jobs: echo "external-plugins : ${{ inputs.external-plugins }}" echo "additionnal-cmake-flags : ${{ inputs.additionnal-cmake-flags }}" echo "builder-os : ${{ inputs.builder-os }}" + # >>> CI-UPDATE-REF BEGIN + echo "ci-update-ref : ${{ inputs.ci-update-ref != '' }}" + # <<< CI-UPDATE-REF END build: @@ -86,6 +99,17 @@ jobs: runs-on: ${{ matrix.os }} name: ${{ matrix.os }} steps: + # >>> CI-UPDATE-REF BEGIN + - name: Export bot token + if: ${{ inputs.ci-update-ref != '' }} + shell: bash + env: + BOT_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + run: | + echo "::add-mask::$BOT_TOKEN" + echo "GH_TOKEN=$BOT_TOKEN" >> $GITHUB_ENV + # <<< CI-UPDATE-REF END + - name: Configure builder id: configure shell: bash @@ -337,7 +361,7 @@ jobs: - name: Unit tests id: unit-tests-step - if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: bash run: | # If os has got docker then use it @@ -368,7 +392,7 @@ jobs: - name: Scene tests id: scene-tests-step - if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: bash run: | # If os has got docker then use it @@ -398,7 +422,7 @@ jobs: - name: Regression tests id: regression-tests-step - if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: bash run: | # If os has got docker then use it @@ -427,6 +451,57 @@ jobs: fi + # >>> CI-UPDATE-REF BEGIN + - name: Update references - prepare + id: update-ref-prepare + if: always() && steps.build-step.outcome == 'success' && inputs.ci-update-ref != '' + shell: bash + env: + CI_UPDATE_REF: ${{ inputs.ci-update-ref }} + run: | + # The reference folder is named after the OS of the (docker) image running the regression + echo "CI_UPDATE_REF_OS=$(echo ${{ matrix.os }} | sed -E 's/^sh-([a-z]+)_.*/\1/')" >> $GITHUB_ENV + export CI_UPDATE_REF_OS=$(echo ${{ matrix.os }} | sed -E 's/^sh-([a-z]+)_.*/\1/') + python3 ${CI_DIR}/scripts/ci_update_ref_build.py prepare + + - name: Update references - generate + id: update-ref-generate + if: always() && steps.update-ref-prepare.outcome == 'success' + shell: bash + run: | + if [ ! -s "${BUILD_DIR}/ci_update_ref/filter.txt" ]; then + echo "No scene to generate." + exit 0 + fi + # If os has got docker then use it + if [[ "${{ matrix.os }}" == *"ubuntu"* ]] || [[ "${{ matrix.os }}" == *"fedora"* ]]; then + echo "Generating references through docker using image ${DOCKER_IMAGE}." + docker run --rm \ + --user 1000:1000 --network=host -v $WORKSPACE:/workspace \ + ${DOCKER_IMAGE} \ + /bin/bash -c "export REGRESSION_DIR="$DOCKER_REGRESSION_DIR"; cd /workspace;. ${DOCKER_CI_DIR}/scripts/tests/setup-test-env.sh \"${DOCKER_BUILD_DIR}\" \"${DOCKER_CI_DIR}/scripts/\" \"${NODE_NAME}\" \"${{ inputs.python-version }}\"; env ; \${VM_PYTHON3_EXECUTABLE} ${DOCKER_REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${DOCKER_SRC_DIR}/examples" --input "${DOCKER_SRC_DIR}/applications" --input "${DOCKER_BUILD_DIR}/external_directories/fetched" --filter \"\$(cat ${DOCKER_BUILD_DIR}/ci_update_ref/filter.txt)\" --write-references --output-logs-errors "\${RESULTS_DIR}/regression" --jobs \${VM_MAX_PARALLEL_TESTS} " + else + . ${CI_DIR}/scripts/tests/setup-test-env.sh "${BUILD_DIR}" "${CI_DIR}/scripts/" "${NODE_NAME}" "${{ inputs.python-version }}" + ${VM_PYTHON3_EXECUTABLE} ${REGRESSION_DIR}/SofaRegressionProgram/SofaRegressionProgram.py --input "${SRC_DIR}/examples" --input "${SRC_DIR}/applications" --input "${BUILD_DIR}/external_directories/fetched" --filter "$(cat ${BUILD_DIR}/ci_update_ref/filter.txt)" --write-references --output-logs-errors "${RESULTS_DIR}/regression" -j ${VM_MAX_PARALLEL_TESTS} + fi + + if [[ ! "$(cd "${{ env.BUILD_DIR }}/tests_results/regression" && find . -maxdepth 1 -type f)" == *"summary"* ]]; then + exit 1 + elif [[ "$(awk -F= '$1=="crashes" {print $2}' ${{ env.BUILD_DIR }}/tests_results/regression/summary.txt)" != '0' ]]; then + exit 1 + fi + + - name: Update references - commit and push + id: update-ref-commit + if: always() && steps.update-ref-generate.outcome == 'success' + shell: bash + env: + CI_UPDATE_REF: ${{ inputs.ci-update-ref }} + BUILD_COMMIT: ${{ (inputs.pr-commit-sha != '' && inputs.pr-commit-sha) || inputs.sofa-commit-sha }} + run: | + python3 ${CI_DIR}/scripts/ci_update_ref_build.py commit + # <<< CI-UPDATE-REF END + - name: Publish artifacts if: steps.build-step.outcome == 'success' && inputs.generate-binaries == true uses: actions/upload-artifact@v4 @@ -479,6 +554,17 @@ jobs: echo "✅ Build OK." fi + # >>> CI-UPDATE-REF BEGIN + if [[ -n "${{ inputs.ci-update-ref }}" ]]; then + if [[ "${{ steps.update-ref-prepare.outcome }}" == "failure" || "${{ steps.update-ref-generate.outcome }}" == "failure" || "${{ steps.update-ref-commit.outcome }}" == "failure" ]]; then + echo "❌ References generation failed" + exit 1 + fi + echo "✅ References generated and pushed." + exit 0 + fi + # <<< CI-UPDATE-REF END + if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ]]; then diff --git a/.github/workflows/CI_generate_regression_references.yml b/.github/workflows/CI_generate_regression_references.yml new file mode 100644 index 00000000000..103f6a0d459 --- /dev/null +++ b/.github/workflows/CI_generate_regression_references.yml @@ -0,0 +1,157 @@ +# >>> CI-UPDATE-REF BEGIN +name: Generate regression references + +# =============================================================== +# Generate the regression references of every OS for the scenes listed in the +# [ci-update-ref {json}] tag of the SOFA-PR description, push them on the branch +# ci/add_sofa_prXXXX_references of each concerned repository and open the REF-PRs. +# =============================================================== + +on: + workflow_dispatch: + inputs: + pr_number: + description: 'Number of the SOFA-PR for which the references are generated' + required: true + type: string + +jobs: + # Parse the SOFA-PR description, create the branches, compute the inputs of the build workflows + setup: + runs-on: launcher + if: ${{ github.repository_owner == 'sofa-framework' }} + outputs: + SOFA_BRANCH_NAME: ${{ steps.export-vars.outputs.SOFA_BRANCH_NAME }} + CI_DEPENDS_ON: ${{ steps.export-vars.outputs.CI_DEPENDS_ON }} + WITH_ALL_TESTS: ${{ steps.export-vars.outputs.WITH_ALL_TESTS }} + FORCE_FULL_BUILD: ${{ steps.export-vars.outputs.FORCE_FULL_BUILD }} + PR_OWNER_URL: ${{ steps.export-vars.outputs.PR_OWNER_URL }} + PR_BRANCH_NAME: ${{ steps.export-vars.outputs.PR_BRANCH_NAME }} + PR_COMMIT_SHA: ${{ steps.export-vars.outputs.PR_COMMIT_SHA }} + SH_BUILDER_OS: ${{ steps.export-vars.outputs.SH_BUILDER_OS }} + PIXI_BUILDER_OS: ${{ steps.export-vars.outputs.PIXI_BUILDER_OS }} + CI_UPDATE_REF: ${{ steps.export-vars.outputs.CI_UPDATE_REF }} + CI_REPO_URL: ${{ steps.ci-checkout.outputs.CI_REPO_URL }} + CI_BRANCH: ${{ steps.ci-checkout.outputs.CI_BRANCH }} + + steps: + # The ci plugin holds the scripts: honor its [ci-depends-on] if the SOFA-PR has one + - name: Check out ci + id: ci-checkout + shell: bash + env: + GH_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + PR_NUMBER: ${{ inputs.pr_number }} + run: | + ci_repo_url="https://github.com/sofa-framework/ci.git" + ci_branch="master" + ci_pr_url=$(gh api "repos/sofa-framework/sofa/pulls/${PR_NUMBER}" --jq .body \ + | grep -oE '\[ci-depends-on https://github.com/[^/]+/ci/pull/[0-9]+\]' | head -1 \ + | grep -oE 'https://github.com/[^]]+' || true) + if [ -n "$ci_pr_url" ]; then + api_path=$(echo "$ci_pr_url" | sed -E 's#https://github.com/([^/]+)/([^/]+)/pull/([0-9]+)#repos/\1/\2/pulls/\3#') + ci_repo_url=$(gh api "$api_path" --jq .head.repo.clone_url) + ci_branch=$(gh api "$api_path" --jq .head.ref) + fi + echo "Using ci from ${ci_repo_url}, branch ${ci_branch}" + rm -rf ./ci + git clone -b "$ci_branch" --single-branch "$ci_repo_url" ./ci + echo "CI_REPO_URL=${ci_repo_url}" >> $GITHUB_OUTPUT + echo "CI_BRANCH=${ci_branch}" >> $GITHUB_OUTPUT + + - name: Parse the SOFA-PR and create the branches + env: + GH_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + PR_NUMBER: ${{ inputs.pr_number }} + run: | + # Warning ! This script writes the build inputs in ENV variables + python3 ci/scripts/ci_update_ref_setup.py + + - name: Export environment variables as outputs + id: export-vars + run: | + # Validate branch format (e.g., v25.06) + if [[ ! "$SOFA_BRANCH_NAME" =~ ^v[0-9]{2}\.[0-9]{2}$ && "$SOFA_BRANCH_NAME" != "master" ]]; then + echo "Error: Invalid branch name format: $SOFA_BRANCH_NAME." + exit 1 + fi + + echo "SOFA_BRANCH_NAME=${SOFA_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "CI_DEPENDS_ON=${CI_DEPENDS_ON}" >> $GITHUB_OUTPUT + echo "WITH_ALL_TESTS=${WITH_ALL_TESTS}" >> $GITHUB_OUTPUT + echo "FORCE_FULL_BUILD=${FORCE_FULL_BUILD}" >> $GITHUB_OUTPUT + echo "PR_OWNER_URL=${PR_OWNER_URL}" >> $GITHUB_OUTPUT + echo "PR_BRANCH_NAME=${PR_BRANCH_NAME}" >> $GITHUB_OUTPUT + echo "PR_COMMIT_SHA=${PR_COMMIT_SHA}" >> $GITHUB_OUTPUT + echo "SH_BUILDER_OS=${SH_BUILDER_OS}" >> $GITHUB_OUTPUT + echo "PIXI_BUILDER_OS=${PIXI_BUILDER_OS}" >> $GITHUB_OUTPUT + echo "CI_UPDATE_REF=${CI_UPDATE_REF}" >> $GITHUB_OUTPUT + + # =============================================================== + + build-on: + needs: setup + if: ${{ needs.setup.outputs.SH_BUILDER_OS != '[]' }} + uses: ./.github/workflows/CI_build_and_test.yml + secrets: + SOFA_BOT_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + with: + sofa-branch-name: ${{ needs.setup.outputs.SOFA_BRANCH_NAME }} + sofa-commit-sha: HEAD + preset: full-dev + python-version: '3.12' + ci-depends-on: ${{ needs.setup.outputs.CI_DEPENDS_ON }} + with-all-tests: false + force-full-build: ${{ needs.setup.outputs.FORCE_FULL_BUILD == 'true' }} + generate-binaries: false + pr-owner-url: ${{ needs.setup.outputs.PR_OWNER_URL }} + pr-branch-name: ${{ needs.setup.outputs.PR_BRANCH_NAME }} + pr-commit-sha: ${{ needs.setup.outputs.PR_COMMIT_SHA }} + builder-os: ${{ needs.setup.outputs.SH_BUILDER_OS }} + ci-update-ref: ${{ needs.setup.outputs.CI_UPDATE_REF }} + + pixi-ci: + needs: setup + if: ${{ needs.setup.outputs.PIXI_BUILDER_OS != '[]' }} + uses: ./.github/workflows/CI_macos_linux_windows_pixi.yml + secrets: + SOFA_BOT_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + with: + sofa-branch-name: ${{ needs.setup.outputs.SOFA_BRANCH_NAME }} + sofa-commit-sha: HEAD + preset: full-dev + python-version: '3.12' + ci-depends-on: ${{ needs.setup.outputs.CI_DEPENDS_ON }} + with-all-tests: false + force-full-build: ${{ needs.setup.outputs.FORCE_FULL_BUILD == 'true' }} + generate-binaries: false + pr-owner-url: ${{ needs.setup.outputs.PR_OWNER_URL }} + pr-branch-name: ${{ needs.setup.outputs.PR_BRANCH_NAME }} + pr-commit-sha: ${{ needs.setup.outputs.PR_COMMIT_SHA }} + builder-os: ${{ needs.setup.outputs.PIXI_BUILDER_OS }} + ci-update-ref: ${{ needs.setup.outputs.CI_UPDATE_REF }} + + # =============================================================== + + # Runs exactly once, after every build job: REF-PRs, SOFA-PR description and the single summary comment + finalize: + needs: [setup, build-on, pixi-ci] + if: ${{ always() && needs.setup.result == 'success' }} + runs-on: launcher + steps: + - name: Check out ci + shell: bash + run: | + rm -rf ./ci + git clone -b "${{ needs.setup.outputs.CI_BRANCH }}" --single-branch "${{ needs.setup.outputs.CI_REPO_URL }}" ./ci + + - name: Open the REF-PRs, update the SOFA-PR and post the summary + env: + GH_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + PR_NUMBER: ${{ inputs.pr_number }} + CI_UPDATE_REF: ${{ needs.setup.outputs.CI_UPDATE_REF }} + GITHUB_RUN_ID: ${{ github.run_id }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + python3 ci/scripts/ci_update_ref_finish.py +# <<< CI-UPDATE-REF END diff --git a/.github/workflows/CI_macos_linux_windows_pixi.yml b/.github/workflows/CI_macos_linux_windows_pixi.yml index 17790c480c7..bb20c16da05 100644 --- a/.github/workflows/CI_macos_linux_windows_pixi.yml +++ b/.github/workflows/CI_macos_linux_windows_pixi.yml @@ -54,6 +54,16 @@ on: required: false default: 'NONE' description: 'Json scructure with three parameters {"COMMIT_HASH":"fullhsashofthecommit", }' +# >>> CI-UPDATE-REF BEGIN + ci-update-ref: + type: string + required: false + default: '' + description: 'When not empty, only build and generate the regression references (JSON given by CI_generate_regression_references)' + secrets: + SOFA_BOT_TOKEN: + required: false +# <<< CI-UPDATE-REF END jobs: display-inputs: @@ -98,6 +108,17 @@ jobs: - run: git config --global user.email "<>" - run: git config --global user.name "SOFA Bot" + # >>> CI-UPDATE-REF BEGIN + - name: Export bot token + if: ${{ inputs.ci-update-ref != '' }} + shell: bash + env: + BOT_TOKEN: ${{ secrets.SOFA_BOT_TOKEN }} + run: | + echo "::add-mask::$BOT_TOKEN" + echo "GH_TOKEN=$BOT_TOKEN" >> $GITHUB_ENV + # <<< CI-UPDATE-REF END + - name: Setup SOFA and clone CI id: clone shell: bash -el {0} @@ -296,7 +317,7 @@ jobs: - name: Unit tests id: unit-tests-step - if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false + if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: pwsh run: | Set-Location $env:SRC_DIR @@ -329,7 +350,7 @@ jobs: - name: Scene tests id: scene-tests-step - if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: pwsh run: | Set-Location $env:SRC_DIR @@ -363,7 +384,7 @@ jobs: - name: Regression tests id: regression-tests-step - if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true + if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.generate-binaries == false && inputs.with-all-tests == true && inputs.ci-update-ref == '' # CI-UPDATE-REF shell: pwsh run: | Set-Location $env:SRC_DIR @@ -397,6 +418,59 @@ jobs: if ($failures -ne '0') { exit 1 } + # >>> CI-UPDATE-REF BEGIN + - name: Update references - prepare + id: update-ref-prepare + if: always() && runner.os == 'Windows' && steps.build-step.outcome == 'success' && inputs.ci-update-ref != '' + shell: bash + env: + CI_UPDATE_REF: ${{ inputs.ci-update-ref }} + run: | + python3 ${CI_DIR}/scripts/ci_update_ref_build.py prepare + + - name: Update references - generate + id: update-ref-generate + if: always() && steps.update-ref-prepare.outcome == 'success' + shell: pwsh + run: | + Set-Location $env:SRC_DIR + + $FilterFile = Join-Path $env:BUILD_DIR "ci_update_ref\filter.txt" + $Filter = (Get-Content -Raw $FilterFile).Trim() + if ($Filter -eq '') { Write-Host "No scene to generate."; exit 0 } + + $RegResultsDir = Join-Path $env:BUILD_DIR "tests_results\regression" + + # Activate the environment in the terminal + pixi shell-hook --shell powershell -e ${{ inputs.preset }} | Out-String | Invoke-Expression + + python "$env:REGRESSION_DIR\SofaRegressionProgram\SofaRegressionProgram.py" ` + --input "$env:SRC_DIR/examples" ` + --input "$env:SRC_DIR\applications" ` + --input "$env:BUILD_DIR\external_directories\fetched" ` + --filter $Filter ` + --write-references ` + --output-logs-errors "$RegResultsDir" ` + -j $env:VM_MAX_PARALLEL_TESTS + + if (-not (Test-Path "$RegResultsDir\*summary*")) { exit 1 } + + foreach ($line in Get-Content "$RegResultsDir\summary.txt") { + $key, $value = $line -split '=', 2 + if ($key -eq 'crashes' -and $value -ne '0') { exit 1 } + } + + - name: Update references - commit and push + id: update-ref-commit + if: always() && steps.update-ref-generate.outcome == 'success' + shell: bash + env: + CI_UPDATE_REF: ${{ inputs.ci-update-ref }} + BUILD_COMMIT: ${{ (inputs.pr-commit-sha != '' && inputs.pr-commit-sha) || inputs.sofa-commit-sha }} + run: | + python3 ${CI_DIR}/scripts/ci_update_ref_build.py commit + # <<< CI-UPDATE-REF END + - name: Publish artifacts if: always() && steps.build-step.outcome == 'success' && inputs.generate-binaries == true uses: actions/upload-artifact@v4 @@ -445,6 +519,17 @@ jobs: echo "✅ Build OK." fi + # >>> CI-UPDATE-REF BEGIN + if [[ -n "${{ inputs.ci-update-ref }}" ]]; then + if [[ "${{ steps.update-ref-prepare.outcome }}" == "failure" || "${{ steps.update-ref-generate.outcome }}" == "failure" || "${{ steps.update-ref-commit.outcome }}" == "failure" ]]; then + echo "❌ References generation failed" + exit 1 + fi + echo "✅ References generated and pushed." + exit 0 + fi + # <<< CI-UPDATE-REF END + if [[ "${{ inputs.generate-binaries }}" == "false" ]]; then if [[ "${{ steps.unit-tests-step.outcome }}" == "failure" || "${{ steps.scene-tests-step.outcome }}" == "failure" || "${{ steps.regression-tests-step.outcome }}" == "failure" ]] && [[ -f "${{ env.SOFA_BUILD_DIR }}/tests_results/unit/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/scene/summary.txt" && -f "${{ env.SOFA_BUILD_DIR }}/tests_results/regression/summary.txt" ]]; then