From be0dce37718223dc5ebead2cb71bb6586d08a79f Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Fri, 9 Jan 2026 17:41:59 +0000 Subject: [PATCH 01/12] added fortitude test and configuration --- rose-stem/site/meto/groups.cylc | 1 + rose-stem/templates/graph/populate_graph_scripts.cylc | 1 + rose-stem/templates/runtime/generate_runtime_scripts.cylc | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/rose-stem/site/meto/groups.cylc b/rose-stem/site/meto/groups.cylc index 2452ddd76..62646ad80 100644 --- a/rose-stem/site/meto/groups.cylc +++ b/rose-stem/site/meto/groups.cylc @@ -9,6 +9,7 @@ "scripts": [ "config_dump_checker", "style_checker", + "fortitude_linter", "site_validator", "validate_rose_meta", "rose-stem_lint_checker", diff --git a/rose-stem/templates/graph/populate_graph_scripts.cylc b/rose-stem/templates/graph/populate_graph_scripts.cylc index 25e0eeda1..2aa328729 100644 --- a/rose-stem/templates/graph/populate_graph_scripts.cylc +++ b/rose-stem/templates/graph/populate_graph_scripts.cylc @@ -51,6 +51,7 @@ {# * config_dump_checker #} {# * style_checker #} {# * site_validator #} + {# * fortitude_linter #} {# * rose-stem_lint_checker #} {# * validate_rose_meta #} {# * global_variables_checker #} diff --git a/rose-stem/templates/runtime/generate_runtime_scripts.cylc b/rose-stem/templates/runtime/generate_runtime_scripts.cylc index 02faaf7b2..3c501f06f 100644 --- a/rose-stem/templates/runtime/generate_runtime_scripts.cylc +++ b/rose-stem/templates/runtime/generate_runtime_scripts.cylc @@ -36,6 +36,11 @@ inherit={{inherit.str|upper}} script="rose task-run --app-key=check_style" +{% elif "fortitude_linter" in task %} + + inherit={{inherit.str|upper}} + script="rose task-run --app-key=check_fortitude_linter" + {% elif "extract_checker" in task %} inherit={{inherit.str|upper}} From 7a4cb6c9be090eb91461dd2a17a0614dd35b98d4 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Fri, 9 Jan 2026 17:42:55 +0000 Subject: [PATCH 02/12] added new files --- infrastructure/fortitude.toml | 14 +++ .../file/fortitude.toml | 15 ++++ rose-stem/bin/fortitude_launcher.py | 88 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 infrastructure/fortitude.toml create mode 100644 rose-stem/app/check_fortitude_linter/file/fortitude.toml create mode 100644 rose-stem/bin/fortitude_launcher.py diff --git a/infrastructure/fortitude.toml b/infrastructure/fortitude.toml new file mode 100644 index 000000000..e3552c8e5 --- /dev/null +++ b/infrastructure/fortitude.toml @@ -0,0 +1,14 @@ +############################################################################## +# (c) Crown copyright 2026 Met Office. All rights reserved. +# The file LICENCE, distributed with this code, contains details of the terms +# under which the code may be used. +############################################################################## + +[check] + +file-extensions= ["f90", "F90", "X90", "x90"] #check these file types + + +select = ["C121"] + +output-format = "grouped" #group results by file diff --git a/rose-stem/app/check_fortitude_linter/file/fortitude.toml b/rose-stem/app/check_fortitude_linter/file/fortitude.toml new file mode 100644 index 000000000..02708f2b3 --- /dev/null +++ b/rose-stem/app/check_fortitude_linter/file/fortitude.toml @@ -0,0 +1,15 @@ +############################################################################## +# (c) Crown copyright 2026 Met Office. All rights reserved. +# The file LICENCE, distributed with this code, contains details of the terms +# under which the code may be used. +############################################################################## + +[check] + +file-extensions= ["f90", "F90", "X90", "x90"] #check these file types + + +select = ["C121"] + + +output-format = "grouped" #group results by file diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py new file mode 100644 index 000000000..188746e15 --- /dev/null +++ b/rose-stem/bin/fortitude_launcher.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +############################################################################## +# (c) Crown copyright 2026 Met Office. All rights reserved. +# The file LICENCE, distributed with this code, contains details of the terms +# under which the code may be used. +############################################################################## + +""" +Launch fortitude on list of directories. Run on all and print outputs. Fail if +any style changes required. +""" + +import argparse +import os +import subprocess +import sys + +def launch_fortitude(config_path, app_path): + """ + Launch fortitude as a subprocess command and check the output + """ + + command = f"fortitude --config-file {config_path} check {app_path}" + result = subprocess.run(command.split(), capture_output=True, text=True) + + print(result.stdout) + return result + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Run fortitude on all applications. If " + "application/fortitude.toml exists use that file, otherwise " + "use one in rose-stem/app/check_fortitude_linter/file. " + "Print output, raise error if any changes required." + ) + parser.add_argument( + "-s", + "--source", + help="The top level of lfric_core directory.", + required=True, + ) + args = parser.parse_args() + + failed_apps = {} + + candidates = [ + "infrastructure", + "mesh_tools", + "components/coupling", + "components/driver", + "components/science", + "components/inventory", + "components/lfric-xios", + "applications/skeleton", + "applications/simple_diffusion", + "applications/io_demo", + ] + for app in candidates: + print(f"Running on {app}\n") + app_path = os.path.join(args.source, app) + config_path = os.path.join(app_path, "fortitude.toml") + if not os.path.exists(os.path.join(config_path)): + print("Using universal config (toml) file. (Some apps use their own config file.)") + config_path = os.path.join( + args.source, + "rose-stem", + "app", + "check_fortitude_linter", + "file", + "fortitude.toml", + ) + result = launch_fortitude(config_path, app_path) + if result.returncode: + print(f"Checking: {app} \n", file=sys.stderr) #prints the app run on if there are errors of any kind + if not result.stderr: + print("Found lint errors:", file=sys.stderr)# prints if no other/config errors are found + print(result.stdout, file=sys.stderr) #prints the lint errors + if result.stderr: + print("Found non-lint errors: \n", file=sys.stderr) #prints if there are other/config errors + print(result.stderr, "\n\n\n", file=sys.stderr) #prints the other/config error + failed_apps[app] = result.stderr + + if failed_apps: + error_message = "" + print("\n\n\nSummary: Fortitude found errors in the following repositories:\n", file=sys.stderr) + for failed in failed_apps: + error_message += f"{failed}\n" + sys.exit(error_message) From 4dae812ef9e34ac30ab376c5e4486598e434461a Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Mon, 12 Jan 2026 17:54:50 +0000 Subject: [PATCH 03/12] added launch command --- rose-stem/app/check_fortitude_linter/rose-app.conf | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 rose-stem/app/check_fortitude_linter/rose-app.conf diff --git a/rose-stem/app/check_fortitude_linter/rose-app.conf b/rose-stem/app/check_fortitude_linter/rose-app.conf new file mode 100644 index 000000000..c2ced2a12 --- /dev/null +++ b/rose-stem/app/check_fortitude_linter/rose-app.conf @@ -0,0 +1,2 @@ +[command] +default=$CYLC_WORKFLOW_RUN_DIR/bin/fortitude_launcher.py -s $SOURCE_ROOT From e3b02060143153fe8997208b499bf1f1b2abcb60 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Tue, 13 Jan 2026 17:36:25 +0000 Subject: [PATCH 04/12] updated permissions on launch file --- rose-stem/bin/fortitude_launcher.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 rose-stem/bin/fortitude_launcher.py diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py old mode 100644 new mode 100755 From 5b86c63693a98fc0821c647f1fca54aa216637a1 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Tue, 13 Jan 2026 18:27:53 +0000 Subject: [PATCH 05/12] updated source --- rose-stem/app/check_fortitude_linter/rose-app.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rose-stem/app/check_fortitude_linter/rose-app.conf b/rose-stem/app/check_fortitude_linter/rose-app.conf index c2ced2a12..e20cad3bb 100644 --- a/rose-stem/app/check_fortitude_linter/rose-app.conf +++ b/rose-stem/app/check_fortitude_linter/rose-app.conf @@ -1,2 +1,2 @@ [command] -default=$CYLC_WORKFLOW_RUN_DIR/bin/fortitude_launcher.py -s $SOURCE_ROOT +default=$CYLC_WORKFLOW_RUN_DIR/bin/fortitude_launcher.py -s $SOURCE_ROOT/lfric_core From 7b33f39654d90aa236117f5962bb1f2febde8155 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Wed, 14 Jan 2026 17:19:19 +0000 Subject: [PATCH 06/12] vupdating copyright --- infrastructure/fortitude.toml | 2 +- rose-stem/app/check_fortitude_linter/file/fortitude.toml | 2 +- rose-stem/bin/fortitude_launcher.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/infrastructure/fortitude.toml b/infrastructure/fortitude.toml index e3552c8e5..2019891b4 100644 --- a/infrastructure/fortitude.toml +++ b/infrastructure/fortitude.toml @@ -1,5 +1,5 @@ ############################################################################## -# (c) Crown copyright 2026 Met Office. All rights reserved. +# (c) Crown copyright Met Office. All rights reserved. # The file LICENCE, distributed with this code, contains details of the terms # under which the code may be used. ############################################################################## diff --git a/rose-stem/app/check_fortitude_linter/file/fortitude.toml b/rose-stem/app/check_fortitude_linter/file/fortitude.toml index 02708f2b3..577dcd310 100644 --- a/rose-stem/app/check_fortitude_linter/file/fortitude.toml +++ b/rose-stem/app/check_fortitude_linter/file/fortitude.toml @@ -1,5 +1,5 @@ ############################################################################## -# (c) Crown copyright 2026 Met Office. All rights reserved. +# (c) Crown copyright Met Office. All rights reserved. # The file LICENCE, distributed with this code, contains details of the terms # under which the code may be used. ############################################################################## diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py index 188746e15..7cdeb6fae 100755 --- a/rose-stem/bin/fortitude_launcher.py +++ b/rose-stem/bin/fortitude_launcher.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 ############################################################################## -# (c) Crown copyright 2026 Met Office. All rights reserved. +# (c) Crown copyright Met Office. All rights reserved. # The file LICENCE, distributed with this code, contains details of the terms # under which the code may be used. ############################################################################## From 329abfacc387431282e03a57854349642bc4d588 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Wed, 14 Jan 2026 17:51:50 +0000 Subject: [PATCH 07/12] fixing pep8 violations --- rose-stem/bin/fortitude_launcher.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py index 7cdeb6fae..ce44a464b 100755 --- a/rose-stem/bin/fortitude_launcher.py +++ b/rose-stem/bin/fortitude_launcher.py @@ -15,6 +15,7 @@ import subprocess import sys + def launch_fortitude(config_path, app_path): """ Launch fortitude as a subprocess command and check the output @@ -26,6 +27,7 @@ def launch_fortitude(config_path, app_path): print(result.stdout) return result + if __name__ == "__main__": parser = argparse.ArgumentParser( description="Run fortitude on all applications. If " @@ -60,7 +62,8 @@ def launch_fortitude(config_path, app_path): app_path = os.path.join(args.source, app) config_path = os.path.join(app_path, "fortitude.toml") if not os.path.exists(os.path.join(config_path)): - print("Using universal config (toml) file. (Some apps use their own config file.)") + print('''Using universal config (toml) file. + (Some apps use their own config file.)''') config_path = os.path.join( args.source, "rose-stem", @@ -69,20 +72,27 @@ def launch_fortitude(config_path, app_path): "file", "fortitude.toml", ) + result = launch_fortitude(config_path, app_path) if result.returncode: - print(f"Checking: {app} \n", file=sys.stderr) #prints the app run on if there are errors of any kind + # prints the app run on if there are errors of any kind + print(f"Checking: {app} \n", file=sys.stderr) if not result.stderr: - print("Found lint errors:", file=sys.stderr)# prints if no other/config errors are found - print(result.stdout, file=sys.stderr) #prints the lint errors + # prints if no other/config errors are found + print("Found lint errors:", file=sys.stderr) + # prints the lint errors + print(result.stdout, file=sys.stderr) if result.stderr: - print("Found non-lint errors: \n", file=sys.stderr) #prints if there are other/config errors - print(result.stderr, "\n\n\n", file=sys.stderr) #prints the other/config error + # prints if there are other/config errors + print("Found non-lint errors: \n", file=sys.stderr) + # prints the other/config error + print(result.stderr, "\n\n\n", file=sys.stderr) failed_apps[app] = result.stderr if failed_apps: error_message = "" - print("\n\n\nSummary: Fortitude found errors in the following repositories:\n", file=sys.stderr) + print('''\n\n\nSummary: Fortitude found errors in + the following repositories:\n''', file=sys.stderr) for failed in failed_apps: error_message += f"{failed}\n" sys.exit(error_message) From ae23ff80c2192ea8323e2ece78d2191fda06a24a Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Wed, 14 Jan 2026 18:19:51 +0000 Subject: [PATCH 08/12] signing contributor page --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1e79a1045..9a8006ef3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -11,4 +11,5 @@ | stevemullerworth | Steve Mullerworth | Met Office | 2026-01-08 | | harry-shepherd | Harry Shepherd | Met Office | 2026-01-08 | | EdHone | Ed Hone | Met Office | 2026-01-09 | +| mo-lucy-gordon | Lucy Gordon | Met Office | 2026-01-14 | From 9191270ec7e3e14c28965360b211ba1d2f68d317 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Fri, 16 Jan 2026 10:51:57 +0000 Subject: [PATCH 09/12] corrected error message formatting --- rose-stem/bin/fortitude_launcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py index ce44a464b..d39729af3 100755 --- a/rose-stem/bin/fortitude_launcher.py +++ b/rose-stem/bin/fortitude_launcher.py @@ -62,8 +62,8 @@ def launch_fortitude(config_path, app_path): app_path = os.path.join(args.source, app) config_path = os.path.join(app_path, "fortitude.toml") if not os.path.exists(os.path.join(config_path)): - print('''Using universal config (toml) file. - (Some apps use their own config file.)''') + print("Using universal config (toml) file." + " (Some apps use their own config file.)") config_path = os.path.join( args.source, "rose-stem", @@ -91,8 +91,8 @@ def launch_fortitude(config_path, app_path): if failed_apps: error_message = "" - print('''\n\n\nSummary: Fortitude found errors in - the following repositories:\n''', file=sys.stderr) + print("\n\n\nSummary: Fortitude found errors in" + " the following repositories:\n", file=sys.stderr) for failed in failed_apps: error_message += f"{failed}\n" sys.exit(error_message) From 439ee4e6065a934f11a6844365049b14d117fb82 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Wed, 11 Mar 2026 17:35:56 +0000 Subject: [PATCH 10/12] updated script to be universal, added code review suggestions --- .../app/check_fortitude_linter/rose-app.conf | 2 +- rose-stem/bin/fortitude_launcher.py | 96 ++++++++----------- 2 files changed, 42 insertions(+), 56 deletions(-) diff --git a/rose-stem/app/check_fortitude_linter/rose-app.conf b/rose-stem/app/check_fortitude_linter/rose-app.conf index e20cad3bb..4fb874d41 100644 --- a/rose-stem/app/check_fortitude_linter/rose-app.conf +++ b/rose-stem/app/check_fortitude_linter/rose-app.conf @@ -1,2 +1,2 @@ [command] -default=$CYLC_WORKFLOW_RUN_DIR/bin/fortitude_launcher.py -s $SOURCE_ROOT/lfric_core +default=$CYLC_WORKFLOW_RUN_DIR/bin/fortitude_launcher.py $SOURCE_ROOT/lfric_core diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py index d39729af3..8aa1b9046 100755 --- a/rose-stem/bin/fortitude_launcher.py +++ b/rose-stem/bin/fortitude_launcher.py @@ -6,23 +6,23 @@ ############################################################################## """ -Launch fortitude on list of directories. Run on all and print outputs. Fail if -any style changes required. + Launch fortitude on list of directories. Run on all and print outputs. +Fail if any style changes required. """ -import argparse -import os -import subprocess import sys +import subprocess +import argparse +from pathlib import Path -def launch_fortitude(config_path, app_path): +def launch_fortitude(config_path: Path, app_path: Path) -> subprocess.CompletedProcess[str]: """ Launch fortitude as a subprocess command and check the output """ - command = f"fortitude --config-file {config_path} check {app_path}" - result = subprocess.run(command.split(), capture_output=True, text=True) + command: list[str] = ["fortitude", "--config-file", str(config_path), "check", str(app_path)] + result = subprocess.run(command, capture_output=True, text=True) print(result.stdout) return result @@ -36,61 +36,47 @@ def launch_fortitude(config_path, app_path): "Print output, raise error if any changes required." ) parser.add_argument( - "-s", - "--source", - help="The top level of lfric_core directory.", - required=True, + "source", + help="The top level of lfric_apps directory." ) args = parser.parse_args() - failed_apps = {} + source_path: Path = Path(args.source) + + failed_apps: dict[str, str] = {} - candidates = [ - "infrastructure", - "mesh_tools", - "components/coupling", - "components/driver", - "components/science", - "components/inventory", - "components/lfric-xios", - "applications/skeleton", - "applications/simple_diffusion", - "applications/io_demo", - ] - for app in candidates: - print(f"Running on {app}\n") - app_path = os.path.join(args.source, app) - config_path = os.path.join(app_path, "fortitude.toml") - if not os.path.exists(os.path.join(config_path)): - print("Using universal config (toml) file." + for top_dir_path in source_path.iterdir(): #e.g. applications,science,interfaces + if not top_dir_path.is_dir(): # don't try to loop over files + continue + for app_path in top_dir_path.iterdir(): #e.g. adjoint_tests, adjoint, coupled_interface + if not app_path.is_dir(): + continue + app_name: str = app_path.name + print(f"Running on {app_name}\n") + config_path: Path = app_path/"fortitude.toml" + if not config_path.exists(): + print("Using universal config (toml) file." " (Some apps use their own config file.)") - config_path = os.path.join( - args.source, - "rose-stem", - "app", - "check_fortitude_linter", - "file", - "fortitude.toml", - ) + config_path: Path = (source_path / "rose-stem" / "app" / "check_fortitude_linter" / "file" / "fortitude.toml") + result: subprocess.CompletedProcess[str] = launch_fortitude(config_path, app_path) + if result.returncode: + # prints the app run on if there are errors of any kind + print(f"Checking: {app_name} \n", file=sys.stderr) + if not result.stderr: + # prints if no other/config errors are found + print("Found lint errors:", file=sys.stderr) + # prints the lint errors + print(result.stdout, file=sys.stderr) + if result.stderr: + # prints if there are other/config errors + print("Found non-lint errors: \n", file=sys.stderr) + # prints the other/config errors + print(result.stderr, "\n\n\n", file=sys.stderr) + failed_apps[app_name] = result.stderr - result = launch_fortitude(config_path, app_path) - if result.returncode: - # prints the app run on if there are errors of any kind - print(f"Checking: {app} \n", file=sys.stderr) - if not result.stderr: - # prints if no other/config errors are found - print("Found lint errors:", file=sys.stderr) - # prints the lint errors - print(result.stdout, file=sys.stderr) - if result.stderr: - # prints if there are other/config errors - print("Found non-lint errors: \n", file=sys.stderr) - # prints the other/config error - print(result.stderr, "\n\n\n", file=sys.stderr) - failed_apps[app] = result.stderr if failed_apps: - error_message = "" + error_message: str = "" print("\n\n\nSummary: Fortitude found errors in" " the following repositories:\n", file=sys.stderr) for failed in failed_apps: From 30a364d6368325cb61486c81374dd2c0b5d71f14 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Thu, 12 Mar 2026 13:42:05 +0000 Subject: [PATCH 11/12] adding fix for pep8 violations --- rose-stem/bin/fortitude_launcher.py | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py index 8aa1b9046..941ea5b93 100755 --- a/rose-stem/bin/fortitude_launcher.py +++ b/rose-stem/bin/fortitude_launcher.py @@ -6,7 +6,7 @@ ############################################################################## """ - Launch fortitude on list of directories. Run on all and print outputs. +Launch fortitude on list of directories. Run on all and print outputs. Fail if any style changes required. """ @@ -16,12 +16,14 @@ from pathlib import Path -def launch_fortitude(config_path: Path, app_path: Path) -> subprocess.CompletedProcess[str]: +def launch_fortitude(config_path: Path, app_path: Path + ) -> subprocess.CompletedProcess[str]: """ Launch fortitude as a subprocess command and check the output """ - command: list[str] = ["fortitude", "--config-file", str(config_path), "check", str(app_path)] + command: list[str] = ["fortitude", "--config-file", str(config_path), + "check", str(app_path)] result = subprocess.run(command, capture_output=True, text=True) print(result.stdout) @@ -45,10 +47,12 @@ def launch_fortitude(config_path: Path, app_path: Path) -> subprocess.CompletedP failed_apps: dict[str, str] = {} - for top_dir_path in source_path.iterdir(): #e.g. applications,science,interfaces + for top_dir_path in source_path.iterdir(): + # e.g. applications,science,interfaces if not top_dir_path.is_dir(): # don't try to loop over files continue - for app_path in top_dir_path.iterdir(): #e.g. adjoint_tests, adjoint, coupled_interface + for app_path in top_dir_path.iterdir(): + # e.g. adjoint_tests, adjoint, coupled_interface if not app_path.is_dir(): continue app_name: str = app_path.name @@ -56,25 +60,27 @@ def launch_fortitude(config_path: Path, app_path: Path) -> subprocess.CompletedP config_path: Path = app_path/"fortitude.toml" if not config_path.exists(): print("Using universal config (toml) file." - " (Some apps use their own config file.)") - config_path: Path = (source_path / "rose-stem" / "app" / "check_fortitude_linter" / "file" / "fortitude.toml") - result: subprocess.CompletedProcess[str] = launch_fortitude(config_path, app_path) + " (Some apps use their own config file.)") + config_path: Path = ( + source_path / "rose-stem" / "app" + / "check_fortitude_linter" / "file" / "fortitude.toml") + result: subprocess.CompletedProcess[str] = launch_fortitude( + config_path, app_path) if result.returncode: # prints the app run on if there are errors of any kind print(f"Checking: {app_name} \n", file=sys.stderr) if not result.stderr: # prints if no other/config errors are found - print("Found lint errors:", file=sys.stderr) + print("Found lint errors:", file=sys.stderr) # prints the lint errors - print(result.stdout, file=sys.stderr) + print(result.stdout, file=sys.stderr) if result.stderr: # prints if there are other/config errors - print("Found non-lint errors: \n", file=sys.stderr) + print("Found non-lint errors: \n", file=sys.stderr) # prints the other/config errors - print(result.stderr, "\n\n\n", file=sys.stderr) + print(result.stderr, "\n\n\n", file=sys.stderr) failed_apps[app_name] = result.stderr - if failed_apps: error_message: str = "" print("\n\n\nSummary: Fortitude found errors in" From bbea2bc92affb1c952017f50e50c5950a455f6e0 Mon Sep 17 00:00:00 2001 From: mo-lucy-gordon Date: Fri, 13 Mar 2026 14:40:56 +0000 Subject: [PATCH 12/12] switch to get simsys launcher script --- .../app/extract_source/bin/extract_source.sh | 6 ++ rose-stem/bin/fortitude_launcher.py | 90 ------------------- 2 files changed, 6 insertions(+), 90 deletions(-) create mode 100644 rose-stem/app/extract_source/bin/extract_source.sh delete mode 100755 rose-stem/bin/fortitude_launcher.py diff --git a/rose-stem/app/extract_source/bin/extract_source.sh b/rose-stem/app/extract_source/bin/extract_source.sh new file mode 100644 index 000000000..59269a484 --- /dev/null +++ b/rose-stem/app/extract_source/bin/extract_source.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eou pipefail + +python3 rose_stem_extract_source.py +cp $SOURCE_ROOT/SimSys_Scripts/fortitude_linter/fortitude_launcher.py $CYLC_WORKFLOW_RUN_DIR/bin diff --git a/rose-stem/bin/fortitude_launcher.py b/rose-stem/bin/fortitude_launcher.py deleted file mode 100755 index 941ea5b93..000000000 --- a/rose-stem/bin/fortitude_launcher.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -############################################################################## -# (c) Crown copyright Met Office. All rights reserved. -# The file LICENCE, distributed with this code, contains details of the terms -# under which the code may be used. -############################################################################## - -""" -Launch fortitude on list of directories. Run on all and print outputs. -Fail if any style changes required. -""" - -import sys -import subprocess -import argparse -from pathlib import Path - - -def launch_fortitude(config_path: Path, app_path: Path - ) -> subprocess.CompletedProcess[str]: - """ - Launch fortitude as a subprocess command and check the output - """ - - command: list[str] = ["fortitude", "--config-file", str(config_path), - "check", str(app_path)] - result = subprocess.run(command, capture_output=True, text=True) - - print(result.stdout) - return result - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Run fortitude on all applications. If " - "application/fortitude.toml exists use that file, otherwise " - "use one in rose-stem/app/check_fortitude_linter/file. " - "Print output, raise error if any changes required." - ) - parser.add_argument( - "source", - help="The top level of lfric_apps directory." - ) - args = parser.parse_args() - - source_path: Path = Path(args.source) - - failed_apps: dict[str, str] = {} - - for top_dir_path in source_path.iterdir(): - # e.g. applications,science,interfaces - if not top_dir_path.is_dir(): # don't try to loop over files - continue - for app_path in top_dir_path.iterdir(): - # e.g. adjoint_tests, adjoint, coupled_interface - if not app_path.is_dir(): - continue - app_name: str = app_path.name - print(f"Running on {app_name}\n") - config_path: Path = app_path/"fortitude.toml" - if not config_path.exists(): - print("Using universal config (toml) file." - " (Some apps use their own config file.)") - config_path: Path = ( - source_path / "rose-stem" / "app" - / "check_fortitude_linter" / "file" / "fortitude.toml") - result: subprocess.CompletedProcess[str] = launch_fortitude( - config_path, app_path) - if result.returncode: - # prints the app run on if there are errors of any kind - print(f"Checking: {app_name} \n", file=sys.stderr) - if not result.stderr: - # prints if no other/config errors are found - print("Found lint errors:", file=sys.stderr) - # prints the lint errors - print(result.stdout, file=sys.stderr) - if result.stderr: - # prints if there are other/config errors - print("Found non-lint errors: \n", file=sys.stderr) - # prints the other/config errors - print(result.stderr, "\n\n\n", file=sys.stderr) - failed_apps[app_name] = result.stderr - - if failed_apps: - error_message: str = "" - print("\n\n\nSummary: Fortitude found errors in" - " the following repositories:\n", file=sys.stderr) - for failed in failed_apps: - error_message += f"{failed}\n" - sys.exit(error_message)