def remove_command(to_remove: list): config = CFBSConfig.get_instance() modules = config["build"] def _get_module_by_name(name) -> dict: if not name.startswith("./") and name.endswith( ".cf") and os.path.exists(name): name = "./" + name for module in modules: if module["name"] == name: return module return None def _get_modules_by_url(name) -> list: r = [] for module in modules: if "url" in module and module["url"] == name: r.append(module) return r num_removed = 0 for name in to_remove: if name.startswith(("https://", "ssh://", "git://")): matches = _get_modules_by_url(name) if not matches: user_error("Could not find module with URL '%s'" % name) for module in matches: answer = prompt_user( "Do you wish to remove '%s'?" % module["name"], choices=YES_NO_CHOICES, default="yes", ) if answer.lower() in ("yes", "y"): print("Removing module '%s'" % module["name"]) modules.remove(module) num_removed += 1 else: module = _get_module_by_name(name) if module: print("Removing module '%s'" % name) modules.remove(module) num_removed += 1 else: print("Module '%s' not found" % name) config.save() if num_removed: try: _clean_unused_modules(config) except CFBSReturnWithoutCommit: pass return 0 else: raise CFBSReturnWithoutCommit(0)
def _clean_unused_modules(config=None): if not config: config = CFBSConfig.get_instance() modules = config["build"] def _someone_needs_me(this) -> bool: if "added_by" not in this or this["added_by"] == "cfbs add": return True for other in modules: if not "dependencies" in other: continue if this["name"] in other["dependencies"]: return _someone_needs_me(other) return False to_remove = list() for module in modules: if not _someone_needs_me(module): to_remove.append(module) if not to_remove: raise CFBSReturnWithoutCommit(0) print( "The following modules were added as dependencies but are no longer needed:" ) for module in to_remove: name = module["name"] if "name" in module else "" description = module["description"] if "description" in module else "" added_by = module["added_by"] if "added_by" in module else "" print("%s - %s - added by: %s" % (name, description, added_by)) answer = prompt_user("Do you wish to remove these modules?", choices=YES_NO_CHOICES, default="yes") if answer.lower() in ("yes", "y"): for module in to_remove: modules.remove(module) config.save() return 0
def git_commit_maybe_prompt(commit_msg, non_interactive, scope="all"): edit_commit_msg = False args = get_args() # Override message if --git-commit-message option is used if args.git_commit_message: global first_commit if first_commit: commit_msg = args.git_commit_message non_interactive = True first_commit = False else: log.warning( "Commit message specified, but command produced multiple commits, using default commit message" ) if not non_interactive: prompt = "The default commit message is '{}' - edit it?".format( commit_msg) if "\n" in commit_msg: prompt = "The default commit message is:\n\n" for line in commit_msg.split("\n"): prompt += "\n" if line == "" else "\t" + line + "\n" prompt += "\nEdit it?" ans = prompt_user( prompt, choices=YES_NO_CHOICES, default="no", ) edit_commit_msg = ans.lower() in ("yes", "y") git_commit( commit_msg, edit_commit_msg, args.git_user_name, args.git_user_email, scope, )
def update_command(to_update): config = CFBSConfig.get_instance() index = config.index if to_update: to_update = [Module(m) for m in to_update] index.translate_aliases(to_update) skip = (m for m in to_update if all(n["name"] != m.name for n in config["build"])) for m in skip: log.warning("Module '%s' not in build. Skipping its update.", m.name) to_update.remove(m) else: # Update all modules in build if no modules are specified to_update = [Module(m["name"]) for m in config["build"]] new_deps = [] new_deps_added_by = dict() changes_made = False msg = "" updated = [] for update in to_update: module = config.get_module_from_build(update.name) assert module is not None # Checked above when logging skipped modules if "version" not in module: print("Module '%s' not updatable" % module["name"]) continue old_version = module["version"] if "index" in module: # TODO: Support custom index log.warning( "Module '%s' is not from the default index. " + "Updating from custom index is currently not supported. " + "Skipping its update.", module["name"], ) continue index_info = index.get_module_object(update) if not index_info: log.warning( "Module '%s' not present in the index, cannot update it", module["name"]) continue if (module["version"] != index_info["version"] and module["commit"] == index_info["commit"]): log.warning( "Version and commit mismatch detected." + " The module %s has the same commit but different version" + " locally (%s) and in the index (%s)." + " Skipping its update.", module["name"], module["version"], index_info["version"], ) continue local_ver = [ int(version_number) for version_number in re.split("[-\.]", module["version"]) ] index_ver = [ int(version_number) for version_number in re.split("[-\.]", index_info["version"]) ] if local_ver == index_ver: continue elif local_ver > index_ver: log.warning( "The requested version of module '%s' is older than current version (%s < %s)." " Skipping its update.", module["name"], index_info["version"], module["version"], ) continue commit_differs = module["commit"] != index_info["commit"] for key in module.keys(): if key not in index_info or module[key] == index_info[key]: continue if key == "steps": # same commit => user modifications, don't revert them if commit_differs: ans = prompt_user( "Module %s has different build steps now\n" % module["name"] + "old steps: %s\n" % module["steps"] + "new steps: %s\n" % index_info["steps"] + "Do you want to use the new build steps?", choices=YES_NO_CHOICES, default="yes", ) if ans.lower() in ["y", "yes"]: module["steps"] = index_info["steps"] else: print("Please make sure the old build steps work" + " with the new version of the module") else: if key == "dependencies": extra = set(index_info["dependencies"]) - set( module["dependencies"]) new_deps.extend(extra) new_deps_added_by.update( {item: module["name"] for item in extra}) module[key] = index_info[key] changes_made = True # add new items for key in set(index_info.keys()) - set(module.keys()): module[key] = index_info[key] if key == "dependencies": extra = index_info["dependencies"] new_deps.extend(extra) new_deps_added_by.update( {item: module["name"] for item in extra}) if not update.version: update.version = index_info["version"] updated.append(update) msg += "\n - Updated module '%s' from version %s to version %s" % ( update.name, old_version, update.version, ) if new_deps: objects = [ index.get_module_object(d, new_deps_added_by[d]) for d in new_deps ] config.add_with_dependencies(objects) config.save() if changes_made: if len(updated) > 1: msg = "Updated %d modules\n" % len(updated) + msg else: assert updated msg = msg[4:] # Remove the '\n - ' part of the message print("%s\n" % msg) else: print("Modules are already up to date") return Result(0, changes_made, msg)
def init_command(index=None, non_interactive=False) -> int: if is_cfbs_repo(): user_error("Already initialized - look at %s" % cfbs_filename()) name = prompt_user("Please enter name of this CFBS repository", default="Example") description = prompt_user( "Please enter description of this CFBS repository", default="Example description", ) config = { "name": name, "type": "policy-set", # TODO: Prompt whether user wants to make a module "description": description, "build": [], } if index: config["index"] = index do_git = get_args().git is_git = is_git_repo() if do_git is None: if is_git: git_ans = prompt_user( "This is a git repository. Do you want cfbs to make commits to it?", choices=YES_NO_CHOICES, default="yes", ) else: git_ans = prompt_user( "Do you want cfbs to initialize a git repository and make commits to it?", choices=YES_NO_CHOICES, default="yes", ) do_git = git_ans.lower() in ("yes", "y") else: assert do_git in ("yes", "no") do_git = True if do_git == "yes" else False if do_git is True: user_name = get_args().git_user_name if not user_name: user_name = git_get_config("user.name") user_name = prompt_user( "Please enter user name to use for git commits", default=user_name or "cfbs", ) user_email = get_args().git_user_email if not user_email: user_email = git_get_config("user.email") node_name = os.uname().nodename user_email = prompt_user( "Please enter user email to use for git commits", default=user_email or ("cfbs@%s" % node_name), ) if not is_git: try: git_init(user_name, user_email, description) except CFBSGitError as e: print(str(e)) return 1 else: if not git_set_config("user.name", user_name) or not git_set_config( "user.email", user_email): print("Failed to set Git user name and email") return 1 config["git"] = do_git write_json(cfbs_filename(), config) assert is_cfbs_repo() if do_git: try: git_commit_maybe_prompt( "Initialized a new CFEngine Build project", non_interactive, [cfbs_filename()], ) except CFBSGitError as e: print(str(e)) os.unlink(cfbs_filename()) return 1 print("Initialized an empty project called '{}' in '{}'".format( name, cfbs_filename())) """ The CFBSConfig instance was initally created in main(). Back then cfbs.json did not exist, thus the instance is empty. Ensure it is reloaded now that the JSON exists. """ CFBSConfig.reload() if prompt_user( "Do you wish to build on top of the default policy set, masterfiles? (Recommended)", choices=YES_NO_CHOICES, default="yes", ) in ("yes", "y"): to_add = "masterfiles" else: to_add = prompt_user( "Specify policy set to use instead (empty to skip)", default="") if to_add: return add_command([to_add]) return 0