Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe6ab5c
Update GHA versions again
kuanchihwang-ncar Jul 20, 2026
83a7bee
Pin Fortitude to 0.9.*
kuanchihwang-ncar Jul 20, 2026
18a3353
Should update `pip` itself before any other package operations
kuanchihwang-ncar Jul 24, 2026
f6965a7
Update Fortitude configuration
kuanchihwang-ncar Jul 21, 2026
3e50094
Remove unnecessary escapes for forward slashes
kuanchihwang-ncar Jul 20, 2026
e1f43f3
Save matched paths for use with subsequent CI jobs
kuanchihwang-ncar Jul 22, 2026
7ef8d23
Introduce Fortitude linting to CAM-SIMA Fortran CI
kuanchihwang-ncar Jul 22, 2026
38ff924
Do not use magic number for standard output
kuanchihwang-ncar Jul 27, 2026
fb228d9
Fix linter error: MOD051 `save` statement is superfluous at the modul…
kuanchihwang-ncar Jul 24, 2026
3611eef
Fix linter error: S233 Keyword `CONTAINS` should be `contains`
kuanchihwang-ncar Jul 24, 2026
c669a85
Fix linter error: C043 File opened without the `action` specifier
kuanchihwang-ncar Jul 24, 2026
3e619a9
Fix linter error: C183 `iostat` used without `iomsg`
kuanchihwang-ncar Jul 24, 2026
47f4f8f
Fix linter error: S241 String uses double quotes but single quotes ar…
kuanchihwang-ncar Jul 24, 2026
204116f
Fix linter error: S271 `use` statements are not sorted
kuanchihwang-ncar Jul 24, 2026
28c3c33
Fix linter error: C122 `use` for intrinsic module missing the `intrin…
kuanchihwang-ncar Jul 24, 2026
9df6a70
Separate decoding and filtering logic for more clarity
kuanchihwang-ncar Jul 28, 2026
5ab052f
Merge branch 'development' into develop/add-fortitude-to-ci
kuanchihwang-ncar Jul 30, 2026
62132a8
Merge branch 'development' into develop/add-fortitude-to-ci
kuanchihwang-ncar Aug 4, 2026
49b5ef2
Temporarily ignore rule about switching to `mpi_f08` module
kuanchihwang-ncar Aug 4, 2026
15804e9
Use annotated output format for GitHub Actions
kuanchihwang-ncar Aug 4, 2026
41d917b
Ignore rule about sorting `use` statements
kuanchihwang-ncar Aug 4, 2026
a313f21
Raise complexity check thresholds
kuanchihwang-ncar Aug 4, 2026
8508b00
Dial back cyclomatic complexity threshold to 20
kuanchihwang-ncar Aug 5, 2026
1079fa1
Merge branch 'development' into develop/add-fortitude-to-ci
kuanchihwang-ncar Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 95 additions & 7 deletions .github/workflows/cam_sima_fortran_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,35 @@ jobs:
name: Check if jobs should run
runs-on: ubuntu-slim
outputs:
should-run: ${{ steps.conditional.outputs.result }}
# Encoded in base64 to preserve the matched paths exactly.
matched-paths: ${{ steps.conditional.outputs.matched-paths }}
should-run: ${{ steps.conditional.outputs.should-run }}
timeout-minutes: 1
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Check if there are changes to Fortran source code
id: conditional
run: |
set -euo pipefail

write_result_and_exit() {
if [ -f matched-paths.txt ]; then
echo "matched-paths=$(base64 -w 0 < matched-paths.txt)" >> "$GITHUB_OUTPUT"
else
echo "matched-paths=" >> "$GITHUB_OUTPUT"
fi

case "$1" in
false)
echo "Result: Skip jobs."
echo "result=$1" >> "$GITHUB_OUTPUT"
echo "should-run=$1" >> "$GITHUB_OUTPUT"
exit 0
;;
true)
echo "Result: Run jobs."
echo "result=$1" >> "$GITHUB_OUTPUT"
echo "should-run=$1" >> "$GITHUB_OUTPUT"
exit 0
;;
error|*)
Expand Down Expand Up @@ -114,7 +122,7 @@ jobs:
echo "Finding changed paths between $BASE_COMMIT..$HEAD_COMMIT..."
git diff --name-only "$BASE_COMMIT..$HEAD_COMMIT" | tee changed-paths.txt || write_result_and_exit error

if grep -E -q '(^\.github\/workflows\/cam_sima_fortran_ci\.yml$|^share$|^src\/.+$|^test\/unit\/.+$)' changed-paths.txt; then
if grep -E '^\.github/workflows/cam_sima_fortran_ci\.yml$|^share$|^src/.+$|^test/unit/.+$' changed-paths.txt > matched-paths.txt; then
write_result_and_exit true
else
write_result_and_exit false
Expand All @@ -129,6 +137,7 @@ jobs:
name: Overall status (CAM-SIMA Fortran CI)
needs:
- conditional-check
- source-code-linting
- unit-tests
if: ${{ always() }}
runs-on: ubuntu-slim
Expand Down Expand Up @@ -167,6 +176,18 @@ jobs:
;;
esac

case "${{ needs.source-code-linting.result }}" in
skipped|success)
:
;;
cancelled|failure)
write_result_and_exit false
;;
*)
write_result_and_exit error
;;
esac

case "${{ needs.unit-tests.result }}" in
skipped|success)
:
Expand All @@ -180,6 +201,73 @@ jobs:
esac

write_result_and_exit true
source-code-linting:
name: Lint Fortran source code
needs: conditional-check
if: ${{ needs.conditional-check.outputs.should-run == 'true' }}
runs-on: ubuntu-slim
env:
FORTITUDE_VERSION: 0.9.*
SOURCE_CODE_PATH: ${{ github.workspace }}
timeout-minutes: 10
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v7

- name: Setup Python
uses: actions/setup-python@v7
with:
cache: pip
python-version: 3.12

- name: Install Fortitude
run: |
python3 -m venv venv-fortitude
source venv-fortitude/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install "fortitude-lint==$FORTITUDE_VERSION"

- name: Lint Fortran source code
run: |
# Retrieve the matched paths.
echo "${{ needs.conditional-check.outputs.matched-paths }}" | base64 -d > matched-paths.txt

# Filter Fortran files for Fortitude.
# Fortitude will choke if it encounters non-Fortran files in the command line arguments.
if grep -E '[^/]+\.(F|f|F90|f90|pf)$' matched-paths.txt > filtered-paths.txt; then
set --
while IFS="" read -r LINE || [ -n "$LINE" ]; do
[ -f "$LINE" ] && set -- "$@" "$LINE"
done < filtered-paths.txt

# Fortitude will still choke if there are no command line arguments.
if [ "$#" -gt 0 ]; then
source venv-fortitude/bin/activate
# First run: Annotated output for GitHub Actions.
fortitude \
--config-file "$SOURCE_CODE_PATH/test/fortitude.toml" \
check \
--exit-zero \
--output-format github \
"$@" | sed -e "s|file=$SOURCE_CODE_PATH/|file=|g"
# Second run: Output to a log file for uploading.
fortitude \
--config-file "$SOURCE_CODE_PATH/test/fortitude.toml" \
check \
--exit-zero \
--output-file "$SOURCE_CODE_PATH/source-code-linting.log" \
"$@"
fi
fi

- name: Upload Fortran source code linting log
if: ${{ always() }}
uses: actions/upload-artifact@v7
with:
if-no-files-found: ignore
name: source-code-linting-log
path: ${{ env.SOURCE_CODE_PATH }}/source-code-linting.log
retention-days: 7
unit-tests:
name: Build and run unit tests (${{ matrix.compiler }})
needs: conditional-check
Expand Down Expand Up @@ -218,14 +306,14 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Checkout dependencies
run: |
./bin/git-fleximod update ncar-physics share

- name: Checkout rrtmgp-data for PIO file reader tests
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
repository: earth-system-radiation/rrtmgp-data
ref: ${{ env.RRTMGP_DATA_REFERENCE }}
Expand Down
28 changes: 18 additions & 10 deletions .github/workflows/mpas_dynamical_core_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,35 @@ jobs:
name: Check if jobs should run
runs-on: ubuntu-slim
outputs:
should-run: ${{ steps.conditional.outputs.result }}
# Encoded in base64 to preserve the matched paths exactly.
matched-paths: ${{ steps.conditional.outputs.matched-paths }}
should-run: ${{ steps.conditional.outputs.should-run }}
timeout-minutes: 1
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Check if there are changes to MPAS dynamical core
id: conditional
run: |
set -euo pipefail

write_result_and_exit() {
if [ -f matched-paths.txt ]; then
echo "matched-paths=$(base64 -w 0 < matched-paths.txt)" >> "$GITHUB_OUTPUT"
else
echo "matched-paths=" >> "$GITHUB_OUTPUT"
fi

case "$1" in
false)
echo "Result: Skip jobs."
echo "result=$1" >> "$GITHUB_OUTPUT"
echo "should-run=$1" >> "$GITHUB_OUTPUT"
exit 0
;;
true)
echo "Result: Run jobs."
echo "result=$1" >> "$GITHUB_OUTPUT"
echo "should-run=$1" >> "$GITHUB_OUTPUT"
exit 0
;;
error|*)
Expand Down Expand Up @@ -114,7 +122,7 @@ jobs:
echo "Finding changed paths between $BASE_COMMIT..$HEAD_COMMIT..."
git diff --name-only "$BASE_COMMIT..$HEAD_COMMIT" | tee changed-paths.txt || write_result_and_exit error

if grep -E -q '(^\.github\/workflows\/mpas_dynamical_core_ci\.yml$|^src\/dynamics\/mpas\/.+$)' changed-paths.txt; then
if grep -E '^\.github/workflows/mpas_dynamical_core_ci\.yml$|^src/dynamics/mpas/.+$' changed-paths.txt > matched-paths.txt; then
write_result_and_exit true
else
write_result_and_exit false
Expand Down Expand Up @@ -199,15 +207,15 @@ jobs:
if: ${{ needs.conditional-check.outputs.should-run == 'true' }}
runs-on: ubuntu-slim
env:
FORTITUDE_VERSION: 0.7.*
FORTITUDE_VERSION: 0.9.*
SOURCE_CODE_PATH: ${{ github.workspace }}/src/dynamics/mpas
timeout-minutes: 10
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
cache: pip
python-version: 3.12
Expand All @@ -216,6 +224,7 @@ jobs:
run: |
python3 -m venv venv-fortitude
source venv-fortitude/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install "fortitude-lint==$FORTITUDE_VERSION"

- name: Lint Fortran source code
Expand All @@ -226,7 +235,6 @@ jobs:
check \
--exit-zero \
--output-file "$SOURCE_CODE_PATH/source-code-linting.log" \
--preview \
"$SOURCE_CODE_PATH"
cat "$SOURCE_CODE_PATH/source-code-linting.log"

Expand Down Expand Up @@ -267,7 +275,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout CAM-SIMA
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Build unit tests
run: |
Expand Down
24 changes: 14 additions & 10 deletions src/control/cam_logfile.F90
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ module cam_logfile
!-----------------------------------------------------------------------
!- use statements ------------------------------------------------------
!-----------------------------------------------------------------------
use, intrinsic :: iso_fortran_env, only: output_unit

!-----------------------------------------------------------------------
!- module boilerplate --------------------------------------------------
!-----------------------------------------------------------------------
implicit none
private
save

!-----------------------------------------------------------------------
! Public interfaces ----------------------------------------------------
Expand All @@ -35,7 +36,7 @@ module cam_logfile
integer, public, protected :: debug_output = DEBUGOUT_NONE
!> \section arg_table_cam_logfile Argument Table
!! \htmlinclude cam_logfile.html
integer, public, protected :: iulog = 6
integer, public, protected :: iulog = output_unit
Comment thread
peverwhee marked this conversation as resolved.
logical, public, protected :: log_output = .false.

!-----------------------------------------------------------------------
Expand All @@ -48,7 +49,7 @@ module cam_logfile
module procedure cam_log_multiwrite_nr8 ! Multiple 8-byte reals
end interface cam_log_multiwrite

CONTAINS
contains

!-----------------------------------------------------------------------
! Subroutines and functions --------------------------------------------
Expand All @@ -72,13 +73,15 @@ end subroutine cam_set_log_unit

subroutine cam_logfile_readnl(nlfile)
use mpi, only: mpi_integer
use shr_kind_mod, only: cx => shr_kind_cx
use shr_nl_mod, only: find_group_name => shr_nl_find_group_name
use spmd_utils, only: mpicom, masterprocid, masterproc

! nlfile: filepath for file containing namelist input
character(len=*), intent(in) :: nlfile

! Local variables
character(len=cx) :: cerr
integer :: unitn
integer :: ierr

Expand All @@ -92,13 +95,14 @@ subroutine cam_logfile_readnl(nlfile)
log_output = masterproc

if (masterproc) then
open(newunit=unitn, file=trim(nlfile), status='old')
open(newunit=unitn, action='read', file=trim(nlfile), status='old')
call find_group_name(unitn, 'cam_logfile_nl', status=ierr)
if (ierr == 0) then
read(unitn, cam_logfile_nl, iostat=ierr)
read(unitn, cam_logfile_nl, iomsg=cerr, iostat=ierr)
if (ierr /= 0) then
! Can't call endrun because of dependency loop
write(iulog, *) subname, ': ERROR: reading namelist'
write(iulog, *) subname, ': ERROR: reading namelist' // new_line('') // &
trim(adjustl(cerr))
end if
end if
close(unitn)
Expand All @@ -123,16 +127,16 @@ subroutine cam_logfile_readnl(nlfile)
if (ierr /= 0) then
! Can't call endrun because of dependency loop
! But MPI usually crashes in Fortran
write(iulog, *) subname, ": ERROR: mpi_bcast: debug_output"
write(iulog, *) subname, ': ERROR: mpi_bcast: debug_output'
end if

end subroutine cam_logfile_readnl

subroutine cam_log_multiwrite_ni(subname, headers, fmt_string, values)
! Print out values from every task
use mpi, only: mpi_integer
use shr_sys_mod, only: shr_sys_flush
use spmd_utils, only: mpicom, masterprocid, masterproc, npes
use mpi, only: mpi_integer

! Dummy arguments
character(len=*), intent(in) :: subname
Expand Down Expand Up @@ -165,10 +169,10 @@ end subroutine cam_log_multiwrite_ni

subroutine cam_log_multiwrite_nr8(subname, headers, fmt_string, values)
! Print out values from every task
use iso_fortran_env, only: r8 => REAL64
use, intrinsic :: iso_fortran_env, only: r8 => REAL64
use mpi, only: mpi_real8
use shr_sys_mod, only: shr_sys_flush
use spmd_utils, only: mpicom, masterprocid, masterproc, npes
use mpi, only: mpi_real8

! Dummy arguments
character(len=*), intent(in) :: subname
Expand Down
Loading