diff --git a/.github/workflows/ci_unit_tests.yaml b/.github/workflows/ci_unit_tests.yaml
index 450d2546b8e..f5305dc7ca3 100644
--- a/.github/workflows/ci_unit_tests.yaml
+++ b/.github/workflows/ci_unit_tests.yaml
@@ -26,8 +26,7 @@ jobs:
sudo apt-get install -y perl libxml-libxml-perl libxml-libxslt-perl libdatetime-perl
python -m pip install --upgrade pip
python -m pip install -r global-workflow/dev/workflow/requirements.txt
- pip install pytest
- pip install wget
+ pip install pytest pytest-cov pyyaml jinja2 wget
- name: Cache Rocoto
id: cache-rocoto
@@ -60,7 +59,12 @@ jobs:
cd global-workflow/sorc
git submodule update --init -j 2 wxflow ufs_model.fd
./link_workflow.sh
- cd ../dev/ci/scripts/tests
+
+ # Create test data directory for unit tests
+ mkdir -p ../dev/ci/scripts/unittests/test_data
+ echo "Creating test directories and files for CI tests"
+
+ cd ../dev/ci/scripts/unittests
pytest -v --junitxml test-results.xml
@@ -68,6 +72,6 @@ jobs:
if: always()
uses: EnricoMi/publish-unit-test-result-action@v2
with:
- files: global-workflow/dev/ci/scripts/tests/test-results.xml
+ files: global-workflow/dev/ci/scripts/unittests/test-results.xml
job_summary: true
comment_mode: off
diff --git a/.github/workflows/pw_aws_ci.yaml b/.github/workflows/pw_aws_ci.yaml
index 490e52cb0fa..e7c05f3e296 100644
--- a/.github/workflows/pw_aws_ci.yaml
+++ b/.github/workflows/pw_aws_ci.yaml
@@ -157,7 +157,7 @@ jobs:
- name: Run Experiment ${{ matrix.case }}
run: |
cd ${{ env.TEST_DIR }}/HOMEgfs
- ./dev/ci/scripts/run-check_ci.sh ${{ env.TEST_DIR }} ${{ matrix.case }}.${{ github.run_id }} HOMEgfs
+ ./dev/ci/scripts/run_check_ci.sh ${{ env.TEST_DIR }} ${{ matrix.case }}.${{ github.run_id }} HOMEgfs
clean-up:
needs: run-experiments
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000000..a08bbd63ddb
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,80 @@
+# ==========================================================================
+# Main GitLab CI Configuration for the global-workflow Project
+# ==========================================================================
+#
+# This is the primary CI/CD configuration file that orchestrates the GitLab CI
+# testing framework for the global-workflow project. It defines pipeline stages
+# and includes specialized configuration files for different testing aspects.
+#
+# The pipeline architecture supports:
+# 1. Multiple host environments (hera, gaeac6, etc.)
+# 2. Two primary testing modes:
+# - PR validation via standard test cases
+# - CTest-based tests triggered via GitHub API
+#
+# Included files:
+# - .gitlab-ci-ctests.yml: Handles CMake/CTest-based testing framework
+# - .gitlab-ci-cases.yml: Defines templates for standard experiment cases
+# - .gitlab-ci-hosts.yml: Contains host-specific configurations and test matrices
+# that can be extended to support additional hosts
+#
+# The host configuration in .gitlab-ci-hosts.yml is designed to be easily
+# extended with new computing platforms and allows per-host specification
+# of which test cases to run.
+
+stages:
+ - build
+ - create_experiments
+ - run_tests
+ - finalize
+
+variables:
+ BUILD: '' # Placeholder for control over build directory path
+ # Using GW prefix to avoid confusion with GitLab predefined variables
+ GW_RUN_PATH: ${CI_BUILDS_DIR}/${BUILD}/${CI_COMMIT_SHORT_SHA}
+ # Overriding GitLab's predefined GIT_CLONE_PATH variable to specify the exact location
+ # where the repository should be cloned. This works with custom_build_dir-enabled in the runner config.
+ GIT_CLONE_PATH: '${GW_RUN_PATH}/global-workflow'
+ GW_HOMEgfs: ${GIT_CLONE_PATH}
+ RUNTESTS_DIR: ${GW_RUN_PATH}/RUNTESTS
+ GIT_DEPTH: 10
+ RUNNER_SCRIPT_TIMEOUT: 6h
+ RUNNER_AFTER_SCRIPT_TIMEOUT: 6h
+ # Controls pipeline behavior: CTests (true) or PR cases (false)
+ GITHUB_API_TRIGGER: ${GITHUB_API_TRIGGER:-"false"}
+ PR_NUMBER: ${PR_NUMBER:-"0"}
+
+# Include specialized pipeline configuration files
+include:
+ - local: 'dev/ci/gitlab-ci-ctests.yml' # CTest framework configuration
+ - local: 'dev/ci/gitlab-ci-cases.yml' # Standard test case templates
+ - local: 'dev/ci/gitlab-ci-hosts.yml' # Host-specific configurations
+
+.base_config:
+ variables:
+ GIT_STRATEGY: none
+
+# Common build template for all modalities
+.build_template:
+ variables:
+ GIT_STRATEGY: clone
+ GIT_SUBMODULE_STRATEGY: recursive
+ GIT_SSL_NO_VERIFY: "true" # Address potential certificate verification issues
+ stage: build
+ script:
+ - |
+ set -e # Fail the job if any command fails
+ echo "Setting up build environment for ${machine}"
+ echo "Using build directory ${GW_HOMEgfs}"
+ git submodule status
+
+ dev/ci/scripts/utils/ci_utils.sh build
+ build_status=$?
+
+ if [ $build_status -ne 0 ]; then
+ echo "Build failed with exit code $build_status"
+ exit $build_status
+ fi
+
+ sorc/link_workflow.sh
+ mkdir -p ${RUNTESTS_DIR}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5044689f7e2..6470de8ca7b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,4 +24,4 @@ endif()
# add_subdirectory(sorc)
# Setup tests
-add_subdirectory(ctests)
+add_subdirectory(dev/ctests)
diff --git a/dev/ci/.gitlab-ci.yml b/dev/ci/.gitlab-ci.yml
deleted file mode 100644
index 810334c0ace..00000000000
--- a/dev/ci/.gitlab-ci.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-stages:
- - build
- - create_experiments
- - run_tests
-
-# Global variables
-variables:
- BUILD: 'TODAY'
- GIT_CLONE_PATH: '${CI_BUILDS_DIR}/${BUILD}/global-workflow'
- HOMEGFS: ${GIT_CLONE_PATH}
- RUNTESTS_DIR: ${CI_BUILDS_DIR}/${BUILD}/RUNTESTS
- GIT_DEPTH: 1
- RUNNER_SCRIPT_TIMEOUT: 6h
- RUNNER_AFTER_SCRIPT_TIMEOUT: 6h
-
-
-# Build stage for the global workflow on compute nodes
-build:
- variables:
- GIT_STRATEGY: clone
- GIT_SUBMODULE_STRATEGY: recursive
- stage: build
- script:
- - echo "Using build directory ${HOMEGFS} (dated ${BUILD_DATE})"
- - dev/ci/scripts/utils/ci_utils_wrapper.sh build_compute
- - sorc/link_workflow.sh
- - mkdir -p ${RUNTESTS_DIR}
- # TODO - Add more machines to the list and make (next PR)
- #parallel:
- # matrix:
- # - MACHINE: ["gaeac6"]
- tags:
- - gaeac6
-
-
-# Create experiments stage from a fixed list of cases in $HOMEGFS/dev/ci/cases/pr
-# TODO: Next PR has simi-dynamic caseName lists
-setup_experiments:
- variables:
- GIT_STRATEGY: none
- stage: create_experiments
- script:
- - export RUNTESTS=${RUNTESTS_DIR}
- - ${HOMEGFS}/dev/ci/scripts/utils/ci_utils_wrapper.sh create_experiment ${HOMEGFS}/dev/ci/cases/pr/${caseName}.yaml
- parallel:
- matrix:
- - caseName: ["C48_ATM", "C48mx500_3DVarAOWCDA", "C48mx500_hybAOWCDA", "C48_S2SWA_gefs", "C48_S2SW", "C96_atm3DVar", "C96C48_hybatmDA", "C96C48_hybatmaerosnowDA"]
- tags:
- - gaeac6
- dependencies:
- - build
-
-# Running the list of experiments created in the previous stage
-# using the run-check_ci.sh script from $HOMEgfs/dev/ci/scripts directory
-run_tests:
- variables:
- GIT_STRATEGY: none
- stage: run_tests
- script:
- - echo "Using build directory ${HOMEGFS} (dated ${BUILD_DATE})"
- - pslot=$(${HOMEGFS}/dev/ci/scripts/utils/ci_utils_wrapper.sh get_pslot ${RUNTESTS_DIR} ${caseName})
- - ${HOMEGFS}/dev/ci/scripts/run-check_ci.sh ${CI_BUILDS_DIR}/${BUILD} ${pslot} global-workflow
- parallel:
- matrix:
- - caseName: ["C48_ATM", "C48mx500_3DVarAOWCDA", "C48mx500_hybAOWCDA", "C48_S2SWA_gefs", "C48_S2SW", "C96_atm3DVar", "C96C48_hybatmDA", "C96C48_hybatmaerosnowDA"]
- tags:
- - gaeac6
- dependencies:
- - setup_experiments
diff --git a/dev/ci/Jenkinsfile b/dev/ci/Jenkinsfile
index 3d31079e110..5bd5f555f02 100644
--- a/dev/ci/Jenkinsfile
+++ b/dev/ci/Jenkinsfile
@@ -129,7 +129,7 @@ pipeline {
def error_logs_message = ""
dir("${HOMEgfs}/sorc") {
try {
- sh(script: "${HOMEgfs_dev}/ci/scripts/utils/ci_utils_wrapper.sh build_compute") // build the global-workflow executables
+ sh(script: "${HOMEgfs_dev}/ci/scripts/utils/ci_utils.sh build") // build the global-workflow executables
} catch (Exception error_build) {
echo "Failed to build global-workflow: ${error_build.getMessage()}"
if ( fileExists("logs/error.logs") ) {
@@ -203,7 +203,7 @@ pipeline {
try {
error_output = sh(script: """
source ${HOMEgfs_dev}/ush/gw_setup.sh
- ${HOMEgfs_dev}/ci/scripts/utils/ci_utils_wrapper.sh create_experiment ${HOMEgfs_dev}/ci/cases/pr/${caseName}.yaml
+ ${HOMEgfs_dev}/ci/scripts/utils/ci_utils.sh create_experiment ${HOMEgfs_dev}/ci/cases/pr/${caseName}.yaml
""", returnStdout: true).trim()
} catch (Exception error_create) {
sh(script: """${GH} pr comment ${env.CHANGE_ID} --repo ${repo_url} --body '${caseName} **FAILED** to create experiment on ${Machine} in BUILD# ${env.BUILD_NUMBER}\n with the error:\n```\n${error_output}```' """)
@@ -215,22 +215,22 @@ pipeline {
stage("Running ${caseName}") {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
script {
- def pslot = sh(script: "${HOMEgfs_dev}/ci/scripts/utils/ci_utils_wrapper.sh get_pslot ${CUSTOM_WORKSPACE}/RUNTESTS ${caseName}", returnStdout: true).trim()
+ def pslot = sh(script: "${HOMEgfs_dev}/ci/scripts/utils/ci_utils.sh get_pslot ${CUSTOM_WORKSPACE}/RUNTESTS ${caseName}", returnStdout: true).trim()
def error_file = "${CUSTOM_WORKSPACE}/RUNTESTS/${pslot}_error.logs"
sh(script: " rm -f ${error_file}")
try {
sh(script: """
source ${HOMEgfs_dev}/ush/gw_setup.sh
- ${HOMEgfs_dev}/ci/scripts/run-check_ci.sh ${CUSTOM_WORKSPACE} ${pslot} 'global-workflow'
+ ${HOMEgfs_dev}/ci/scripts/run_check_ci.sh ${CUSTOM_WORKSPACE} ${pslot} 'global-workflow'
""")
sh(script: """
source ${HOMEgfs_dev}/ush/gw_setup.sh
- ${HOMEgfs_dev}/ci/scripts/utils/ci_utils_wrapper.sh cleanup_experiment ${CUSTOM_WORKSPACE}/RUNTESTS/EXPDIR/${pslot}
+ ${HOMEgfs_dev}/ci/scripts/utils/ci_utils.sh cleanup_experiment ${CUSTOM_WORKSPACE}/RUNTESTS/EXPDIR/${pslot}
""")
} catch (Exception error_experment) {
sh(script: """
source ${HOMEgfs_dev}/ush/gw_setup.sh
- ${HOMEgfs_dev}/ci/scripts/utils/ci_utils_wrapper.sh cancel_batch_jobs ${pslot}
+ ${HOMEgfs_dev}/ci/scripts/utils/ci_utils.sh cancel_batch_jobs ${pslot}
""")
ws(CUSTOM_WORKSPACE) {
def error_logs = ""
@@ -300,9 +300,9 @@ pipeline {
done
""", returnStatus: true)
sh(script: """${GH} pr edit ${env.CHANGE_ID} --repo ${repo_url} --add-label "CI-${Machine}-${STATUS}" """, returnStatus: true)
- if (fileExists("${CUSTOM_WORKSPACE}/RUNTESTS/ci-run_check.log")) {
- sh(script: """echo "**CI ${STATUS}** on ${Machine} in Build# ${env.BUILD_NUMBER}
Built and ran in directory \\`${CUSTOM_WORKSPACE}\\`\n\\`\\`\\`\n" | cat - ${CUSTOM_WORKSPACE}/RUNTESTS/ci-run_check.log > temp && mv temp ${CUSTOM_WORKSPACE}/RUNTESTS/ci-run_check.log""", returnStatus: true)
- sh(script: """${GH} pr comment ${env.CHANGE_ID} --repo ${repo_url} --body-file ${CUSTOM_WORKSPACE}/RUNTESTS/ci-run_check.log """, returnStatus: true)
+ if (fileExists("${CUSTOM_WORKSPACE}/RUNTESTS/run_check_ci.log")) {
+ sh(script: """echo "**CI ${STATUS}** on ${Machine} in Build# ${env.BUILD_NUMBER}
Built and ran in directory \\`${CUSTOM_WORKSPACE}\\`\n\\`\\`\\`\n" | cat - ${CUSTOM_WORKSPACE}/RUNTESTS/run_check_ci.log > temp && mv temp ${CUSTOM_WORKSPACE}/RUNTESTS/run_check_ci.log""", returnStatus: true)
+ sh(script: """${GH} pr comment ${env.CHANGE_ID} --repo ${repo_url} --body-file ${CUSTOM_WORKSPACE}/RUNTESTS/run_check_ci.log """, returnStatus: true)
}
if (STATUS == 'Passed') {
try {
diff --git a/dev/ci/Jenkinsfile4AWS b/dev/ci/Jenkinsfile4AWS
index 899558087cb..d5b31065f24 100644
--- a/dev/ci/Jenkinsfile4AWS
+++ b/dev/ci/Jenkinsfile4AWS
@@ -134,7 +134,7 @@ pipeline {
def error_logs_message = ""
dir("${HOMEgfs}/sorc") {
try {
- // sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh build_compute") // build the global-workflow executables
+ // sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils.sh build") // build the global-workflow executables
sh(script: './build_compute.sh -A ${USER} gfs gefs sfs') // build the global-workflow executables
} catch (Exception error_build) {
echo "Failed to build global-workflow: ${error_build.getMessage()}"
@@ -205,10 +205,10 @@ pipeline {
script {
env.RUNTESTS = "${CUSTOM_WORKSPACE}/RUNTESTS"
try {
- error_output = sh(script: """
+ error_output = sh(script: \"\"\"
source ${HOMEgfs}/workflow/gw_setup.sh
- ${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh create_experiment ${HOMEgfs}/ci/cases/pr/${caseName}.yaml
- """, returnStdout: true).trim()
+ ${HOMEgfs}/ci/scripts/utils/ci_utils.sh create_experiment ${HOMEgfs}/ci/cases/pr/${caseName}.yaml
+ \"\"\", returnStdout: true).trim()
} catch (Exception error_create) {
sh(script: """${GH} pr comment ${env.CHANGE_ID} --repo ${repo_url} --body "${Case} **FAILED** to create experiment on ${Machine} in BUILD# ${env.BUILD_NUMBER}\n with the error:\n\\`\\`\\`\n${error_output}\\`\\`\\`" """)
error("Case ${caseName} failed to create experiment directory")
@@ -219,22 +219,22 @@ pipeline {
stage("Running ${caseName}") {
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
script {
- def pslot = sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh get_pslot ${CUSTOM_WORKSPACE}/RUNTESTS ${caseName}", returnStdout: true).trim()
+ def pslot = sh(script: "${HOMEgfs}/ci/scripts/utils/ci_utils.sh get_pslot ${CUSTOM_WORKSPACE}/RUNTESTS ${caseName}", returnStdout: true).trim()
def error_file = "${CUSTOM_WORKSPACE}/RUNTESTS/${pslot}_error.logs"
sh(script: " rm -f ${error_file}")
try {
sh(script: """
source ${HOMEgfs}/workflow/gw_setup.sh
- ${HOMEgfs}/ci/scripts/run-check_ci.sh ${CUSTOM_WORKSPACE} ${pslot} 'global-workflow'
+ ${HOMEgfs}/ci/scripts/run_check_ci.sh ${CUSTOM_WORKSPACE} ${pslot} 'global-workflow'
""")
sh(script: """
source ${HOMEgfs}/workflow/gw_setup.sh
- ${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh cleanup_experiment ${CUSTOM_WORKSPACE}/RUNTESTS/EXPDIR/${pslot}
+ ${HOMEgfs}/ci/scripts/utils/ci_utils.sh cleanup_experiment ${CUSTOM_WORKSPACE}/RUNTESTS/EXPDIR/${pslot}
""")
} catch (Exception error_experment) {
sh(script: """
source ${HOMEgfs}/workflow/gw_setup.sh
- ${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh cancel_batch_jobs ${pslot}
+ ${HOMEgfs}/ci/scripts/utils/ci_utils.sh cancel_batch_jobs ${pslot}
""")
ws(CUSTOM_WORKSPACE) {
def error_logs = ""
diff --git a/dev/ci/gitlab-ci-cases.yml b/dev/ci/gitlab-ci-cases.yml
new file mode 100644
index 00000000000..773960871f4
--- /dev/null
+++ b/dev/ci/gitlab-ci-cases.yml
@@ -0,0 +1,42 @@
+# ==========================================================================
+# Templates for Standard Test Cases
+# ==========================================================================
+#
+# This file defines templates for setting up and running standard experiment
+# test cases for the global-workflow project. These cases represent more
+# comprehensive, end-to-end testing scenarios than the CTesting framework.
+#
+# These templates are used primarily for PR testing when GITHUB_API_TRIGGER=false.
+# The actual test case matrix for each host is defined in .gitlab-ci-hosts.yml.
+#
+# Key templates:
+# - .setup_template: Creates experiment directories based on case YAML files
+# - .run_tests_template: Executes and validates the experiment workflows
+#
+# Test cases are defined as YAML files in the dev/ci/cases/pr directory and
+# are referenced by name in the host configuration file's test matrix.
+
+# Template for experiment setup jobs
+.setup_template:
+ extends: .base_config
+ stage: create_experiments
+ script:
+ - |
+ export RUNTESTS=${RUNTESTS_DIR}
+ ${GW_HOMEgfs}/dev/ci/scripts/utils/ci_utils.sh create_experiment ${GW_HOMEgfs}/dev/ci/cases/pr/${caseName}.yaml
+ exit $?
+ needs:
+ - build-${machine}
+
+# Template for test execution jobs
+.run_tests_template:
+ extends: .base_config
+ stage: run_tests
+ script:
+ - |
+ echo "Using build directory $GW_HOMEgfs (dated $BUILD_DATE)"
+ pslot=$(${GW_HOMEgfs}/dev/ci/scripts/utils/ci_utils.sh get_pslot ${RUNTESTS_DIR} ${caseName})
+ ${GW_HOMEgfs}/dev/ci/scripts/run_check_ci.sh ${GW_RUN_PATH} ${pslot} global-workflow
+ exit $?
+ needs:
+ - setup_experiments-${machine}
diff --git a/dev/ci/gitlab-ci-ctests.yml b/dev/ci/gitlab-ci-ctests.yml
new file mode 100644
index 00000000000..38a17e3cfab
--- /dev/null
+++ b/dev/ci/gitlab-ci-ctests.yml
@@ -0,0 +1,53 @@
+# ==========================================================================
+# Templates and Configuration for CTest-Based Testing
+# ==========================================================================
+#
+# This file defines jobs and templates for running CMake/CTest-based fuctional
+# tests in the global-workflow project. The CTests are designed to
+# test specific Rocoto Jobs singluarly with predefined input data.
+#
+# CTests are primarily triggered via GitHub API for quick validation of PRs,
+# whereas the full experiment cases are used for more extensive testing.
+#
+# Key components:
+# - .create_ctests: Template for setting up the CMake/CTest environment
+# - .run_ctests_template: Template for executing specific CTest labels
+#
+# CTests are defined in the dev/ctests directory and are categorized by labels,
+# which are referenced in the hosts file to determine which tests to run on
+# which platforms.
+
+# =======================================
+# Templates for CTests
+# =======================================
+
+# Setup job for ctests using CMake
+.create_ctests:
+ extends: .base_config
+ stage: create_experiments
+ script: |
+ set -e # Fail the job if any command fails
+ echo "Setting up for ctests workflow"
+ source ${GW_HOMEgfs}/dev/ci/platforms/config.${machine}
+ cd ${GW_HOMEgfs}/dev/ctests
+ mkdir -p build
+ cd build
+ cmake -S ${GW_HOMEgfs}
+ ctest -N
+ num=$(ctest -N | grep "Total Tests" | awk "{print \$3}")
+ [ "$num" -gt 0 ] || { echo "No tests found"; exit 1; }
+ echo "CTests have been created and configured successfully"
+ needs:
+ - build-${machine}
+
+# Main template for CTest execution jobs
+.run_ctests_template:
+ extends: .base_config
+ stage: run_tests
+ script: |
+ set -e # Fail the job if any command fails
+ echo "Running ${CTEST} tests in ${GW_HOMEgfs}/dev/ctests"
+ cd ${GW_HOMEgfs}/dev/ctests/build
+ ctest -L ${CTEST} --output-on-failure
+ needs:
+ - create_ctests-${machine}
diff --git a/dev/ci/gitlab-ci-hosts.yml b/dev/ci/gitlab-ci-hosts.yml
new file mode 100644
index 00000000000..dc4f23c4d57
--- /dev/null
+++ b/dev/ci/gitlab-ci-hosts.yml
@@ -0,0 +1,154 @@
+# ==========================================================================
+# Host-Specific Configurations for the global-workflow CI Pipeline
+# ==========================================================================
+#
+# This file defines host-specific job configurations for different computing
+# platforms supported by the global-workflow testing pipeline. It's designed
+# to be easily extendable with new computing platforms.
+#
+# Key features:
+# - Per-host test case matrices that define which tests run on which hosts
+# - Includes two sections supporting the standard PR Cases and CTests
+# - Host-specific tags and variables for job routing to the correct runners
+# - Conditional rules based on trigger type (PR vs API)
+#
+# The GITHUB_API_TRIGGER variable (set in the main .gitlab-ci.yml) controls
+# whether to run CTests (true) or standard cases (false).
+
+# =======================================
+# Standard Cases configurations by host
+# =======================================
+
+# Host: Hera - Standard Cases
+setup_experiments-hera:
+ extends: .setup_template
+ variables:
+ machine: hera
+ tags:
+ - hera
+ parallel:
+ matrix:
+ - caseName: ["C48mx500_hybAOWCDA", "C96C48_hybatmDA", "C48_S2SWA_gefs", "C96C48_hybatmaerosnowDA", "C48_S2SW", "C96_atm3DVar", "C48_ATM", "C96mx100_S2S", "C48mx500_3DVarAOWCDA"]
+ needs:
+ - build-hera
+ rules:
+ - if: $GITHUB_API_TRIGGER != "true"
+
+run_tests-hera:
+ extends: .run_tests_template
+ variables:
+ machine: hera
+ tags:
+ - hera
+ parallel:
+ matrix:
+ - caseName: ["C48mx500_hybAOWCDA", "C96C48_hybatmDA", "C48_S2SWA_gefs", "C96C48_hybatmaerosnowDA", "C48_S2SW", "C96_atm3DVar", "C48_ATM", "C96mx100_S2S", "C48mx500_3DVarAOWCDA"]
+ needs:
+ - setup_experiments-hera
+ rules:
+ - if: $GITHUB_API_TRIGGER != "true"
+
+# Host: GAEAC6 - Standard Cases
+setup_experiments-gaeac6:
+ extends: .setup_template
+ variables:
+ machine: gaeac6
+ tags:
+ - gaeac6
+ parallel:
+ matrix:
+ - caseName: ["C48mx500_hybAOWCDA", "C96C48_hybatmDA", "C48_S2SWA_gefs", "C96C48_hybatmaerosnowDA", "C48_S2SW", "C96_atm3DVar", "C48_ATM", "C48mx500_3DVarAOWCDA"]
+ needs:
+ - build-gaeac6
+ rules:
+ - if: $GITHUB_API_TRIGGER != "true"
+
+run_tests-gaeac6:
+ extends: .run_tests_template
+ variables:
+ machine: gaeac6
+ tags:
+ - gaeac6
+ parallel:
+ matrix:
+ - caseName: ["C48mx500_hybAOWCDA", "C96C48_hybatmDA", "C48_S2SWA_gefs", "C96C48_hybatmaerosnowDA", "C48_S2SW", "C96_atm3DVar", "C48_ATM", "C48mx500_3DVarAOWCDA"]
+ needs:
+ - setup_experiments-gaeac6
+ rules:
+ - if: $GITHUB_API_TRIGGER != "true"
+
+# =======================================
+# CTests configurations by host
+# =======================================
+
+# Template for CTest jobs that will be used across machines
+.ctests_cases_template:
+ extends: .run_ctests_template
+ stage: run_tests
+ parallel:
+ matrix:
+ - CTEST: ['C48_ATM_gfs_fcst_seg0', 'C48_S2SW_gfs_fcst_seg0', 'C48_S2SW_gfs_atmos_prod_f000-f003']
+
+# Host-specific CTest setup jobs
+create_ctests-hera:
+ extends: .create_ctests
+ stage: create_experiments
+ tags:
+ - hera
+ variables:
+ machine: hera
+ needs:
+ - build-hera
+ rules:
+ - if: $GITHUB_API_TRIGGER == "true"
+
+create_ctests-gaeac6:
+ extends: .create_ctests
+ stage: create_experiments
+ tags:
+ - gaeac6
+ variables:
+ machine: gaeac6
+ needs:
+ - build-gaeac6
+ rules:
+ - if: $GITHUB_API_TRIGGER == "true"
+
+# Host: Hera - CTests
+run_ctests-hera:
+ extends: .ctests_cases_template
+ tags:
+ - hera
+ needs:
+ - create_ctests-hera
+ rules:
+ - if: $GITHUB_API_TRIGGER == "true"
+
+# Host: GAEAC6 - CTests
+run_ctests-gaeac6:
+ extends: .ctests_cases_template
+ tags:
+ - gaeac6
+ needs:
+ - create_ctests-gaeac6
+ rules:
+ - if: $GITHUB_API_TRIGGER == "true"
+
+# =======================================
+# Common build configurations by host
+# These will always be included, regardless of modality
+# =======================================
+
+build-hera:
+ extends: .build_template
+ variables:
+ machine: hera
+ tags:
+ - hera
+
+build-gaeac6:
+ extends: .build_template
+ variables:
+ machine: gaeac6
+ tags:
+ - gaeac6
diff --git a/dev/ci/platforms/config.gaeac6 b/dev/ci/platforms/config.gaeac6
index f32c7e5627e..08883e36782 100644
--- a/dev/ci/platforms/config.gaeac6
+++ b/dev/ci/platforms/config.gaeac6
@@ -41,7 +41,7 @@ export GITLAB_RUNNER_NAME="RDHPCS Gaea C6"
# Directory for GitLab builds
# Used in launch_gitlab_runner.sh for location of builds
-export GITLAB_CI_BUILDS_DIR=/gpfs/f6/drsa-precip3/world-shared/global/CI/GITLAB
+export GITLAB_BUILDS_DIR=/gpfs/f6/drsa-precip3/world-shared/global/CI/GITLAB
# Directory for GitLab runner used by launch_gitlab_runner.sh
export GITLAB_RUNNER_DIR="${GFS_CI_ROOT}/GitLab/Runner"
diff --git a/dev/ci/platforms/config.hera b/dev/ci/platforms/config.hera
index 1b589cf943d..c457f875b15 100644
--- a/dev/ci/platforms/config.hera
+++ b/dev/ci/platforms/config.hera
@@ -41,19 +41,22 @@ export GITLAB_URL=https://vlab.noaa.gov/gitlab-licensed
export GITLAB_RUNNER_NAME="RDHPCS Hera"
# Directory for GitLab builds
-# Used in launch_gitlab_runner.sh for location of builds
-export GITLAB_CI_BUILDS_DIR=/scratch1/NCEPDEV/global/glopara/GFS_CI_CD/CI_WORKSPACES/CI_GITLAB
+# Used in launch_gitlab_runner.sh for the --builds-dir parameter
+# This works with --custom_build_dir-enabled=true to allow GIT_CLONE_PATH override
+# from .gitlab-ci.yml to specify exact clone locations within this directory
+export GITLAB_BUILDS_DIR=${GFS_CI_ROOT}/BUILDS/GITLAB
# Directory for GitLab runner used by launch_gitlab_runner.sh
+# This is where runner state/config files are stored (--working-directory parameter)
export GITLAB_RUNNER_DIR="${GFS_CI_ROOT}/GitLab/Runner"
# CTest functional test directories for pre stagged input data
-export STAGED_TESTS_DIR=${GFS_CI_ROOT}/STAGED_TESTS_DIR
+export CTESTS_STAGED_TESTS_DIR=${GFS_CI_ROOT}/STAGED_TESTS_DIR
#########################################################################
# CI CRON system configuration
#########################################################################
-export GFS_BASH_CI_ROOT=${GFS_CI_ROOT}/GFS_BASH_CI
+export GW_BASH_CI_ROOT=${GFS_CI_ROOT}/GFS_BASH_CI
export max_concurrent_cases=5
export max_concurrent_pr=4
diff --git a/dev/ci/scripts/check_ci.sh b/dev/ci/scripts/check_ci.sh
deleted file mode 100755
index b1a8fa05d9b..00000000000
--- a/dev/ci/scripts/check_ci.sh
+++ /dev/null
@@ -1,180 +0,0 @@
-#!/bin/bash
-set -eux
-#####################################################################################
-#
-# Script description: BASH script for checking for cases in a given PR and
-# running rocotostat on each to determine if the experiment has
-# succeeded or faild. This script is intended
-# to run from within a cron job in the CI Managers account
-#####################################################################################
-
-HOMEgfs="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." >/dev/null 2>&1 && pwd )"
-scriptname=$(basename "${BASH_SOURCE[0]}")
-echo "Begin ${scriptname} at $(date -u)" || true
-export PS4='+ $(basename ${BASH_SOURCE})[${LINENO}]'
-
-REPO_URL=${REPO_URL:-"git@github.com:NOAA-EMC/global-workflow.git"}
-
-#########################################################################
-# Set up runtime environment varibles for accounts on supproted machines
-#########################################################################
-
-source "${HOMEgfs}/ush/detect_machine.sh"
-case ${MACHINE_ID} in
- hera | orion | hercules | wcoss2 | gaea)
- echo "Running Automated Testing on ${MACHINE_ID}"
- source "${HOMEgfs}/ci/platforms/config.${MACHINE_ID}"
- ;;
- *)
- echo "Unsupported platform. Exiting with error."
- exit 1
- ;;
-esac
-set +x
-export HOMEgfs
-source "${HOMEgfs}/ush/module-setup.sh"
-source "${HOMEgfs}/ci/scripts/utils/ci_utils.sh"
-module use "${HOMEgfs}/modulefiles"
-module load "module_gwsetup.${MACHINE_ID}"
-module list
-# Load machine specific modules for ci (only wcoss2 is current)
-if [[ "${MACHINE_ID}" == "wcoss2" ]]; then
- module load "module_gwci.${MACHINE_ID}"
-fi
-set -x
-if ! command -v gh > /dev/null; then
- GH="${HOME}/bin/gh"
-else
- GH=$(command -v gh)
-fi
-export GH
-
-rocotostat=$(command -v rocotostat)
-if [[ -z ${rocotostat} ]]; then
- echo "rocotostat not found on system"
- exit 1
-else
- echo "rocotostat being used from ${rocotostat}"
-fi
-rocotocheck=$(command -v rocotocheck)
-if [[ -z ${rocotocheck} ]]; then
- echo "rocotocheck not found on system"
- exit 1
-else
- echo "rocotocheck being used from ${rocotocheck}"
-fi
-
-pr_list_dbfile="${GFS_BASH_CI_ROOT}/open_pr_list.db"
-
-pr_list=""
-if [[ -f "${pr_list_dbfile}" ]]; then
- pr_list=$("${HOMEgfs}/ci/scripts/utils/pr_list_database.py" --dbfile "${pr_list_dbfile}" --list Open Running) || true
-fi
-if [[ -z "${pr_list}" ]]; then
- echo "no PRs open and ready to run cases on .. exiting"
- exit 0
-fi
-
-#############################################################
-# Loop throu all PRs in PR List and look for expirments in
-# the RUNTESTS dir and for each one run runcotorun on them
-#############################################################
-
-for pr in ${pr_list}; do
- id=$("${GH}" pr view "${pr}" --repo "${REPO_URL}" --json id --jq '.id')
- output_ci="${GFS_BASH_CI_ROOT}/PR/${pr}/output_runtime_${id}"
- output_ci_single="${GFS_BASH_CI_ROOT}/PR/${pr}/output_runtime_single.log"
- echo "Processing Pull Request #${pr} and looking for cases"
- pr_dir="${GFS_BASH_CI_ROOT}/PR/${pr}"
-
- # If there is no RUNTESTS dir for this PR then cases have not been made yet
- if [[ ! -d "${pr_dir}/RUNTESTS" ]]; then
- continue
- fi
-
- #Check for PR success when ${pr_dir}/RUNTESTS/EXPDIR is void of subfolders
- # since all successfull ones where previously removed
- # shellcheck disable=SC2312
- if [[ -z $(ls -A "${pr_dir}/RUNTESTS/EXPDIR") ]] ; then
- "${GH}" pr edit --repo "${REPO_URL}" "${pr}" --remove-label "CI-${MACHINE_ID^}-Running" --add-label "CI-${MACHINE_ID^}-Passed"
- sed -i "1 i\`\`\`" "${output_ci}"
- sed -i "1 i\All CI Test Cases Passed on ${MACHINE_ID^}:" "${output_ci}"
- "${GH}" pr comment "${pr}" --repo "${REPO_URL}" --body-file "${output_ci}"
- "${HOMEgfs}/ci/scripts/utils/pr_list_database.py" --remove_pr "${pr}" --dbfile "${pr_list_dbfile}"
- # Check to see if this PR that was opened by the weekly tests and if so close it if it passed on all platforms
- weekly_labels=$(${GH} pr view "${pr}" --repo "${REPO_URL}" --json headRefName,labels,author --jq 'select(.author.login | contains("emcbot")) | select(.headRefName | contains("weekly_ci")) | .labels[].name ') || true
- if [[ -n "${weekly_labels}" ]]; then
- num_platforms=$(find "${HOMEgfs}/ci/platforms" -type f -name "config.*" | wc -l)
- passed=0
- for platforms in "${HOMEgfs}"/ci/platforms/config.*; do
- machine=$(basename "${platforms}" | cut -d. -f2)
- if [[ "${weekly_labels}" == *"CI-${machine^}-Passed"* ]]; then
- ((passed=passed+1))
- fi
- done
- if [[ "${passed}" == "${num_platforms}" ]]; then
- "${GH}" pr close --repo "${REPO_URL}" "${pr}"
- fi
- fi
- # Completely remove the PR and its cloned repo on sucess
- # of all cases on this platform
- rm -Rf "${pr_dir}"
- continue
- fi
-
- for pslot_dir in "${pr_dir}/RUNTESTS/EXPDIR/"*; do
- pslot=$(basename "${pslot_dir}") || true
- if [[ -z "${pslot}" ]]; then
- echo "No experiments found in ${pslot_dir} .. exiting"
- exit 0
- fi
- xml="${pslot_dir}/${pslot}.xml"
- db="${pslot_dir}/${pslot}.db"
- if [[ ! -f "${db}" ]]; then
- continue
- fi
-
- set +e
- rocoto_state="$("${HOMEgfs}/ci/scripts/utils/rocotostat.py" -w "${xml}" -d "${db}")"
- rocoto_error=$?
- rm -f "${output_ci_single}"
- if [[ "${rocoto_error}" -ne 0 ]]; then
- "${GH}" pr edit --repo "${REPO_URL}" "${pr}" --remove-label "CI-${MACHINE_ID^}-Running" --add-label "CI-${MACHINE_ID^}-Failed"
- if [[ "${rocoto_state}" == "STALLED" ]]; then
- # shellcheck disable=SC2312
- "${GH}" pr comment "${pr}" --repo "${REPO_URL}" --body "Experiment ${pslot} **${rocoto_state}** on ${MACHINE_ID^} at $(date +'%D %r')"
- "${HOMEgfs}/ci/scripts/utils/pr_list_database.py" --remove_pr "${pr}" --dbfile "${pr_list_dbfile}"
- cancel_all_batch_jobs "${pr_dir}/RUNTESTS"
- exit "${rocoto_error}"
- fi
- error_logs=$("${rocotostat}" -d "${db}" -w "${xml}" | grep -E 'FAIL|DEAD' | awk '{print "-c", $1, "-t", $2}' | xargs "${rocotocheck}" -d "${db}" -w "${xml}" | grep join | awk '{print $2}') || true
- # shellcheck disable=SC2086
- ${HOMEgfs}/ci/scripts/utils/publish_logs.py --file ${error_logs} --repo "PR_${pr}" > /dev/null
- # shellcheck disable=SC2086
- gist_url="$("${HOMEgfs}/ci/scripts/utils/publish_logs.py" --file ${error_logs} --gist "PR_${pr}")"
- {
- echo "Experiment ${pslot} **${rocoto_state}** on ${MACHINE_ID^} at $(date +'%D %r')" || true
- echo ""
- echo "Error logs:"
- echo "\`\`\`"
- echo "${error_logs}"
- echo "\`\`\`"
- echo "Follow link here to view the contents of the above file(s): [(link)](${gist_url})"
- } >> "${output_ci_single}"
- "${GH}" pr comment "${pr}" --repo "${REPO_URL}" --body-file "${output_ci_single}"
- "${HOMEgfs}/ci/scripts/utils/pr_list_database.py" --remove_pr "${pr}" --dbfile "${pr_list_dbfile}"
- cancel_all_batch_jobs "${pr_dir}/RUNTESTS"
- exit "${rocoto_error}"
- fi
- if [[ "${rocoto_state}" == "DONE" ]]; then
- #Remove Experment cases that completed successfully
- "${HOMEgfs}/ci/scripts/utils/ci_utils_wrapper.sh" cleanup_experiment "${pslot_dir}"
- rm -f "${output_ci_single}"
- # echo "\`\`\`" > "${output_ci_single}"
- DATE=$(date +'%D %r')
- echo "Experiment ${pslot} **SUCCESS** on ${MACHINE_ID^} at ${DATE}" >> "${output_ci_single}"
- echo "Experiment ${pslot} *** SUCCESS *** at ${DATE}" >> "${output_ci}"
- # "${GH}" pr comment "${pr}" --repo "${REPO_URL}" --body-file "${output_ci_single}"
- fi
- done
-done
diff --git a/dev/ci/scripts/clone-build_ci.sh b/dev/ci/scripts/clone-build_ci.sh
deleted file mode 100755
index 3cef7fc230b..00000000000
--- a/dev/ci/scripts/clone-build_ci.sh
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/bin/bash
-set -eux
-
-#####################################################################
-# Usage and arguments for specfifying cloned directgory
-#####################################################################
-usage() {
- set +x
- echo
- echo "Usage: $0 -p -d -o