-
Notifications
You must be signed in to change notification settings - Fork 4
Validate rose meta #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Validate rose meta #105
Changes from all commits
9136795
6e456a1
de0d575
13d91dd
a863278
69f4176
11a6c9f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,230 @@ | ||||||
#!/usr/bin/env python3 | ||||||
############################################################################## | ||||||
# (c) Crown copyright 2024 Met Office. All rights reserved. | ||||||
# The file LICENCE, distributed with this code, contains details of the terms | ||||||
# under which the code may be used. | ||||||
############################################################################## | ||||||
""" | ||||||
Validate the rose metadata in lfric_apps | ||||||
""" | ||||||
|
||||||
import os | ||||||
import sys | ||||||
import subprocess | ||||||
import argparse | ||||||
|
||||||
# A list of invalid metadata sections. Most are invalid as they are imported by | ||||||
# lfric-gungho but also use the files namelist contained there, creating a circular | ||||||
# dependency | ||||||
INVALID_METADATA = [ | ||||||
"jules-lfric", | ||||||
"lfric-jules-shared", | ||||||
"socrates-radiation", | ||||||
"um-aerosol", | ||||||
"um-boundary_layer", | ||||||
"um-chemistry", | ||||||
"um-cloud", | ||||||
"um-convection", | ||||||
"um-iau", | ||||||
"um-microphysics", | ||||||
"um-orographic_drag", | ||||||
"um-spectral_gwd", | ||||||
"um-stochastic_physics", | ||||||
] | ||||||
|
||||||
# A list of invalid rose-stem apps to ignore when checking metadata validity. These are | ||||||
# mostly apps required for lfric coupled which use metadata from other repositories | ||||||
INVALID_APPS = [ | ||||||
"fcm_make_drivers", | ||||||
"fcm_make_ocean", | ||||||
"fcm_make_river", | ||||||
"lfric_coupled_rivers", | ||||||
] | ||||||
|
||||||
|
||||||
def run_command(command, shell=False, env=None): | ||||||
""" | ||||||
Run a subprocess command and return the result object | ||||||
Inputs: | ||||||
- command, str with command to run | ||||||
Outputs: | ||||||
- result object from subprocess.run | ||||||
""" | ||||||
if not shell: | ||||||
command = command.split() | ||||||
if not env: | ||||||
env = os.environ | ||||||
return subprocess.run( | ||||||
command, | ||||||
capture_output=True, | ||||||
text=True, | ||||||
timeout=120, | ||||||
shell=shell, | ||||||
check=False, | ||||||
env=env, | ||||||
) | ||||||
|
||||||
|
||||||
def check_rose_metadata(rose_meta_path, source_path): | ||||||
""" | ||||||
Auto find rose-meta sections from the top level rose-meta directory and run `rose | ||||||
metadata-check` on each | ||||||
""" | ||||||
|
||||||
print("\n\n[INFO] - Checking rose metadata sections\n\n") | ||||||
failed = False | ||||||
|
||||||
# Add ROSE_META_PATH to env for rose metadata-check command | ||||||
my_env = os.environ.copy() | ||||||
my_env["ROSE_META_PATH"] = rose_meta_path | ||||||
|
||||||
start_dir = os.path.join(source_path, "rose-meta") | ||||||
dirs = os.listdir(start_dir) | ||||||
for section in dirs: | ||||||
if section in INVALID_METADATA: | ||||||
continue | ||||||
meta_dir = os.path.join(start_dir, section, "HEAD") | ||||||
command = f"rose metadata-check --verbose -C {meta_dir}" | ||||||
result = run_command(command, env=my_env) | ||||||
if result.returncode: | ||||||
print(f"[FAIL] - {section} failed to validate") | ||||||
print( | ||||||
f"Failure running rose metata-check on {section}:\n{result.stderr}", | ||||||
file=sys.stderr, | ||||||
) | ||||||
failed = True | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
else: | ||||||
print(f"[PASS] - {section} validated") | ||||||
|
||||||
return failed | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
|
||||||
def parse_suite_controlled(err_msg): | ||||||
""" | ||||||
Remove any app validation error messages resulting from suite_controlled option | ||||||
configs | ||||||
""" | ||||||
|
||||||
err = [] | ||||||
split_err = err_msg.split("\n") | ||||||
i = 0 | ||||||
while i < len(split_err): | ||||||
line = split_err[i] | ||||||
if "opts=suite_controlled" in line: | ||||||
i += 2 | ||||||
continue | ||||||
if line.strip(): | ||||||
err.append(line) | ||||||
i += 1 | ||||||
if len(err) > 1: | ||||||
return err | ||||||
return [] | ||||||
|
||||||
|
||||||
def check_rose_stem_apps(meta_paths, source_path): | ||||||
""" | ||||||
Auto find rose-stem apps that use rose metadata and validate these using 'rose | ||||||
macro --validate' | ||||||
""" | ||||||
|
||||||
print("\n\n[INFO] - Validating rose-stem apps\n\n") | ||||||
failed = False | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
start_dir = os.path.join(source_path, "rose-stem", "app") | ||||||
apps = os.listdir(start_dir) | ||||||
for app in apps: | ||||||
if app in INVALID_APPS: | ||||||
continue | ||||||
app_dir = os.path.join(start_dir, app) | ||||||
conf_file = os.path.join(app_dir, "rose-app.conf") | ||||||
with open(conf_file, "r") as f: | ||||||
for line in f: | ||||||
if line.startswith("meta="): | ||||||
break | ||||||
else: | ||||||
continue | ||||||
command = f"rose macro --validate {meta_paths} -C {app_dir} --no-warn version" | ||||||
result = run_command(command) | ||||||
# Check that errors are only from rose-app-suite-controlled.conf | ||||||
errors = [] | ||||||
if result.returncode: | ||||||
errors = parse_suite_controlled(result.stderr) | ||||||
errors = "\n".join(e for e in errors) | ||||||
|
||||||
if errors: | ||||||
print(f"[FAIL] - {app} failed to validate") | ||||||
print(f"Failure validating app {app}:\n{errors}", file=sys.stderr) | ||||||
failed = True | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
else: | ||||||
print(f"[PASS] - {app} validated") | ||||||
|
||||||
return failed | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
|
||||||
def parse_args(): | ||||||
""" | ||||||
Read command line args | ||||||
""" | ||||||
|
||||||
parser = argparse.ArgumentParser("Pre-process and apply LFRic Apps upgrade macros.") | ||||||
parser.add_argument( | ||||||
"-a", | ||||||
"--apps", | ||||||
default=None, | ||||||
help="The path to the LFRic Apps source. At least one of this or Core are " | ||||||
"required. If both are provided then it will be assumed that Apps is the " | ||||||
"repository to check.", | ||||||
) | ||||||
parser.add_argument( | ||||||
"-c", | ||||||
"--core", | ||||||
default=None, | ||||||
help="The path to the LFRic Core source. At least one of this or Apps are " | ||||||
"required. If both are provided then it will be assumed that Apps is the " | ||||||
"repository to check.", | ||||||
) | ||||||
|
||||||
args = parser.parse_args() | ||||||
|
||||||
if args.apps: | ||||||
args.apps = os.path.expanduser(args.apps) | ||||||
if args.core: | ||||||
args.core = os.path.expanduser(args.core) | ||||||
|
||||||
return args | ||||||
|
||||||
|
||||||
def main(): | ||||||
""" | ||||||
main function for this script | ||||||
""" | ||||||
args = parse_args() | ||||||
|
||||||
meta_paths = "" | ||||||
rose_meta_path = "" | ||||||
if args.apps: | ||||||
source_path = args.apps | ||||||
meta_paths += f"-M {os.path.join(args.apps, "rose-meta")}" | ||||||
rose_meta_path += args.apps | ||||||
elif args.core: | ||||||
source_path = args.core | ||||||
meta_paths += f"-M {os.path.join(args.core, "rose-meta")} " | ||||||
if rose_meta_path: | ||||||
rose_meta_path += f":{args.core}" | ||||||
else: | ||||||
rose_meta_path = args.core | ||||||
else: | ||||||
raise RuntimeError( | ||||||
"At least one of the Apps or Core sources must be provided as a command " | ||||||
"line argument" | ||||||
) | ||||||
|
||||||
if check_rose_metadata(rose_meta_path, source_path) or check_rose_stem_apps( | ||||||
meta_paths, source_path | ||||||
): | ||||||
sys.exit("There were metadata validation failures. See output for details") | ||||||
print("All metadata successfully validated") | ||||||
|
||||||
|
||||||
if __name__ == "__main__": | ||||||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.