From b6d86139cf09d5dfaafe5cff8b5858ac0e8591e1 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 00:17:35 +0200 Subject: [PATCH 1/7] Fixed validation for index field and command line argument Signed-off-by: Ole Herman Schumacher Elgesem --- JSON.md | 6 +++--- cfbs/args.py | 6 +++++- cfbs/main.py | 8 ++++++++ cfbs/pretty.py | 1 + cfbs/validate.py | 47 +++++++++++++++++++++++++++++++++-------------- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/JSON.md b/JSON.md index d4eaed2b..be2e599a 100644 --- a/JSON.md +++ b/JSON.md @@ -368,7 +368,7 @@ Which results in: You can start a project with an alternate index: ``` -cfbs init --index blah +cfbs init --index ./some-index.json ``` ```json @@ -376,12 +376,12 @@ cfbs init --index blah "name": "Example", "description": "Example description", "type": "policy-set", - "index": "blah", + "index": "./some-index.json", "build": [] } ``` -`blah` can be a URL or a relative file path (inside project). +The `--index` argument can either be a relative path to a JSON, as seen above, or a HTTPS URL. ## Index file diff --git a/cfbs/args.py b/cfbs/args.py index 7f85db4d..edd4bd87 100644 --- a/cfbs/args.py +++ b/cfbs/args.py @@ -94,7 +94,11 @@ def get_arg_parser(): help="Don't prompt, use defaults (only for testing)", action="store_true", ) - parser.add_argument("--index", help="Specify alternate index", type=str) + parser.add_argument( + "--index", + help="Specify alternate index (HTTPS URL or relative path to JSON file)", + type=str, + ) parser.add_argument( "--check", help="Check if file(s) would be reformatted", action="store_true" ) diff --git a/cfbs/main.py b/cfbs/main.py index 2efbae59..4506a37d 100644 --- a/cfbs/main.py +++ b/cfbs/main.py @@ -9,6 +9,7 @@ from typing import Union from cfbs.result import Result +from cfbs.validate import validate_index_string from cfbs.version import string as version from cfbs.utils import ( CFBSValidationError, @@ -75,6 +76,13 @@ def _main() -> Union[int, Result]: print_help() raise CFBSUserError("Command '%s' not found" % args.command) + if args.index is not None: + if args.command not in ["init", "add", "search", "validate"]: + raise CFBSUserError( + "'--index' option can not be used with the '%s' command" % args.command + ) + validate_index_string(args.index) + if args.masterfiles and args.command != "init": raise CFBSUserError( "The option --masterfiles is only for 'cfbs init', not 'cfbs %s'" diff --git a/cfbs/pretty.py b/cfbs/pretty.py index 4633ed5b..588afeb6 100644 --- a/cfbs/pretty.py +++ b/cfbs/pretty.py @@ -15,6 +15,7 @@ "repo", "url", "by", + "index", "version", "commit", "subdirectory", diff --git a/cfbs/validate.py b/cfbs/validate.py index eb11be86..51cec415 100644 --- a/cfbs/validate.py +++ b/cfbs/validate.py @@ -58,6 +58,25 @@ MAX_BUILD_STEP_LENGTH = 256 +def validate_index_string(index): + assert type(index) is str + if index.strip() == "": + raise CFBSValidationError( + 'The "index" string must be a URL / path (string), not "%s" (whitespace)' + % index + ) + if not index.endswith(".json"): + raise CFBSValidationError( + 'The "index" string must refer to a JSON file / URL (ending in .json)' + ) + if not index.startswith(("https://", "./")): + raise CFBSValidationError( + 'The "index" string must be a URL (starting with https://) or relative path (starting with ./)' + ) + if index.startswith("https://") and " " in index: + raise CFBSValidationError('The "index" URL must not contain spaces') + + def split_build_step(command) -> Tuple[str, List[str]]: terms = command.split(" ") operation, args = terms[0], terms[1:] @@ -131,20 +150,8 @@ def _validate_top_level_keys(config): raise CFBSValidationError( 'The "index" field must either be a URL / path (string) or an inline index (object / dictionary)' ) - if type(index) is str and index.strip() == "": - raise CFBSValidationError( - 'The "index" string must be a URL / path (string), not "%s"' % index - ) - if type(index) is str and not index.endswith(".json"): - raise CFBSValidationError( - 'The "index" string must refer to a JSON file / URL (ending in .json)' - ) - if type(index) is str and not index.startswith(("https://", "./")): - raise CFBSValidationError( - 'The "index" string must be a URL (starting with https://) or relative path (starting with ./)' - ) - if type(index) is str and index.startswith("https://") and " " in index: - raise CFBSValidationError('The "index" URL must not contain spaces') + if type(index) is str: + validate_index_string(index) if "provides" in config: if type(config["provides"]) not in (dict, OrderedDict): @@ -438,6 +445,16 @@ def validate_dependencies(name, module, config, context): name, '"dependencies" cannot reference an alias' ) + def validate_index(name, module): + assert "index" in module + if type(module["version"]) is not str: + raise CFBSValidationError(name, '"index" in "%s" must be a string' % name) + try: + validate_index_string(module["index"]) + except CFBSValidationError as e: + msg = str(e) + " (in module '%s')" % name + raise CFBSValidationError(msg) + def validate_version(name, module): assert "version" in module if type(module["version"]) is not str: @@ -661,6 +678,8 @@ def validate_module_input(name, module): validate_by(name, module) if "dependencies" in module: validate_dependencies(name, module, config, context) + if "index" in module: + validate_index(name, module) if "version" in module: validate_version(name, module) if "commit" in module: From b06304f8f12262072125fd414170a3145b70d274 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 00:27:53 +0200 Subject: [PATCH 2/7] validate.py: Refactored out nested function definitions Our linters say complexity is too high, and I agree. Signed-off-by: Ole Herman Schumacher Elgesem --- cfbs/validate.py | 524 ++++++++++++++++++++++++----------------------- 1 file changed, 266 insertions(+), 258 deletions(-) diff --git a/cfbs/validate.py b/cfbs/validate.py index 51cec415..4169ba29 100644 --- a/cfbs/validate.py +++ b/cfbs/validate.py @@ -350,290 +350,298 @@ def validate_build_step(name, module, i, operation, args, strict=False): pass -def _validate_module_object(context, name, module, config): - def validate_alias(name, module, context): - if context == "index": - search_in = ("index",) - elif context == "provides": - search_in = "provides" - else: - raise CFBSValidationError( - name, '"alias" is only allowed inside "index" or "provides"' - ) - assert "alias" in module - if len(module) != 1: +def _validate_module_alias(name, module, context, config): + if context == "index": + search_in = ("index",) + elif context == "provides": + search_in = "provides" + else: + raise CFBSValidationError( + name, '"alias" is only allowed inside "index" or "provides"' + ) + assert "alias" in module + if len(module) != 1: + raise CFBSValidationError(name, '"alias" cannot be used with other attributes') + if type(module["alias"]) is not str: + raise CFBSValidationError(name, '"alias" must be of type string') + if not module["alias"]: + raise CFBSValidationError(name, '"alias" must be non-empty') + validate_module_name_content(name) + if not config.can_reach_dependency(module["alias"], search_in): + raise CFBSValidationError( + name, '"alias" must reference another module in the index' + ) + if "alias" in config.find_module(module["alias"], search_in): + raise CFBSValidationError(name, '"alias" cannot reference another alias') + + +def _validate_module_name(name, module): + assert "name" in module + assert name == module["name"] + if type(module["name"]) is not str: + raise CFBSValidationError(name, '"name" must be of type string') + if not module["name"]: + raise CFBSValidationError(name, '"name" must be non-empty') + + validate_module_name_content(name) + + +def _validate_module_description(name, module): + assert "description" in module + if type(module["description"]) is not str: + raise CFBSValidationError(name, '"description" must be of type string') + if not module["description"]: + raise CFBSValidationError(name, '"description" must be non-empty') + + +def _validate_module_tags(name, module): + assert "tags" in module + if type(module["tags"]) is not list: + raise CFBSValidationError(name, '"tags" must be of type list') + for tag in module["tags"]: + if type(tag) is not str: + raise CFBSValidationError(name, '"tags" must be a list of strings') + + +def _validate_module_repo(name, module): + assert "repo" in module + if type(module["repo"]) is not str: + raise CFBSValidationError(name, '"repo" must be of type string') + if not module["repo"]: + raise CFBSValidationError(name, '"repo" must be non-empty') + + +def _validate_module_by(name, module): + assert "by" in module + if type(module["by"]) is not str: + raise CFBSValidationError(name, '"by" must be of type string') + if not module["by"]: + raise CFBSValidationError(name, '"by" must be non-empty') + + +def _validate_module_dependencies(name, module, config, context): + if context == "build": + search_in = ("build",) + elif context == "provides": + search_in = ("index", "provides") + else: + assert context == "index" + search_in = ("index",) + assert "dependencies" in module + if type(module["dependencies"]) is not list: + raise CFBSValidationError( + name, 'Value of attribute "dependencies" must be of type list' + ) + for dependency in module["dependencies"]: + if type(dependency) is not str: + raise CFBSValidationError(name, '"dependencies" must be a list of strings') + if not config.can_reach_dependency(dependency, search_in): raise CFBSValidationError( - name, '"alias" cannot be used with other attributes' + name, + '"dependencies" references a module which could not be found: "%s"' + % dependency, ) - if type(module["alias"]) is not str: - raise CFBSValidationError(name, '"alias" must be of type string') - if not module["alias"]: - raise CFBSValidationError(name, '"alias" must be non-empty') - validate_module_name_content(name) - if not config.can_reach_dependency(module["alias"], search_in): + if "alias" in config.find_module(dependency): + raise CFBSValidationError(name, '"dependencies" cannot reference an alias') + + +def _validate_module_index(name, module): + assert "index" in module + if type(module["version"]) is not str: + raise CFBSValidationError(name, '"index" in "%s" must be a string' % name) + try: + validate_index_string(module["index"]) + except CFBSValidationError as e: + msg = str(e) + " (in module '%s')" % name + raise CFBSValidationError(msg) + + +def _validate_module_version(name, module): + assert "version" in module + if type(module["version"]) is not str: + raise CFBSValidationError(name, '"version" must be of type string') + regex = r"(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-([0-9]+))?" + if re.fullmatch(regex, module["version"]) is None: + raise CFBSValidationError(name, '"version" must match regex %s' % regex) + + +def _validate_module_commit(name, module): + assert "commit" in module + commit = module["commit"] + if type(commit) is not str: + raise CFBSValidationError(name, '"commit" must be of type string') + if not is_a_commit_hash(commit): + raise CFBSValidationError(name, '"commit" must be a commit reference') + + +def _validate_module_subdirectory(name, module): + assert "subdirectory" in module + if type(module["subdirectory"]) is not str: + raise CFBSValidationError(name, '"subdirectory" must be of type string') + if not module["subdirectory"]: + raise CFBSValidationError(name, '"subdirectory" must be non-empty') + if module["subdirectory"].startswith("./"): + raise CFBSValidationError(name, '"subdirectory" must not start with ./') + if module["subdirectory"].startswith("/"): + raise CFBSValidationError( + name, '"subdirectory" must be a relative path, not starting with /' + ) + if " " in module["subdirectory"]: + raise CFBSValidationError(name, '"subdirectory" cannot contain spaces') + if module["subdirectory"].endswith(("/", "/.")): + raise CFBSValidationError(name, '"subdirectory" must not end with / or /.') + + +def _validate_module_steps(name, module): + assert "steps" in module + if type(module["steps"]) is not list: + raise CFBSValidationError(name, '"steps" must be of type list') + if not module["steps"]: + raise CFBSValidationError(name, '"steps" must be non-empty') + for i, step in enumerate(module["steps"]): + if type(step) is not str: + raise CFBSValidationError(name, '"steps" must be a list of strings') + if not step or step.strip() == "": raise CFBSValidationError( - name, '"alias" must reference another module in the index' + name, '"steps" must be a list of non-empty / non-whitespace strings' ) - if "alias" in config.find_module(module["alias"], search_in): - raise CFBSValidationError(name, '"alias" cannot reference another alias') + operation, args = split_build_step(step) + validate_build_step(name, module, i, operation, args) - def validate_name(name, module): - assert "name" in module - assert name == module["name"] - if type(module["name"]) is not str: - raise CFBSValidationError(name, '"name" must be of type string') - if not module["name"]: - raise CFBSValidationError(name, '"name" must be non-empty') - validate_module_name_content(name) +def _validate_module_url_field(name, module, field): + assert field in module + url = module.get(field) + if url and not url.startswith("https://"): + raise CFBSValidationError(name, '"%s" must be an HTTPS URL' % field) - def validate_description(name, module): - assert "description" in module - if type(module["description"]) is not str: - raise CFBSValidationError(name, '"description" must be of type string') - if not module["description"]: - raise CFBSValidationError(name, '"description" must be non-empty') - - def validate_tags(name, module): - assert "tags" in module - if type(module["tags"]) is not list: - raise CFBSValidationError(name, '"tags" must be of type list') - for tag in module["tags"]: - if type(tag) is not str: - raise CFBSValidationError(name, '"tags" must be a list of strings') - - def validate_repo(name, module): - assert "repo" in module - if type(module["repo"]) is not str: - raise CFBSValidationError(name, '"repo" must be of type string') - if not module["repo"]: - raise CFBSValidationError(name, '"repo" must be non-empty') - - def validate_by(name, module): - assert "by" in module - if type(module["by"]) is not str: - raise CFBSValidationError(name, '"by" must be of type string') - if not module["by"]: - raise CFBSValidationError(name, '"by" must be non-empty') - - def validate_dependencies(name, module, config, context): - if context == "build": - search_in = ("build",) - elif context == "provides": - search_in = ("index", "provides") - else: - assert context == "index" - search_in = ("index",) - assert "dependencies" in module - if type(module["dependencies"]) is not list: + +def _validate_module_input(name, module): + assert "input" in module + if type(module["input"]) is not list or not module["input"]: + raise CFBSValidationError( + name, 'The module\'s "input" must be a non-empty array' + ) + + required_string_fields = ["type", "variable", "namespace", "bundle", "label"] + + required_string_fields_subtype = ["type", "label", "question"] + + for input_element in module["input"]: + if type(input_element) not in (dict, OrderedDict) or not input_element: raise CFBSValidationError( - name, 'Value of attribute "dependencies" must be of type list' + name, + 'The module\'s "input" array must consist of non-empty objects (dictionaries)', ) - for dependency in module["dependencies"]: - if type(dependency) is not str: - raise CFBSValidationError( - name, '"dependencies" must be a list of strings' - ) - if not config.can_reach_dependency(dependency, search_in): + for field in required_string_fields: + if field not in input_element: raise CFBSValidationError( name, - '"dependencies" references a module which could not be found: "%s"' - % dependency, + 'The "%s" field is required in module input elements' % field, ) - if "alias" in config.find_module(dependency): + if ( + type(input_element[field]) is not str + or input_element[field].strip() == "" + ): raise CFBSValidationError( - name, '"dependencies" cannot reference an alias' + name, + 'The "%s" field in input elements must be a non-empty / non-whitespace string' + % field, ) - def validate_index(name, module): - assert "index" in module - if type(module["version"]) is not str: - raise CFBSValidationError(name, '"index" in "%s" must be a string' % name) - try: - validate_index_string(module["index"]) - except CFBSValidationError as e: - msg = str(e) + " (in module '%s')" % name - raise CFBSValidationError(msg) - - def validate_version(name, module): - assert "version" in module - if type(module["version"]) is not str: - raise CFBSValidationError(name, '"version" must be of type string') - regex = r"(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-([0-9]+))?" - if re.fullmatch(regex, module["version"]) is None: - raise CFBSValidationError(name, '"version" must match regex %s' % regex) - - def validate_commit(name, module): - assert "commit" in module - commit = module["commit"] - if type(commit) is not str: - raise CFBSValidationError(name, '"commit" must be of type string') - if not is_a_commit_hash(commit): - raise CFBSValidationError(name, '"commit" must be a commit reference') - - def validate_subdirectory(name, module): - assert "subdirectory" in module - if type(module["subdirectory"]) is not str: - raise CFBSValidationError(name, '"subdirectory" must be of type string') - if not module["subdirectory"]: - raise CFBSValidationError(name, '"subdirectory" must be non-empty') - if module["subdirectory"].startswith("./"): - raise CFBSValidationError(name, '"subdirectory" must not start with ./') - if module["subdirectory"].startswith("/"): + if input_element["type"] not in ("string", "list"): raise CFBSValidationError( - name, '"subdirectory" must be a relative path, not starting with /' + name, + 'The input "type" must be "string" or "list", not "%s"' + % input_element["type"], ) - if " " in module["subdirectory"]: - raise CFBSValidationError(name, '"subdirectory" cannot contain spaces') - if module["subdirectory"].endswith(("/", "/.")): - raise CFBSValidationError(name, '"subdirectory" must not end with / or /.') - - def validate_steps(name, module): - assert "steps" in module - if type(module["steps"]) is not list: - raise CFBSValidationError(name, '"steps" must be of type list') - if not module["steps"]: - raise CFBSValidationError(name, '"steps" must be non-empty') - for i, step in enumerate(module["steps"]): - if type(step) is not str: - raise CFBSValidationError(name, '"steps" must be a list of strings') - if not step or step.strip() == "": - raise CFBSValidationError( - name, '"steps" must be a list of non-empty / non-whitespace strings' - ) - operation, args = split_build_step(step) - validate_build_step(name, module, i, operation, args) - - def validate_url_field(name, module, field): - assert field in module - url = module.get(field) - if url and not url.startswith("https://"): - raise CFBSValidationError(name, '"%s" must be an HTTPS URL' % field) - - def validate_module_input(name, module): - assert "input" in module - if type(module["input"]) is not list or not module["input"]: + if not re.fullmatch(r"[a-z_]+", input_element["variable"]): raise CFBSValidationError( - name, 'The module\'s "input" must be a non-empty array' + name, + '"%s" is not an acceptable variable name, must match regex "[a-z_]+"' + % input_element["variable"], + ) + if not re.fullmatch(r"[a-z_][a-z0-9_]+", input_element["namespace"]): + raise CFBSValidationError( + name, + '"%s" is not an acceptable namespace, must match regex "[a-z_][a-z0-9_]+"' + % input_element["namespace"], + ) + if not re.fullmatch(r"[a-z_]+", input_element["bundle"]): + raise CFBSValidationError( + name, + '"%s" is not an acceptable bundle name, must match regex "[a-z_]+"' + % input_element["bundle"], ) - required_string_fields = ["type", "variable", "namespace", "bundle", "label"] - - required_string_fields_subtype = ["type", "label", "question"] - - for input_element in module["input"]: - if type(input_element) not in (dict, OrderedDict) or not input_element: - raise CFBSValidationError( - name, - 'The module\'s "input" array must consist of non-empty objects (dictionaries)', - ) - for field in required_string_fields: - if field not in input_element: - raise CFBSValidationError( - name, - 'The "%s" field is required in module input elements' % field, - ) - if ( - type(input_element[field]) is not str - or input_element[field].strip() == "" - ): - raise CFBSValidationError( - name, - 'The "%s" field in input elements must be a non-empty / non-whitespace string' - % field, - ) - - if input_element["type"] not in ("string", "list"): + if input_element["type"] == "list": + if "while" not in input_element: raise CFBSValidationError( - name, - 'The input "type" must be "string" or "list", not "%s"' - % input_element["type"], + name, 'For a "list" input element, a "while" prompt is required' ) - if not re.fullmatch(r"[a-z_]+", input_element["variable"]): + if ( + type(input_element["while"]) is not str + or not input_element["while"].strip() + ): raise CFBSValidationError( name, - '"%s" is not an acceptable variable name, must match regex "[a-z_]+"' - % input_element["variable"], + 'The "while" prompt in an input "list" element must be a non-empty / non-whitespace string', ) - if not re.fullmatch(r"[a-z_][a-z0-9_]+", input_element["namespace"]): + if "subtype" not in input_element: raise CFBSValidationError( - name, - '"%s" is not an acceptable namespace, must match regex "[a-z_][a-z0-9_]+"' - % input_element["namespace"], + name, 'For a "list" input element, a "subtype" is required' ) - if not re.fullmatch(r"[a-z_]+", input_element["bundle"]): + if type(input_element["subtype"]) not in (list, dict, OrderedDict): raise CFBSValidationError( name, - '"%s" is not an acceptable bundle name, must match regex "[a-z_]+"' - % input_element["bundle"], + 'The list element "subtype" must be an object or an array of objects (dictionaries)', ) - - if input_element["type"] == "list": - if "while" not in input_element: - raise CFBSValidationError( - name, 'For a "list" input element, a "while" prompt is required' - ) - if ( - type(input_element["while"]) is not str - or not input_element["while"].strip() - ): - raise CFBSValidationError( - name, - 'The "while" prompt in an input "list" element must be a non-empty / non-whitespace string', - ) - if "subtype" not in input_element: - raise CFBSValidationError( - name, 'For a "list" input element, a "subtype" is required' - ) - if type(input_element["subtype"]) not in (list, dict, OrderedDict): - raise CFBSValidationError( - name, - 'The list element "subtype" must be an object or an array of objects (dictionaries)', - ) - subtype = input_element["subtype"] - if type(subtype) is not list: - subtype = [subtype] - for part in subtype: - for field in required_string_fields_subtype: - if field not in part: - raise CFBSValidationError( - name, - 'The "%s" field is required in module input "subtype" objects' - % field, - ) - if type(part[field]) is not str or part[field].strip() == "": - raise CFBSValidationError( - name, - 'The "%s" field in module input "subtype" objects must be a non-empty / non-whitespace string' - % field, - ) - if len(subtype) > 1: - # The "key" field is used to create the JSON objects for each - # input in a list of "things" which are not just strings, - # i.e. consist of multiple values - if ( - "key" not in part - or type(part["key"]) is not str - or part["key"].strip() == "" - ): - raise CFBSValidationError( - name, - 'When using module input with type list, and subtype includes multiple values, "key" is required to distinguish them', - ) - if part["type"] != "string": + subtype = input_element["subtype"] + if type(subtype) is not list: + subtype = [subtype] + for part in subtype: + for field in required_string_fields_subtype: + if field not in part: + raise CFBSValidationError( + name, + 'The "%s" field is required in module input "subtype" objects' + % field, + ) + if type(part[field]) is not str or part[field].strip() == "": + raise CFBSValidationError( + name, + 'The "%s" field in module input "subtype" objects must be a non-empty / non-whitespace string' + % field, + ) + if len(subtype) > 1: + # The "key" field is used to create the JSON objects for each + # input in a list of "things" which are not just strings, + # i.e. consist of multiple values + if ( + "key" not in part + or type(part["key"]) is not str + or part["key"].strip() == "" + ): raise CFBSValidationError( name, - 'Only "string" supported for the "type" of module input list elements, not "%s"' - % part["type"], + 'When using module input with type list, and subtype includes multiple values, "key" is required to distinguish them', ) + if part["type"] != "string": + raise CFBSValidationError( + name, + 'Only "string" supported for the "type" of module input list elements, not "%s"' + % part["type"], + ) + +def _validate_module_object(context, name, module, config): assert context in ("index", "provides", "build") # Step 1 - Handle special cases (alias): if "alias" in module: # Needs to be validated first because it's missing the other fields: - validate_alias(name, module, context) + _validate_module_alias(name, module, context, config) return # alias entries would fail the other validation below # Step 2 - Check for required fields: @@ -667,33 +675,33 @@ def validate_module_input(name, module): # Step 3 - Validate fields: if "name" in module: - validate_name(name, module) + _validate_module_name(name, module) if "description" in module: - validate_description(name, module) + _validate_module_description(name, module) if "tags" in module: - validate_tags(name, module) + _validate_module_tags(name, module) if "repo" in module: - validate_repo(name, module) + _validate_module_repo(name, module) if "by" in module: - validate_by(name, module) + _validate_module_by(name, module) if "dependencies" in module: - validate_dependencies(name, module, config, context) + _validate_module_dependencies(name, module, config, context) if "index" in module: - validate_index(name, module) + _validate_module_index(name, module) if "version" in module: - validate_version(name, module) + _validate_module_version(name, module) if "commit" in module: - validate_commit(name, module) + _validate_module_commit(name, module) if "subdirectory" in module: - validate_subdirectory(name, module) + _validate_module_subdirectory(name, module) if "steps" in module: - validate_steps(name, module) + _validate_module_steps(name, module) if "website" in module: - validate_url_field(name, module, "website") + _validate_module_url_field(name, module, "website") if "documentation" in module: - validate_url_field(name, module, "documentation") + _validate_module_url_field(name, module, "documentation") if "input" in module: - validate_module_input(name, module) + _validate_module_input(name, module) # Step 4 - Additional validation checks: From 08326b5098056d4d8a4f75007a49ebcf4166cc1a Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 11:32:47 +0200 Subject: [PATCH 3/7] add_validate_build_all.sh: Fixed typo Signed-off-by: Ole Herman Schumacher Elgesem --- .github/workflows/add_validate_build_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/add_validate_build_all.sh b/.github/workflows/add_validate_build_all.sh index 3e996773..3f90808e 100644 --- a/.github/workflows/add_validate_build_all.sh +++ b/.github/workflows/add_validate_build_all.sh @@ -11,7 +11,7 @@ rm -rf ./tmp/ mkdir -p ./tmp/ # This script is written to also work in for example cfengine/cfbs -# where we'd need to downooad cfbs.json. +# where we'd need to download cfbs.json. if [ ! -f ./cfbs.json ] ; then echo "No cfbs.json found in current working directory, downloading from GitHub" curl -L https://raw.githubusercontent.com/cfengine/build-index/refs/heads/master/cfbs.json -o ./tmp/cfbs.json From 5a3ac51c33594015c424c2f8759b3d00d559db36 Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 11:33:46 +0200 Subject: [PATCH 4/7] add_validate_build_all.yaml: Bumped actions/checkout to v4 Signed-off-by: Ole Herman Schumacher Elgesem --- .github/workflows/add_validate_build_all.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/add_validate_build_all.yaml b/.github/workflows/add_validate_build_all.yaml index d0773d2e..b5808d65 100644 --- a/.github/workflows/add_validate_build_all.yaml +++ b/.github/workflows/add_validate_build_all.yaml @@ -22,7 +22,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install cfbs with pip run: pip3 install cfbs From f14606bb35d12eafd904b046f7c29f88f76e15be Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 11:34:41 +0200 Subject: [PATCH 5/7] Renamed .yaml file to .yml for consistency Signed-off-by: Ole Herman Schumacher Elgesem --- .../{add_validate_build_all.yaml => add_validate_build_all.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{add_validate_build_all.yaml => add_validate_build_all.yml} (100%) diff --git a/.github/workflows/add_validate_build_all.yaml b/.github/workflows/add_validate_build_all.yml similarity index 100% rename from .github/workflows/add_validate_build_all.yaml rename to .github/workflows/add_validate_build_all.yml From 0cf8e5ed452dc0acf13d8efad9a2c483aab358ff Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 12:08:21 +0200 Subject: [PATCH 6/7] Fixed wrong key used for checking type of index Signed-off-by: Ole Herman Schumacher Elgesem --- cfbs/validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfbs/validate.py b/cfbs/validate.py index 4169ba29..f4f011ad 100644 --- a/cfbs/validate.py +++ b/cfbs/validate.py @@ -447,7 +447,7 @@ def _validate_module_dependencies(name, module, config, context): def _validate_module_index(name, module): assert "index" in module - if type(module["version"]) is not str: + if type(module["index"]) is not str: raise CFBSValidationError(name, '"index" in "%s" must be a string' % name) try: validate_index_string(module["index"]) From 56195d6e9ab93218dfc0eff9ab6e5f7949bc3d5c Mon Sep 17 00:00:00 2001 From: Ole Herman Schumacher Elgesem Date: Wed, 16 Jul 2025 12:10:22 +0200 Subject: [PATCH 7/7] validate.py: Removed main() and CFBSConfig import I don't think anyone is using the main function here, and getting rid of the import is nice to avoid potential circular imports in the future. It's nice if validate can easily be imported and used from "everywhere". Signed-off-by: Ole Herman Schumacher Elgesem --- cfbs/validate.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/cfbs/validate.py b/cfbs/validate.py index f4f011ad..5a3eae82 100644 --- a/cfbs/validate.py +++ b/cfbs/validate.py @@ -19,9 +19,7 @@ not in build.py. """ -import argparse import logging as log -import sys import re from collections import OrderedDict from typing import List, Tuple @@ -35,7 +33,6 @@ CFBSValidationError, ) from cfbs.pretty import TOP_LEVEL_KEYS, MODULE_KEYS -from cfbs.cfbs_config import CFBSConfig AVAILABLE_BUILD_STEPS = { "copy": 2, @@ -730,18 +727,3 @@ def _validate_config_for_build_field(config, empty_build_list_ok=False): raise CFBSExitError( "The \"build\" field in ./cfbs.json is empty - add modules with 'cfbs add'" ) - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("file", nargs="?", default="./cfbs.json") - args = parser.parse_args() - - config = CFBSConfig.get_instance(filename=args.file, non_interactive=True) - r = validate_config(config) - - sys.exit(r) - - -if __name__ == "__main__": - main()