Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
68 changes: 47 additions & 21 deletions cfbs/cfbs_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import glob
import logging as log
from collections import OrderedDict
from typing import List
from typing import List, Optional

from cfbs.types import CFBSCommandGitResult
from cfbs.utils import (
Expand All @@ -37,7 +37,7 @@
)
from cfbs.pretty import pretty, CFBS_DEFAULT_SORTING_RULES
from cfbs.cfbs_json import CFBSJson
from cfbs.module import Module, is_module_added_manually
from cfbs.module import Module, is_module_added_manually, is_module_local
from cfbs.prompts import prompt_user, prompt_user_yesno
from cfbs.validate import validate_single_module

Expand Down Expand Up @@ -106,17 +106,15 @@ def longest_module_key_length(self, key) -> int:
else 0
)

def add_with_dependencies(
self, module, remote_config=None, str_added_by="cfbs add"
):
def add_with_dependencies(self, module, remote_config=None, added_by="cfbs add"):
if type(module) is list:
# TODO: reuse logic from _add_modules instead
for m in module:
self.add_with_dependencies(m, remote_config, str_added_by)
self.add_with_dependencies(m, remote_config, added_by)
return
if type(module) is str:
module_str = module
module = (remote_config or self).get_module_for_build(module, str_added_by)
module = (remote_config or self).get_module_for_build(module, added_by)
if not module:
raise CFBSExitError("Module '%s' not found" % module_str)
if not module:
Expand All @@ -125,7 +123,7 @@ def add_with_dependencies(
name = module["name"]
assert "steps" in module
if self._module_is_in_build(module):
if is_module_added_manually(str_added_by):
if is_module_added_manually(added_by):
print("Skipping already added module '%s'" % name)
return
if "dependencies" in module:
Expand All @@ -145,10 +143,11 @@ def add_with_dependencies(

def _add_using_url(
self,
url,
url: str,
to_add: list,
added_by="cfbs add",
checksum=None,
explicit_build_steps=None,
):
url_commit = None
if url.endswith(SUPPORTED_ARCHIVES):
Expand Down Expand Up @@ -195,7 +194,7 @@ def _add_using_url(
% (i, len(modules), module["name"]),
):
continue
self.add_with_dependencies(module, remote_config, str_added_by=added_by)
self.add_with_dependencies(module, remote_config, added_by)

@staticmethod
def _convert_added_by(added_by, to_add):
Expand Down Expand Up @@ -297,7 +296,7 @@ def _add_bundles_build_step(self, module, policy_files):
module["steps"].append(step)
log.debug("Added build step '%s' for module '%s'" % (step, name))

def _handle_local_module(self, module):
def _handle_local_module(self, module, use_default_build_steps=True):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's necessary to switch from explicit_build_steps to use_default_build_steps. Just use explicit_build_steps all the way down where needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual build step values would be unused then, there would only be a check whether they're None or not. That would read weird. Dunno.

name = module["name"]
if not (
name.startswith("./")
Expand Down Expand Up @@ -329,10 +328,17 @@ def _handle_local_module(self, module):
)
# TODO: Support adding local modules with autorun tag

self._add_policy_files_build_step(module)
self._add_bundles_build_step(module, policy_files)
if use_default_build_steps:
self._add_policy_files_build_step(module)
self._add_bundles_build_step(module, policy_files)
Comment on lines +331 to +333
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaves the module object in a "half done" state, I think one of these would be preferable:

  1. Just add the defaults, and you can overwrite module["steps"] in the calling code.
  2. Or, send explicit_build_steps all the way down to this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaves the module object in a "half done" state

I don't see how that would be the case after and not be the case before. The argument to this function is a Module object, which at the time of entering this function will already have some build steps from when it was constructed (and that construction will now use explicit_build_steps, too, so all the required steps should be there already).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, are these methods the ones which prompt user about bundle and policy files? If so, we should add a comment about that here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, those are the methods. I still don't see what you would ideally like to see here, so feel free to suggest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like _handle_local_module() is not really needed for our case? Maybe a better approach is to clarify it a bit (give it a better name / docstring), and avoid calling it in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I found this function a bit strange/unclear (e.g. how it appears in a general _add_without_dependencies function) at first. The current code is setting build steps in a few separate places, maybe there could be improvements, I avoided changing the structure for now, since optional params in existing places could do the job.


def _add_without_dependencies(self, modules, use_default_build_steps=True):
"""Note: `use_default_build_steps` is only relevant for local modules."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring note could be replaced by an assert.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say the note is for the users of the function, for them to learn what the function parameter does. This is why it's in the docstring. An assert is only readable inside of the function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could have both. An assert is much more important IMO, and less likely to go stale.

if not use_default_build_steps:
assert len(modules) == 1 and modules[0]["name"].startswith(
"./"
), "`use_default_build_steps` is currently only expected to be explicitly used for adding a single local module"

def _add_without_dependencies(self, modules):
assert modules
assert len(modules) > 0
assert modules[0]["name"]
Expand All @@ -350,7 +356,7 @@ def _add_without_dependencies(self, modules):
context="build", name=name, module=module, config=None, local_check=True
)
self["build"].append(module)
self._handle_local_module(module)
self._handle_local_module(module, use_default_build_steps)

assert "added_by" in module
added_by = module["added_by"]
Expand All @@ -364,7 +370,17 @@ def _add_modules(
to_add: list,
added_by="cfbs add",
checksum=None,
) -> int:
explicit_build_steps: Optional[List[str]] = None,
):
"""Note: explicit build steps are currently limited to single local modules without dependencies, see the asserts."""
if explicit_build_steps is not None:
assert (
len(to_add) == 1
), "explicit_build_steps is only for adding a single module"
assert is_module_local(
to_add[0]
), "explicit_build_steps is only for adding a local module"

index = self.index

modules = [Module(m) for m in to_add]
Expand All @@ -380,10 +396,14 @@ def _add_modules(
# Filter modules which are already added:
to_add = self._filter_modules_to_add(modules)
if not to_add:
return 0 # Everything already added
# Everything already added
return

# Convert names to objects:
modules_to_add = [index.get_module_object(m, added_by[m.name]) for m in to_add]
modules_to_add = [
index.get_module_object(m, added_by[m.name], explicit_build_steps)
for m in to_add
]
modules_already_added = self["build"]

assert not any(m for m in modules_to_add if "name" not in m)
Expand All @@ -393,16 +413,21 @@ def _add_modules(
dependencies = self._find_dependencies(modules_to_add, modules_already_added)

if dependencies:
assert (
explicit_build_steps is None
), "explicit build steps do not apply to dependencies"
self._add_without_dependencies(dependencies)

self._add_without_dependencies(modules_to_add)
return 0
self._add_without_dependencies(
modules_to_add, use_default_build_steps=explicit_build_steps is None
)

def add_command(
self,
to_add: List[str],
added_by="cfbs add",
checksum=None,
explicit_build_steps: Optional[List[str]] = None,
) -> CFBSCommandGitResult:
if not to_add:
raise CFBSUserError("Must specify at least one module to add")
Expand All @@ -417,6 +442,7 @@ def add_command(
to_add=to_add[1:],
added_by=added_by,
checksum=checksum,
explicit_build_steps=explicit_build_steps,
)
else:
# for this `if` to be valid, module names containing `://` should be illegal
Expand All @@ -425,7 +451,7 @@ def add_command(
"URI scheme not supported. The supported URI schemes are: "
+ ", ".join(SUPPORTED_URI_SCHEMES)
)
self._add_modules(to_add, added_by, checksum)
self._add_modules(to_add, added_by, checksum, explicit_build_steps)

added = {m["name"] for m in self["build"]}.difference(before)

Expand Down
5 changes: 3 additions & 2 deletions cfbs/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def search_command(terms: List[str]):
import logging as log
import json
import functools
from typing import Callable, List, Union
from typing import Callable, List, Optional, Union
from collections import OrderedDict
from cfbs.analyze import analyze_policyset
from cfbs.args import get_args
Expand Down Expand Up @@ -399,10 +399,11 @@ def add_command(
to_add: List[str],
added_by="cfbs add",
checksum=None,
explicit_build_steps: Optional[List[str]] = None,
):
config = CFBSConfig.get_instance()
validate_config_raise_exceptions(config, empty_build_list_ok=True)
r = config.add_command(to_add, added_by, checksum)
r = config.add_command(to_add, added_by, checksum, explicit_build_steps)
config.save()
return r

Expand Down
59 changes: 36 additions & 23 deletions cfbs/index.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
import os
from collections import OrderedDict
from typing import Union
from typing import List, Optional, Union

from cfbs.module import Module
from cfbs.utils import CFBSNetworkError, get_or_read_json, CFBSExitError, get_json
Expand All @@ -15,48 +15,56 @@
)


def _local_module_data_cf_file(module):
dst = os.path.join("services", "cfbs", module[2:])
def _local_module_data_cf_file(module_name: str):
dst = os.path.join("services", "cfbs", module_name[2:])
return {
"description": "Local policy file added using cfbs command line",
"tags": ["local"],
"steps": ["copy %s %s" % (module, dst)],
"steps": ["copy %s %s" % (module_name, dst)],
"added_by": "cfbs add",
}


def _local_module_data_json_file(module):
def _local_module_data_json_file(module_name: str):
return {
"description": "Local augments file added using cfbs command line",
"tags": ["local"],
"steps": ["json %s def.json" % module],
"steps": ["json %s def.json" % module_name],
"added_by": "cfbs add",
}


def _local_module_data_subdir(module):
assert module.startswith("./")
assert module.endswith(("/", "/."))
dst = os.path.join("services", "cfbs", module[2:])
def _local_module_data_subdir(
module_name: str, explicit_build_steps: Optional[List[str]] = None
):
assert module_name.startswith("./")
assert module_name.endswith(("/", "/."))
dst = os.path.join("services", "cfbs", module_name[2:])
if explicit_build_steps is None:
build_steps = ["directory ./ {}".format(dst)]
else:
build_steps = explicit_build_steps
return {
"description": "Local subdirectory added using cfbs command line",
"tags": ["local"],
"steps": ["directory ./ {}".format(dst)],
"steps": build_steps,
"added_by": "cfbs add",
}


def _generate_local_module_object(module):
assert module.startswith("./")
assert module.endswith((".cf", ".json", "/"))
assert os.path.isfile(module) or os.path.isdir(module)
def _generate_local_module_object(
module_name: str, explicit_build_steps: Optional[List[str]] = None
):
assert module_name.startswith("./")
assert module_name.endswith((".cf", ".json", "/"))
assert os.path.isfile(module_name) or os.path.isdir(module_name)

if os.path.isdir(module):
return _local_module_data_subdir(module)
if module.endswith(".cf"):
return _local_module_data_cf_file(module)
if module.endswith(".json"):
return _local_module_data_json_file(module)
if os.path.isdir(module_name):
return _local_module_data_subdir(module_name, explicit_build_steps)
if module_name.endswith(".cf"):
return _local_module_data_cf_file(module_name)
if module_name.endswith(".json"):
return _local_module_data_json_file(module_name)


class Index:
Expand Down Expand Up @@ -165,15 +173,20 @@ def translate_alias(self, module: Module):
if os.path.exists(module.name):
module.name = local_module_name(module.name)

def get_module_object(self, module, added_by=None):
def get_module_object(
self,
module,
added_by: Optional[str] = None,
explicit_build_steps: Optional[List[str]] = None,
):
if isinstance(module, str):
module = Module(module)
name = module.name
version = module.version
module = module.to_dict()

if name.startswith("./"):
object = _generate_local_module_object(name)
object = _generate_local_module_object(name, explicit_build_steps)
else:
object = self[name]
if version:
Expand Down
4 changes: 2 additions & 2 deletions cfbs/internal_file_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def _clone_and_checkout(url, path, commit):
sh("git checkout " + commit, directory=path)


def clone_url_repo(repo_url):
def clone_url_repo(repo_url: str):
assert repo_url.startswith(SUPPORTED_URI_SCHEMES)

commit = None
Expand Down Expand Up @@ -196,7 +196,7 @@ def clone_url_repo(repo_url):


def fetch_archive(
url, checksum=None, directory=None, with_index=True, extract_to_directory=False
url: str, checksum=None, directory=None, with_index=True, extract_to_directory=False
):
assert url.endswith(SUPPORTED_ARCHIVES)

Expand Down