package-manager-2.0.7/0000755000175000017500000000000013551142424014072 5ustar rhaistrhaistpackage-manager-2.0.7/bro-pkg0000755000175000017500000000103213551142424015355 0ustar rhaistrhaist#! /usr/bin/env bash # Wrapper to continue supporting the old `bro-pkg` executable name. # # This will print a deprecation warning to stderr if (1) stdin/stdout/stderr # are all connected to a tty, and (2) the environment variable ZEEK_IS_BRO # is unset. function deprecated { cat >&2 <` class. This package provides a logger named `LOG` to which logging stream handlers may be added in order to help log/debug applications. """ import logging __version__ = "2.0.7" __all__ = ['manager', 'package', 'source'] LOG = logging.getLogger(__name__) LOG.addHandler(logging.NullHandler()) from .manager import * from .package import * from .source import * package-manager-2.0.7/zeekpkg/_util.py0000644000175000017500000001106513551142424017223 0ustar rhaistrhaist""" These are meant to be private utility methods for internal use. """ import os import sys import errno import shutil import git def make_dir(path): """Create a directory or do nothing if it already exists. Raises: OSError: if directory cannot be created """ try: os.makedirs(path) except OSError as exception: if exception.errno != errno.EEXIST: raise elif os.path.isfile(path): raise def remove_trailing_slashes(path): if path.endswith('/'): return remove_trailing_slashes(path[:-1]) return path def delete_path(path): if os.path.islink(path): os.remove(path) return if not os.path.exists(path): return if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) def copy_over_path(src, dst, ignore=None): delete_path(dst) shutil.copytree(src, dst, symlinks=True, ignore=ignore) def make_symlink(target_path, link_path, force=True): try: os.symlink(target_path, link_path) except OSError as error: if error.errno == errno.EEXIST and force and os.path.islink(link_path): os.remove(link_path) os.symlink(target_path, link_path) else: raise error def find_sentence_end(s): beg = 0 while True: period_idx = s.find('.', beg) if period_idx == -1: return -1 if period_idx == len(s) - 1: return period_idx next_char = s[period_idx + 1] if next_char.isspace(): return period_idx beg = period_idx + 1 def git_clone(git_url, dst_path, shallow=False): if shallow: try: git.Git().clone(git_url, dst_path, '--no-single-branch', recursive=True, depth=1) except git.exc.GitCommandError: if not git_url.startswith('.') and not git_url.startswith('/'): # Not a local repo raise if not os.path.exists(os.path.join(git_url, '.git', 'shallow')): raise # Some git versions cannot clone from a shallow-clone, so copy # and reset/clean it to a pristine condition. copy_over_path(git_url, dst_path) rval = git.Repo(dst_path) rval.git.reset('--hard') rval.git.clean('-ffdx') else: git.Git().clone(git_url, dst_path, recursive=True) rval = git.Repo(dst_path) # This setting of the "origin" remote will be a no-op in most cases, but # for some reason, when cloning from a local directory, the clone may # inherit the "origin" instead of using the local directory as its new # "origin". This is bad in some cases since we do not want to try # fetching from a remote location (e.g. when unbundling). This # unintended inheritence of "origin" seems to only happen when cloning a # local git repo that has submodules ? rval.git.remote('set-url', 'origin', git_url) rval.git.fetch('--no-recurse-submodules', tags=True) return rval def is_sha1(s): if not s: return False; if len(s) != 40: return False for c in s: if c not in {'a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}: return False return True def is_exe(path): return os.path.isfile(path) and os.access(path, os.X_OK) def find_program(prog_name): path, _ = os.path.split(prog_name) if path: return prog_name if is_exe(prog_name) else '' for path in os.environ["PATH"].split(os.pathsep): path = os.path.join(path.strip('"'), prog_name) if is_exe(path): return path return '' def std_encoding(stream): if stream.encoding: return stream.encoding import locale if locale.getdefaultlocale()[1] is None: return 'utf-8' return locale.getpreferredencoding() def read_zeek_config_line(stdout): rval = stdout.readline() # Python 2 returned bytes, Python 3 returned unicode if isinstance(rval, bytes): rval = rval.decode(std_encoding(sys.stdout)) return rval.strip() def get_zeek_version(): zeek_config = find_program('zeek-config') if not zeek_config: zeek_config = find_program('bro-config') if not zeek_config: return '' import subprocess cmd = subprocess.Popen([zeek_config, '--version'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) return read_zeek_config_line(cmd.stdout) package-manager-2.0.7/zeekpkg/manager.py0000644000175000017500000030265013551142424017524 0ustar rhaistrhaist""" A module defining the main Zeek Package Manager interface which supplies methods to interact with and operate on Zeek packages. """ import os import sys import copy import json import shutil import filecmp import tarfile import subprocess try: from backports import configparser except ImportError as err: import configparser if ( sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 2) ): from configparser import SafeConfigParser as GoodConfigParser else: # SafeConfigParser renamed to ConfigParser in Python >= 3.2 from configparser import ConfigParser as GoodConfigParser import git import semantic_version as semver from ._util import ( make_dir, delete_path, make_symlink, copy_over_path, git_clone, is_sha1, get_zeek_version, std_encoding, find_program, read_zeek_config_line, ) from .source import ( AGGREGATE_DATA_FILE, Source ) from .package import ( METADATA_FILENAME, LEGACY_METADATA_FILENAME, TRACKING_METHOD_VERSION, TRACKING_METHOD_BRANCH, TRACKING_METHOD_COMMIT, PLUGIN_MAGIC_FILE, PLUGIN_MAGIC_FILE_DISABLED, name_from_path, aliases, user_vars, canonical_url, Package, PackageInfo, PackageStatus, InstalledPackage ) from . import ( __version__, LOG, ) class Manager(object): """A package manager object performs various operations on packages. It uses a state directory and a manifest file within it to keep track of package sources, installed packages and their statuses. Attributes: sources (dict of str -> :class:`.source.Source`): dictionary package sources keyed by the name given to :meth:`add_source()` installed_pkgs (dict of str -> :class:`.package.InstalledPackage`): a dictionary of installed packaged keyed on package names (the last component of the package's git URL) zeek_dist (str): path to the Zeek source code distribution. This is needed for packages that contain Zeek plugins that need to be built from source code. state_dir (str): the directory where the package manager will a maintain manifest file, package/source git clones, and other persistent state the manager needs in order to operate user_vars (dict of str -> str): dictionary of key-value pairs where the value will be substituted into package build commands in place of the key. backup_dir (str): a directory where the package manager will store backup files (e.g. locally modified package config files) log_dir (str): a directory where the package manager will store misc. logs files (e.g. package build logs) scratch_dir (str): a directory where the package manager performs miscellaneous/temporary file operations script_dir (str): the directory where the package manager will copy each installed package's `script_dir` (as given by its :file:`zkg.meta` or :file:`bro-pkg.meta`). Each package gets a subdirectory within `script_dir` associated with its name. plugin_dir (str): the directory where the package manager will copy each installed package's `plugin_dir` (as given by its :file:`zkg.meta` or :file:`bro-pkg.meta`). Each package gets a subdirectory within `plugin_dir` associated with its name. source_clonedir (str): the directory where the package manager will clone package sources. Each source gets a subdirectory associated with its name. package_clonedir (str): the directory where the package manager will clone installed packages. Each package gets a subdirectory associated with its name. package_testdir (str): the directory where the package manager will run tests. Each package gets a subdirectory associated with its name. manifest (str): the path to the package manager's manifest file. This file maintains a list of installed packages and their status. autoload_script (str): path to a Zeek script named :file:`packages.zeek` that the package manager maintains. It is a list of ``@load`` for each installed package that is marked as loaded (see :meth:`load()`). autoload_package (str): path to a Zeek :file:`__load__.zeek` script which is just a symlink to `autoload_script`. It's always located in a directory named :file:`packages`, so as long as :envvar:`ZEEKPATH` is configured correctly, ``@load packages`` will load all installed packages that have been marked as loaded. """ def __init__(self, state_dir, script_dir, plugin_dir, zeek_dist='', user_vars=None): """Creates a package manager instance. Args: state_dir (str): value to set the `state_dir` attribute to script_dir (str): value to set the `script_dir` attribute to plugin_dir (str): value to set the `plugin_dir` attribute to zeek_dist (str): value to set the `zeek_dist` attribute to user_vars (dict of str -> str): key-value pair substitutions for use in package build commands. Raises: OSError: when a package manager state directory can't be created IOError: when a package manager state file can't be created """ LOG.debug('init Manager version %s', __version__) self.sources = {} self.installed_pkgs = {} # The bro_dist attribute exists just for backward compatibility self.bro_dist = zeek_dist self.zeek_dist = zeek_dist self.state_dir = state_dir self.user_vars = {} if user_vars is None else user_vars self.backup_dir = os.path.join(self.state_dir, 'backups') self.log_dir = os.path.join(self.state_dir, 'logs') self.scratch_dir = os.path.join(self.state_dir, 'scratch') self._script_dir = script_dir self.script_dir = os.path.join(script_dir, 'packages') self._plugin_dir = plugin_dir self.plugin_dir = os.path.join(plugin_dir, 'packages') self.source_clonedir = os.path.join(self.state_dir, 'clones', 'source') self.package_clonedir = os.path.join( self.state_dir, 'clones', 'package') self.package_testdir = os.path.join(self.state_dir, 'testing') self.manifest = os.path.join(self.state_dir, 'manifest.json') self.autoload_script = os.path.join(self.script_dir, 'packages.zeek') self.autoload_package = os.path.join(self.script_dir, '__load__.zeek') make_dir(self.state_dir) make_dir(self.log_dir) make_dir(self.scratch_dir) make_dir(self.source_clonedir) make_dir(self.package_clonedir) make_dir(self.script_dir) make_dir(self.plugin_dir) _create_readme(os.path.join(self.script_dir, 'README')) _create_readme(os.path.join(self.plugin_dir, 'README')) if not os.path.exists(self.manifest): self._write_manifest() prev_script_dir, prev_plugin_dir = self._read_manifest() if os.path.realpath(prev_script_dir) != os.path.realpath(self.script_dir): LOG.info('relocating script_dir %s -> %s', prev_script_dir, self.script_dir) if os.path.exists(prev_script_dir): delete_path(self.script_dir) shutil.move(prev_script_dir, self.script_dir) prev_zeekpath = os.path.dirname(prev_script_dir) for pkg_name in self.installed_pkgs: old_link = os.path.join(prev_zeekpath, pkg_name) new_link = os.path.join(self.zeekpath(), pkg_name) if os.path.lexists(old_link): LOG.info('moving package link %s -> %s', old_link, new_link) shutil.move(old_link, new_link) else: LOG.info('skip moving package link %s -> %s', old_link, new_link) self._write_manifest() if os.path.realpath(prev_plugin_dir) != os.path.realpath(self.plugin_dir): LOG.info('relocating plugin_dir %s -> %s', prev_plugin_dir, self.plugin_dir) if os.path.exists(prev_plugin_dir): delete_path(self.plugin_dir) shutil.move(prev_plugin_dir, self.plugin_dir) self._write_manifest() self._write_autoloader() make_symlink('packages.zeek', self.autoload_package) # Backward compatibility (Pre-Zeek 3.0 does not handle .zeek files) autoload_script_fallback = os.path.join(self.script_dir, 'packages.bro') autoload_package_fallback = os.path.join(self.script_dir, '__load__.bro') delete_path(autoload_script_fallback) delete_path(autoload_package_fallback) make_symlink('packages.zeek', autoload_script_fallback) make_symlink('packages.zeek', autoload_package_fallback) def _write_autoloader(self): """Write the :file:`packages.zeek` loader script. Raises: IOError: if :file:`packages.zeek` loader script cannot be written """ with open(self.autoload_script, 'w') as f: content = ('# WARNING: This file is managed by zkg.\n' '# Do not make direct modifications here.\n') for ipkg in self.loaded_packages(): content += '@load ./{}\n'.format(ipkg.package.name) f.write(content) def _write_plugin_magic(self, ipkg): """Enables/disables any Zeek plugin included with a package. Zeek's plugin code scans its plugin directories for __bro_plugin__ magic files, which indicate presence of a plugin directory. When this file does not exist, Zeek does not recognize a plugin. When we're loading a package, this function renames an existing __bro_plugin__.disabled file to __bro_plugin__, and vice versa when we're unloading a package. When the package doesn't include a plugin, or when the plugin directory already contains a correctly named magic file, this function does nothing. """ magic_path = os.path.join( self.plugin_dir, ipkg.package.name, PLUGIN_MAGIC_FILE) magic_path_disabled = os.path.join( self.plugin_dir, ipkg.package.name, PLUGIN_MAGIC_FILE_DISABLED) if ipkg.status.is_loaded: if os.path.exists(magic_path_disabled): try: os.rename(magic_path_disabled, magic_path) except OSError as exception: LOG.warning('could not enable plugin: %s: %s'.format( type(exception).__name__, exception)) else: if os.path.exists(magic_path): try: os.rename(magic_path, magic_path_disabled) except OSError as exception: LOG.warning('could not disable plugin: %s: %s'.format( type(exception).__name__, exception)) def _read_manifest(self): """Read the manifest file containing the list of installed packages. Returns: tuple: (previous script_dir, previous plugin_dir) Raises: IOError: when the manifest file can't be read """ with open(self.manifest, 'r') as f: data = json.load(f) version = data['manifest_version'] pkg_list = data['installed_packages'] self.installed_pkgs = {} for dicts in pkg_list: pkg_dict = dicts['package_dict'] status_dict = dicts['status_dict'] pkg_name = pkg_dict['name'] if version == 0 and 'index_data' in pkg_dict: del pkg_dict['index_data'] pkg_dict['canonical'] = True; pkg = Package(**pkg_dict) status = PackageStatus(**status_dict) self.installed_pkgs[pkg_name] = InstalledPackage(pkg, status) return data['script_dir'], data['plugin_dir'] def _write_manifest(self): """Writes the manifest file containing the list of installed packages. Raises: IOError: when the manifest file can't be written """ pkg_list = [] for _, installed_pkg in self.installed_pkgs.items(): pkg_list.append({'package_dict': installed_pkg.package.__dict__, 'status_dict': installed_pkg.status.__dict__}) data = {'manifest_version': 1, 'script_dir': self.script_dir, 'plugin_dir': self.plugin_dir, 'installed_packages': pkg_list} with open(self.manifest, 'w') as f: json.dump(data, f, indent=2, sort_keys=True) def zeekpath(self): """Return the path where installed package scripts are located. This path can be added to :envvar:`ZEEKPATH` for interoperability with Zeek. """ return os.path.dirname(self.script_dir) def bropath(self): """Same as :meth:`zeekpath`. Using :meth:`zeekpath` is preferred since this may later be deprecated. """ return self.zeekpath() def zeek_plugin_path(self): """Return the path where installed package plugins are located. This path can be added to :envvar:`ZEEK_PLUGIN_PATH` for interoperability with Zeek. """ return os.path.dirname(self.plugin_dir) def bro_plugin_path(self): """Same as :meth:`zeek_plugin_path`. Using :meth:`zeek_plugin_path` is preferred since this may later be deprecated. """ return self.zeek_plugin_path() def add_source(self, name, git_url): """Add a git repository that acts as a source of packages. Args: name (str): a short name that will be used to reference the package source. git_url (str): the git URL of the package source Returns: str: empty string if the source is successfully added, else the reason why it failed. """ if name in self.sources: existing_source = self.sources[name] if existing_source.git_url == git_url: LOG.debug('duplicate source "%s"', name) return True return 'source already exists with different URL: {}'.format( existing_source.git_url) clone_path = os.path.join(self.source_clonedir, name) try: source = Source(name=name, clone_path=clone_path, git_url=git_url) except git.exc.GitCommandError as error: LOG.warning('failed to clone git repo: %s', error) return 'failed to clone git repo' else: self.sources[name] = source return '' def source_packages(self): """Return a list of :class:`.package.Package` within all sources.""" rval = [] for _, source in self.sources.items(): rval += source.packages() return rval def installed_packages(self): """Return list of :class:`.package.InstalledPackage`.""" return [ipkg for _, ipkg in sorted(self.installed_pkgs.items())] def loaded_packages(self): """Return list of loaded :class:`.package.InstalledPackage`.""" rval = [] for _, ipkg in sorted(self.installed_pkgs.items()): if ipkg.status.is_loaded: rval.append(ipkg) return rval def package_build_log(self, pkg_path): """Return the path to the package manager's build log for a package. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". """ name = name_from_path(pkg_path) return os.path.join(self.log_dir, '{}-build.log'.format(name)) def match_source_packages(self, pkg_path): """Return a list of :class:`.package.Package` that match a given path. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". """ rval = [] canon_url = canonical_url(pkg_path) for pkg in self.source_packages(): if pkg.matches_path(canon_url): rval.append(pkg) return rval def find_installed_package(self, pkg_path): """Return an :class:`.package.InstalledPackage` if one matches the name. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". A package's name is the last component of it's git URL. """ pkg_name = name_from_path(pkg_path) return self.installed_pkgs.get(pkg_name) def has_scripts(self, installed_pkg): """Return whether a :class:`.package.InstalledPackage` installed scripts. Args: installed_pkg(:class:`.package.InstalledPackage`): the installed package to check for whether it has installed any Zeek scripts. Returns: bool: True if the package has installed Zeek scripts. """ return os.path.exists(os.path.join(self.script_dir, installed_pkg.package.name)) def has_plugin(self, installed_pkg): """Return whether a :class:`.package.InstalledPackage` installed a plugin. Args: installed_pkg(:class:`.package.InstalledPackage`): the installed package to check for whether it has installed a Zeek plugin. Returns: bool: True if the package has installed a Zeek plugin. """ return os.path.exists(os.path.join(self.plugin_dir, installed_pkg.package.name)) def save_temporary_config_files(self, installed_pkg): """Return a list of temporary package config file backups. Args: installed_pkg(:class:`.package.InstalledPackage`): the installed package to save temporary config file backups for. Returns: list of (str, str): tuples that describe the config files backups. The first element is the config file as specified in the package metadata (a file path relative to the package's root directory). The second element is an absolute file system path to where that config file has been copied. It should be considered temporary, so make use of it before doing any further operations on packages. """ import re metadata = installed_pkg.package.metadata config_files = re.split(',\s*', metadata.get('config_files', '')) if not config_files: return [] pkg_name = installed_pkg.package.name clone_dir = os.path.join(self.package_clonedir, pkg_name) rval = [] for config_file in config_files: config_file_path = os.path.join(clone_dir, config_file) if not os.path.isfile(config_file_path): LOG.info("package '%s' claims config file at '%s'," " but it does not exist", pkg_name, config_file) continue backup_file = os.path.join(self.scratch_dir, 'tmpcfg', config_file) make_dir(os.path.dirname(backup_file)) shutil.copy2(config_file_path, backup_file) rval.append((config_file, backup_file)) return rval def modified_config_files(self, installed_pkg): """Return a list of package config files that the user has modified. Args: installed_pkg(:class:`.package.InstalledPackage`): the installed package to check for whether it has installed any Zeek scripts. Returns: list of (str, str): tuples that describe the modified config files. The first element is the config file as specified in the package metadata (a file path relative to the package's root directory). The second element is an absolute file system path to where that config file is currently installed. """ import re metadata = installed_pkg.package.metadata config_files = re.split(',\s*', metadata.get('config_files', '')) if not config_files: return [] pkg_name = installed_pkg.package.name script_install_dir = os.path.join(self.script_dir, pkg_name) plugin_install_dir = os.path.join(self.plugin_dir, pkg_name) clone_dir = os.path.join(self.package_clonedir, pkg_name) script_dir = metadata.get('script_dir', '') plugin_dir = metadata.get('plugin_dir', 'build') rval = [] for config_file in config_files: their_config_file_path = os.path.join(clone_dir, config_file) if not os.path.isfile(their_config_file_path): LOG.info("package '%s' claims config file at '%s'," " but it does not exist", pkg_name, config_file) continue if config_file.startswith(plugin_dir): our_config_file_path = os.path.join( plugin_install_dir, config_file[len(plugin_dir):]) if not os.path.isfile(our_config_file_path): LOG.info("package '%s' config file '%s' not found" " in plugin_dir: %s", pkg_name, config_file, our_config_file_path) continue elif config_file.startswith(script_dir): our_config_file_path = os.path.join( script_install_dir, config_file[len(script_dir):]) if not os.path.isfile(our_config_file_path): LOG.info("package '%s' config file '%s' not found" " in script_dir: %s", pkg_name, config_file, our_config_file_path) continue else: # Their config file is outside script/plugin install dirs, # so no way user has it even installed, much less modified. LOG.warning("package '%s' config file '%s' not within" " plugin_dir or script_dir", pkg_name, config_file) continue if not filecmp.cmp(our_config_file_path, their_config_file_path): rval.append((config_file, our_config_file_path)) return rval def backup_modified_files(self, backup_subdir, modified_files): """Creates backups of modified config files Args: modified_files(list of (str, str)): the return value of :meth:`modified_config_files()`. backup_subdir(str): the subdir of `backup_dir` in which Returns: list of str: paths indicating the backup locations. The order of the returned list corresponds directly to the order of `modified_files`. """ import time rval = [] for modified_file in modified_files: config_file = modified_file[0] config_file_dir = os.path.dirname(config_file) install_path = modified_file[1] filename = os.path.basename(install_path) backup_dir = os.path.join( self.backup_dir, backup_subdir, config_file_dir) timestamp = time.strftime('.%Y-%m-%d-%H:%M:%S') backup_path = os.path.join(backup_dir, filename + timestamp) make_dir(backup_dir) shutil.copy2(install_path, backup_path) rval.append(backup_path) return rval def refresh_source(self, name, aggregate=False, push=False): """Pull latest git information from a package source. This makes the latest pre-aggregated package metadata available or performs the aggregation locally in order to push it to the actual package source. Locally aggregated data also takes precedence over the source's pre-aggregated data, so it can be useful in the case the operator of the source does not update their pre-aggregated data at a frequent enough interval. Args: name(str): the name of the package source. E.g. the same name used as a key to :meth:`add_source()`. aggregate (bool): whether to perform a local metadata aggregation by crawling all packages listed in the source's index files. push (bool): whether to push local changes to the aggregated metadata to the remote package source. If the `aggregate` flag is set, the data will be pushed after the aggregation is finished. Returns: str: an empty string if no errors occurred, else a description of what went wrong. """ if name not in self.sources: return 'source name does not exist' source = self.sources[name] LOG.debug('refresh "%s": pulling %s', name, source.git_url) aggregate_file = os.path.join(source.clone.working_dir, AGGREGATE_DATA_FILE) agg_file_ours = os.path.join( self.scratch_dir, AGGREGATE_DATA_FILE) agg_file_their_orig = os.path.join(self.scratch_dir, AGGREGATE_DATA_FILE + '.orig') delete_path(agg_file_ours) delete_path(agg_file_their_orig) if os.path.isfile(aggregate_file): shutil.copy2(aggregate_file, agg_file_ours) source.clone.git.reset(hard=True) source.clone.git.clean('-f', '-x', '-d') if os.path.isfile(aggregate_file): shutil.copy2(aggregate_file, agg_file_their_orig) try: source.clone.git.fetch('--recurse-submodules=yes') source.clone.git.pull() source.clone.git.submodule('sync', '--recursive') source.clone.git.submodule('update', '--recursive', '--init') except git.exc.GitCommandError as error: LOG.error('failed to pull source %s: %s', name, error) return 'failed to pull from remote source: {}'.format(error) if os.path.isfile(agg_file_ours): if os.path.isfile(aggregate_file): # There's a tracked version of the file after pull. if os.path.isfile(agg_file_their_orig): # We had local modifications to the file. if filecmp.cmp(aggregate_file, agg_file_their_orig): # Their file hasn't changed, use ours. shutil.copy2(agg_file_ours, aggregate_file) LOG.debug( "aggegrate file in source unchanged, restore local one") else: # Their file changed, use theirs. LOG.debug( "aggegrate file in source changed, discard local one") else: # File was untracked before pull and tracked after, # use their version. LOG.debug("new aggegrate file in source, discard local one") else: # They don't have the file after pulling, so restore ours. shutil.copy2(agg_file_ours, aggregate_file) LOG.debug("no aggegrate file in source, restore local one") if aggregate: # Use raw parser so no value interpolation takes place. parser = configparser.RawConfigParser() for index_file in source.package_index_files(): urls = [] with open(index_file) as f: urls = [line.rstrip('\n') for line in f] for url in urls: pkg_name = name_from_path(url) clonepath = os.path.join(self.scratch_dir, pkg_name) delete_path(clonepath) try: clone = git_clone(url, clonepath, shallow=True) except git.exc.GitCommandError as error: LOG.warn('failed to clone %s, skipping aggregation: %s', url, error) continue version_tags = _get_version_tags(clone) if len(version_tags): version = version_tags[-1] else: version = 'master' _git_checkout(clone, version) metadata_file = _pick_metadata_file(clone.working_dir) # Use raw parser so no value interpolation takes place. metadata_parser = configparser.RawConfigParser() invalid_reason = _parse_package_metadata( metadata_parser, metadata_file) if invalid_reason: LOG.warn('skipping aggregation of %s: bad metadata: %s', url, invalid_reason) continue metadata = _get_package_metadata(metadata_parser) index_dir = os.path.dirname(index_file)[len( self.source_clonedir) + len(name) + 2:] qualified_name = os.path.join(index_dir, pkg_name) parser.add_section(qualified_name) for key, value in sorted(metadata.items()): parser.set(qualified_name, key, value) parser.set(qualified_name, 'url', url) parser.set(qualified_name, 'version', version) with open(aggregate_file, 'w') as f: parser.write(f) if push: if os.path.isfile(os.path.join(source.clone.working_dir, AGGREGATE_DATA_FILE)): source.clone.git.add(AGGREGATE_DATA_FILE) if source.clone.is_dirty(): source.clone.git.commit( '--message', 'Update aggregated metadata.') source.clone.git.push() return '' def refresh_installed_packages(self): """Fetch latest git information for installed packages. This retrieves information about outdated packages, but does not actually upgrade their installations. Raises: IOError: if the package manifest file can't be written """ for ipkg in self.installed_packages(): clonepath = os.path.join(self.package_clonedir, ipkg.package.name) clone = git.Repo(clonepath) LOG.debug('fetch package %s', ipkg.package.qualified_name()) try: clone.git.fetch('--recurse-submodules=yes') except git.exc.GitCommandError as error: LOG.warn('failed to fetch package %s: %s', ipkg.package.qualified_name(), error) ipkg.status.is_outdated = _is_clone_outdated( clone, ipkg.status.current_version, ipkg.status.tracking_method) self._write_manifest() def upgrade(self, pkg_path): """Upgrade a package to the latest available version. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: str: an empty string if package upgrade succeeded else an error string explaining why it failed. Raises: IOError: if the manifest can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('upgrading "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('upgrading "%s": no matching package', pkg_path) return "no such package installed" if ipkg.status.is_pinned: LOG.info('upgrading "%s": package is pinned', pkg_path) return "package is pinned" if not ipkg.status.is_outdated: LOG.info('upgrading "%s": package not outdated', pkg_path) return "package is not outdated" clonepath = os.path.join(self.package_clonedir, ipkg.package.name) clone = git.Repo(clonepath) if ipkg.status.tracking_method == TRACKING_METHOD_VERSION: version_tags = _get_version_tags(clone) return self._install(ipkg.package, version_tags[-1]) elif ipkg.status.tracking_method == TRACKING_METHOD_BRANCH: clone.git.pull() clone.git.submodule('sync', '--recursive') clone.git.submodule('update', '--recursive', '--init') return self._install(ipkg.package, ipkg.status.current_version) elif ipkg.status.tracking_method == TRACKING_METHOD_COMMIT: # The above check for whether the installed package is outdated # also should have already caught this situation. return "package is not outdated" else: raise NotImplementedError def remove(self, pkg_path): """Remove an installed package. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: bool: True if an installed package was removed, else False. Raises: IOError: if the package manifest file can't be written OSError: if the installed package's directory can't be deleted """ pkg_path = canonical_url(pkg_path) LOG.debug('removing "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('removing "%s": could not find matching package', pkg_path) return False self.unload(pkg_path) pkg_to_remove = ipkg.package delete_path(os.path.join(self.package_clonedir, pkg_to_remove.name)) delete_path(os.path.join(self.script_dir, pkg_to_remove.name)) delete_path(os.path.join(self.plugin_dir, pkg_to_remove.name)) delete_path(os.path.join(self.zeekpath(), pkg_to_remove.name)) for alias in pkg_to_remove.aliases(): delete_path(os.path.join(self.zeekpath(), alias)) del self.installed_pkgs[pkg_to_remove.name] self._write_manifest() LOG.debug('removed "%s"', pkg_path) return True def pin(self, pkg_path): """Pin a currently installed package to the currently installed version. Pinned packages are never upgraded when calling :meth:`upgrade()`. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: :class:`.package.InstalledPackage`: None if no matching installed package could be found, else the installed package that was pinned. Raises: IOError: when the manifest file can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('pinning "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('pinning "%s": no matching package', pkg_path) return None if ipkg.status.is_pinned: LOG.debug('pinning "%s": already pinned', pkg_path) return ipkg ipkg.status.is_pinned = True self._write_manifest() LOG.debug('pinned "%s"', pkg_path) return ipkg def unpin(self, pkg_path): """Unpin a currently installed package and allow it to be upgraded. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: :class:`.package.InstalledPackage`: None if no matching installed package could be found, else the installed package that was unpinned. Raises: IOError: when the manifest file can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('unpinning "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('unpinning "%s": no matching package', pkg_path) return None if not ipkg.status.is_pinned: LOG.debug('unpinning "%s": already unpinned', pkg_path) return ipkg ipkg.status.is_pinned = False self._write_manifest() LOG.debug('unpinned "%s"', pkg_path) return ipkg def load(self, pkg_path): """Mark an installed package as being "loaded". The collection of "loaded" packages is a convenient way for Zeek to more simply load a whole group of packages installed via the package manager. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: str: empty string if the package is successfully marked as loaded, else an explanation of why it failed. Raises: IOError: if the loader script or manifest can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('loading "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('loading "%s": no matching package', pkg_path) return 'no such package' if ipkg.status.is_loaded: LOG.debug('loading "%s": already loaded', pkg_path) return '' pkg_load_script = os.path.join(self.script_dir, ipkg.package.name, '__load__.zeek') # Check if __load__.bro exists for compatibility with older packages pkg_load_fallback = os.path.join(self.script_dir, ipkg.package.name, '__load__.bro') if (not os.path.exists(pkg_load_script) and not os.path.exists(pkg_load_fallback) and not self.has_plugin(ipkg)): LOG.debug('loading "%s": %s not found and package has no plugin', pkg_path, pkg_load_script) return 'no __load__.zeek within package script_dir and no plugin included' ipkg.status.is_loaded = True self._write_autoloader() self._write_manifest() self._write_plugin_magic(ipkg) LOG.debug('loaded "%s"', pkg_path) return '' def unload(self, pkg_path): """Unmark an installed package as being "loaded". The collection of "loaded" packages is a convenient way for Zeek to more simply load a whole group of packages installed via the package manager. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". Returns: bool: True if a package is successfully unmarked as loaded. Raises: IOError: if the loader script or manifest can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('unloading "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if not ipkg: LOG.info('unloading "%s": no matching package', pkg_path) return False if not ipkg.status.is_loaded: LOG.debug('unloading "%s": already unloaded', pkg_path) return True ipkg.status.is_loaded = False self._write_autoloader() self._write_manifest() self._write_plugin_magic(ipkg) LOG.debug('unloaded "%s"', pkg_path) return True def bundle_info(self, bundle_file): """Retrieves information on all packages contained in a bundle. Args: bundle_file (str): the path to the bundle to inspect. Returns: (str, list of (str, str, :class:`.package.PackageInfo`)): a tuple with the the first element set to an empty string if the information successfully retrieved, else an error message explaining why the bundle file was invalid. The second element of the tuple is a list containing information on each package contained in the bundle: the exact git URL and version string from the bundle's manifest along with the package info object retrieved by inspecting git repo contained in the bundle. """ LOG.debug('getting bundle info for file "%s"', bundle_file) bundle_dir = os.path.join(self.scratch_dir, 'bundle') delete_path(bundle_dir) make_dir(bundle_dir) infos = [] try: with tarfile.open(bundle_file) as tf: tf.extractall(bundle_dir) except Exception as error: return (str(error), infos) manifest_file = os.path.join(bundle_dir, 'manifest.txt') config = GoodConfigParser(delimiters='=') config.optionxform = str if not config.read(manifest_file): return ('invalid bundle: no manifest file', infos) if not config.has_section('bundle'): return ('invalid bundle: no [bundle] section in manifest file', infos) manifest = config.items('bundle') for git_url, version in manifest: package = Package(git_url=git_url, name=git_url.split('/')[-1], canonical=True) pkg_path = os.path.join(bundle_dir, package.name) LOG.debug('getting info for bundled package "%s"', package.name) pkg_info = self.info(pkg_path, version=version, prefer_installed=False) infos.append((git_url, version, pkg_info)) return ('', infos) def info(self, pkg_path, version='', prefer_installed=True): """Retrieves information about a package. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". version (str): may be a git version tag, branch name, or commit hash from which metadata will be pulled. If an empty string is given, then the latest git version tag is used (or the "master" branch if no version tags exist). prefer_installed (bool): if this is set, then the information from any current installation of the package is returned instead of retrieving the latest information from the package's git repo. The `version` parameter is also ignored when this is set as it uses whatever version of the package is currently installed. Returns: A :class:`.package.PackageInfo` object. """ pkg_path = canonical_url(pkg_path) LOG.debug('getting info on "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if prefer_installed and ipkg: status = ipkg.status pkg_name = ipkg.package.name clonepath = os.path.join(self.package_clonedir, pkg_name) clone = git.Repo(clonepath) return _info_from_clone(clone, ipkg.package, status, status.current_version) else: status = None matches = self.match_source_packages(pkg_path) if not matches: package = Package(git_url=pkg_path) try: return self._info(package, status, version) except git.exc.GitCommandError as error: LOG.info('getting info on "%s": invalid git repo path: %s', pkg_path, error) LOG.info('getting info on "%s": matched no source package', pkg_path) reason = ('package name not found in sources and also' ' not a usable git URL (invalid or inaccessible,' ' use -vvv for details)') return PackageInfo(package=package, invalid_reason=reason, status=status) if len(matches) > 1: matches_string = [match.qualified_name() for match in matches] LOG.info('getting info on "%s": matched multiple packages: %s', pkg_path, matches_string) reason = str.format('"{}" matches multiple packages, try a more' ' specific name from: {}', pkg_path, matches_string) return PackageInfo(invalid_reason=reason, status=status) package = matches[0] try: return self._info(package, status, version) except git.exc.GitCommandError as error: LOG.info('getting info on "%s": invalid git repo path: %s', pkg_path, error) reason = 'git repository is either invalid or unreachable' return PackageInfo(package=package, invalid_reason=reason, status=status) def _info(self, package, status, version): """Retrieves information about a package. Returns: A :class:`.package.PackageInfo` object. Raises: git.exc.GitCommandError: when failing to clone the package repo """ clonepath = os.path.join(self.scratch_dir, package.name) clone = _clone_package(package, clonepath, version) versions = _get_version_tags(clone) if not version: if len(versions): version = versions[-1] else: if 'master' not in _get_branch_names(clone): reason = 'git repo has no "master" branch or version tags' return PackageInfo(package=package, status=status, invalid_reason=reason) version = 'master' try: _git_checkout(clone, version) except git.exc.GitCommandError: reason = 'no such commit, branch, or version tag: "{}"'.format( version) return PackageInfo(package=package, status=status, invalid_reason=reason) LOG.debug('checked out "%s", branch/version "%s"', package, version) return _info_from_clone(clone, package, status, version) def package_versions(self, installed_package): """Returns a list of version number tags available for a package. Args: installed_package (:class:`.package.InstalledPackage`): the package for which version number tags will be retrieved. Returns: list of str: the version number tags. """ name = installed_package.package.name clonepath = os.path.join(self.package_clonedir, name) clone = git.Repo(clonepath) return _get_version_tags(clone) def validate_dependencies(self, requested_packages, ignore_installed_packages=False, ignore_suggestions=False): """Validates package dependencies. Args: requested_packages (list of (str, str)): a list of (package name or git URL, version) string tuples validate. If the version string is empty, the latest available version of the package is used. ignore_installed_packages (bool): whether the dependency analysis should consider installed packages as satisfying dependency requirements. ignore_suggestions (bool): whether the dependency analysis should consider installing dependencies that are marked in another package's 'suggests' metadata field. Returns: (str, list of (:class:`.package.PackageInfo`, str, bool)): the first element of the tuple is an empty string if dependency graph was successfully validated, else an error string explaining what is invalid. In the case it was validated, the second element is a list of tuples where the first elements are dependency packages that would need to be installed in order to satisfy the dependencies of the requested packages (it will not include any packages that are already installed or that are in the `requested_packages` argument). The second element of tuples in the list is a version string of the associated package that satisfies dependency requirements. The third element of the tuples in the list is a boolean value indicating whether the package is included in the list because it's merely suggested by another package. """ class Node(object): def __init__(self, name): self.name = name self.info = None self.requested_version = None # (tracking method, version) self.installed_version = None # (tracking method, version) self.dependers = dict() # name -> version self.is_suggestion = False def __str__(self): return str.format( '{}\n\trequested: {}\n\tinstalled: {}\n\tdependers: {}\n\tsuggestion: {}', self.name, self.requested_version, self.installed_version, self.dependers, self.is_suggestion) new_pkgs = [] graph = dict() # 1. Try to make nodes for everything in the dependency graph... # Add nodes for packages that are requested for installation for name, version in requested_packages: info = self.info(name, version=version, prefer_installed=False) if info.invalid_reason: return ('invalid package "{}": {}'.format(name, info.invalid_reason), new_pkgs) node = Node(info.package.qualified_name()) node.info = info method = node.info.version_type node.requested_version = (method, version) graph[node.name] = node # Recursively add nodes for all dependencies of requested packages to_process = copy.copy(graph) while to_process: (_, node) = to_process.popitem() dd = node.info.dependencies(field='depends') ds = node.info.dependencies(field='suggests') if dd is None: return (str.format('package "{}" has malformed "depends" field', node.name), new_pkgs) all_deps = dd.copy() if not ignore_suggestions: if ds is None: return (str.format('package "{}" has malformed "suggests" field', node.name), new_pkgs) all_deps.update(ds) for dep_name, _ in all_deps.items(): if dep_name == 'bro' or dep_name == 'zeek': # A zeek node will get added later. continue if dep_name == 'bro-pkg' or dep_name == 'zkg': # A zkg node will get added later. continue # Suggestion status propagates to 'depends' field of suggested packages. is_suggestion = node.is_suggestion or dep_name in ds and dep_name not in dd info = self.info(dep_name, prefer_installed=False) if info.invalid_reason: return (str.format( 'package "{}" has invalid dependency "{}": {}', node.name, dep_name, info.invalid_reason), new_pkgs) dep_name = info.package.qualified_name() if dep_name in graph: if graph[dep_name].is_suggestion and not is_suggestion: # Suggestion found to be required by another package. graph[dep_name].is_suggestion = False continue if dep_name in to_process: if to_process[dep_name].is_suggestion and not is_suggestion: # Suggestion found to be required by another package. to_process[dep_name].is_suggestion = False continue node = Node(dep_name) node.info = info node.is_suggestion = is_suggestion graph[node.name] = node to_process[node.name] = node # Add nodes for things that are already installed (including zeek) if not ignore_installed_packages: zeek_version = get_zeek_version() if zeek_version: node = Node('zeek') node.installed_version = (TRACKING_METHOD_VERSION, zeek_version) graph['zeek'] = node else: LOG.warning( 'could not get zeek version: no "zeek-config" or "bro-config" in PATH ?') node = Node('zkg') node.installed_version = (TRACKING_METHOD_VERSION, __version__) graph['zkg'] = node for ipkg in self.installed_packages(): name = ipkg.package.qualified_name() status = ipkg.status if name not in graph: info = self.info(name, prefer_installed=True) node = Node(name) node.info = info graph[node.name] = node graph[name].installed_version = ( status.tracking_method, status.current_version) # 2. Fill in the edges of the graph with dependency information. for name, node in graph.items(): if name == 'zeek': continue if name == 'zkg': continue dd = node.info.dependencies(field='depends') ds = node.info.dependencies(field='suggests') if dd is None: return (str.format('package "{}" has malformed "depends" field', node.name), new_pkgs) all_deps = dd.copy() if not ignore_suggestions: if ds is None: return (str.format('package "{}" has malformed "suggests" field', node.name), new_pkgs) all_deps.update(ds) for dep_name, dep_version in all_deps.items(): if dep_name == 'bro' or dep_name == 'zeek': if 'zeek' in graph: graph['zeek'].dependers[name] = dep_version elif dep_name == 'bro-pkg' or dep_name == 'zkg': if 'zkg' in graph: graph['zkg'].dependers[name] = dep_version else: for _, dependency_node in graph.items(): if dependency_node.name == 'zeek': continue if dependency_node.name == 'zkg': continue if dependency_node.info.package.matches_path(dep_name): dependency_node.dependers[name] = dep_version break # 3. Try to solve for a connected graph with no edge conflicts. for name, node in graph.items(): if not node.dependers: if node.installed_version: continue if node.requested_version: continue new_pkgs.append((node.info, node.info.best_version(), node.is_suggestion)) continue if node.requested_version: # Check that requested version doesn't conflict with dependers. track_method, required_version = node.requested_version if track_method == TRACKING_METHOD_BRANCH: for depender_name, version_spec in node.dependers.items(): if version_spec == '*': continue if version_spec.startswith('branch='): version_spec = version_spec[len('branch='):] if version_spec == required_version: continue return (str.format( 'unsatisfiable dependency: requested "{}" ({}),' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) elif track_method == TRACKING_METHOD_COMMIT: for depender_name, version_spec in node.dependers.items(): if version_spec == '*': continue # Could allow commit= version specification like what # is done with branches, but unsure there's a common # use-case for it. return (str.format( 'unsatisfiable dependency: requested "{}" ({}),' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) else: normal_version = _normalize_version_tag(required_version) req_semver = semver.Version.coerce(normal_version) for depender_name, version_spec in node.dependers.items(): if version_spec.startswith('branch='): version_spec = version_spec[len('branch='):] return (str.format( 'unsatisfiable dependency: requested "{}" ({}),' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) else: try: semver_spec = semver.Spec(version_spec) except ValueError: return (str.format( 'package "{}" has invalid semver spec: {}', depender_name, version_spec), new_pkgs) if req_semver not in semver_spec: return (str.format( 'unsatisfiable dependency: requested "{}" ({}),' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) elif node.installed_version: # Check that installed version doesn't conflict with dependers. track_method, required_version = node.installed_version for depender_name, version_spec in node.dependers.items(): if ( depender_name in graph and graph[depender_name].installed_version ): # Both nodes already installed, so assume compatible. # They're maybe not actually if the user installed via # --nodeps, but if that's their desired state, it's # better not to complain about it now. continue if track_method == TRACKING_METHOD_BRANCH: if version_spec == '*': continue if version_spec.startswith('branch='): version_spec = version_spec[len('branch='):] if version_spec == required_version: continue return (str.format( 'unsatisfiable dependency: "{}" ({}) is installed,' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) elif track_method == TRACKING_METHOD_COMMIT: if version_spec == '*': continue # Could allow commit= version specification like what # is done with branches, but unsure there's a common # use-case for it. return (str.format( 'unsatisfiable dependency: "{}" ({}) is installed,' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) else: normal_version = _normalize_version_tag(required_version) req_semver = semver.Version.coerce(normal_version) if version_spec.startswith('branch='): version_spec = version_spec[len('branch='):] return (str.format( 'unsatisfiable dependency: "{}" ({}) is installed,' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) else: try: semver_spec = semver.Spec(version_spec) except ValueError: return (str.format( 'package "{}" has invalid semver spec: {}', depender_name, version_spec), new_pkgs) if req_semver not in semver_spec: return (str.format( 'unsatisfiable dependency: "{}" ({}) is installed,' ' but "{}" requires {}', node.name, required_version, depender_name, version_spec), new_pkgs) else: # Choose best version that satisfies constraints if not node.info.versions: best_version = 'master' for depender_name, version_spec in node.dependers.items(): if version_spec == '*': continue if version_spec.startswith('branch='): version_spec = version_spec[len('branch='):] if version_spec == best_version: continue return (str.format( 'unsatisfiable dependency "{}": "{}" requires {}', node.name, depender_name, version_spec), new_pkgs) else: best_version = None need_branch = False need_version = False def no_best_version_string(node): rval = str.format( '"{}" has no version satisfying dependencies:\n', node.name) for depender_name, version_spec in node.dependers.items(): rval += str.format('\t{} needs {}\n', depender_name, version_spec) return (rval, new_pkgs) for _, version_spec in node.dependers.items(): if version_spec.startswith('branch='): need_branch = True elif version_spec != '*': need_version = True if need_branch and need_version: return (no_best_version_string(node), new_pkgs) if need_branch: branch_name = None for depender_name, version_spec in node.dependers.items(): if version_spec == '*': continue if not branch_name: branch_name = version_spec[len('branch='):] continue if branch_name != version_spec[len('branch='):]: return (no_best_version_string(), new_pkgs) if branch_name: best_version = branch_name else: best_version = 'master' elif need_version: for version in node.info.versions[::-1]: normal_version = _normalize_version_tag(version) req_semver = semver.Version.coerce(normal_version) satisfied = True for depender_name, version_spec in node.dependers.items(): try: semver_spec = semver.Spec(version_spec) except ValueError: return (str.format( 'package "{}" has invalid semver spec: {}', depender_name, version_spec), new_pkgs) if req_semver not in semver_spec: satisfied = False break if satisfied: best_version = version break if not best_version: return (no_best_version_string(node), new_pkgs) else: # Must have been all '*' wildcards best_version = node.info.best_version() new_pkgs.append((node.info, best_version, node.is_suggestion)) return ('', new_pkgs) def bundle(self, bundle_file, package_list, prefer_existing_clones=False): """Creates a package bundle. Args: bundle_file (str): filesystem path of the zip file to create. package_list (list of (str, str)): a list of (git URL, version) string tuples to put in the bundle. If the version string is empty, the latest available version of the package is used. prefer_existing_clones (bool): if True and the package list contains a package at a version that is already installed, then the existing git clone of that package is put into the bundle instead of cloning from the remote repository. Returns: str: empty string if the bundle is successfully created, else an error string explaining what failed. """ bundle_dir = os.path.join(self.scratch_dir, 'bundle') delete_path(bundle_dir) make_dir(bundle_dir) manifest_file = os.path.join(bundle_dir, 'manifest.txt') config = GoodConfigParser(delimiters='=') config.optionxform = str config.add_section('bundle') def match_package_url_and_version(git_url, version): for ipkg in self.installed_packages(): if ipkg.package.git_url != git_url: continue if ipkg.status.current_version != version: continue return ipkg return None for git_url, version in package_list: name = name_from_path(git_url) clonepath = os.path.join(bundle_dir, name) config.set('bundle', git_url, version) if prefer_existing_clones: ipkg = match_package_url_and_version(git_url, version) if ipkg: src = os.path.join( self.package_clonedir, ipkg.package.name) shutil.copytree(src, clonepath, symlinks=True) clone = git.Repo(clonepath) clone.git.reset(hard=True) clone.git.clean('-f', '-x', '-d') for modified_config in self.modified_config_files(ipkg): dst = os.path.join(clonepath, modified_config[0]) shutil.copy2(modified_config[1], dst) continue try: git_clone(git_url, clonepath, shallow=(not is_sha1(version))) except git.exc.GitCommandError as error: return 'failed to clone {}: {}'.format(git_url, error) with open(manifest_file, 'w') as f: config.write(f) archive = shutil.make_archive(bundle_dir, 'gztar', bundle_dir) delete_path(bundle_file) shutil.move(archive, bundle_file) return '' def unbundle(self, bundle_file): """Installs all packages contained within a bundle. Args: bundle_file (str): the path to the bundle to install. Returns: str: an empty string if the operation was successful, else an error message indicated what went wrong. """ LOG.debug('unbundle "%s"', bundle_file) bundle_dir = os.path.join(self.scratch_dir, 'bundle') delete_path(bundle_dir) make_dir(bundle_dir) try: with tarfile.open(bundle_file) as tf: tf.extractall(bundle_dir) except Exception as error: return str(error) manifest_file = os.path.join(bundle_dir, 'manifest.txt') config = GoodConfigParser(delimiters='=') config.optionxform = str if not config.read(manifest_file): return 'invalid bundle: no manifest file' if not config.has_section('bundle'): return 'invalid bundle: no [bundle] section in manifest file' manifest = config.items('bundle') for git_url, version in manifest: package = Package(git_url=git_url, name=git_url.split('/')[-1], canonical=True) clonepath = os.path.join(self.package_clonedir, package.name) delete_path(clonepath) shutil.move(os.path.join(bundle_dir, package.name), clonepath) LOG.debug('unbundle installing "%s"', package.name) error = self._install(package, version, use_existing_clone=True) if error: return error return '' def test(self, pkg_path, version=''): """Test a package. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". version (str): if not given, then the latest git version tag is used (or if no version tags exist, the "master" branch is used). If given, it may be either a git version tag or a git branch name. Returns: (str, bool, str): a tuple containing an error message string, a boolean indicating whether the tests passed, as well as a path to the directory in which the tests were run. In the case where tests failed, the directory can be inspected to figure out what went wrong. In the case where the error message string is not empty, the error message indicates the reason why tests could not be run. """ pkg_path = canonical_url(pkg_path) LOG.debug('testing "%s"', pkg_path) pkg_info = self.info(pkg_path, version=version, prefer_installed=False) if pkg_info.invalid_reason: return (pkg_info.invalid_reason, 'False', '') if 'test_command' not in pkg_info.metadata: return ('Package does not specify a test_command', False, '') if not version: version = pkg_info.metadata_version package = pkg_info.package test_dir = os.path.join(self.package_testdir, package.name) clone_dir = os.path.join(test_dir, 'clones') stage_script_dir = os.path.join(test_dir, 'scripts', 'packages') stage_plugin_dir = os.path.join(test_dir, 'plugins', 'packages') delete_path(test_dir) make_dir(clone_dir) make_dir(stage_script_dir) make_dir(stage_plugin_dir) request = [(package.qualified_name(), version)] invalid_deps, new_pkgs = self.validate_dependencies(request, True) if invalid_deps: return (invalid_deps, False, test_dir) pkgs = [] pkgs.append((pkg_info, version)) for info, version, _ in new_pkgs: pkgs.append((info, version)) # Clone all packages, checkout right version, and build/install to # staging area. for info, version in pkgs: clonepath = os.path.join(clone_dir, info.package.name) try: clone = _clone_package(info.package, clonepath, version) except git.exc.GitCommandError as error: LOG.warning('failed to clone git repo: %s', error) return ('failed to clone {}'.format(info.package.git_url), False, test_dir) try: _git_checkout(clone, version) except git.exc.GitCommandError as error: LOG.warning('failed to checkout git repo version: %s', error) return (str.format('failed to checkout {} of {}', version, info.package.git_url), False, test_dir) fail_msg = self._stage(info.package, version, clone, stage_script_dir, stage_plugin_dir) if fail_msg: return (fail_msg, False, test_dir) # Finally, run tests (with correct environment set) test_command = pkg_info.metadata['test_command'] zeek_config = find_program('zeek-config') path_option = '--zeekpath' if not zeek_config: zeek_config = find_program('bro-config') path_option = '--bropath' zeekpath = os.environ.get('ZEEKPATH') if not zeekpath: zeekpath = os.environ.get('BROPATH') pluginpath = os.environ.get('ZEEK_PLUGIN_PATH') if not pluginpath: pluginpath = os.environ.get('BRO_PLUGIN_PATH') if zeek_config: cmd = subprocess.Popen([zeek_config, path_option, '--plugin_dir'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) line1 = read_zeek_config_line(cmd.stdout) line2 = read_zeek_config_line(cmd.stdout) if not zeekpath: zeekpath = line1 if not pluginpath: pluginpath = line2 else: LOG.warning('zeek-config not found when running tests for %s', package.name) return ('no "zeek-config" or "bro-config" found in PATH', False, test_dir) zeekpath = os.path.dirname(stage_script_dir) + ':' + zeekpath pluginpath = os.path.dirname(stage_plugin_dir) + ':' + pluginpath env = os.environ.copy() env['ZEEKPATH'] = zeekpath env['ZEEK_PLUGIN_PATH'] = pluginpath env['BROPATH'] = zeekpath env['BRO_PLUGIN_PATH'] = pluginpath cwd = os.path.join(clone_dir, package.name) outfile = os.path.join(cwd, 'zkg.test_command.stdout') errfile = os.path.join(cwd, 'zkg.test_command.stderr') LOG.debug('running test_command for %s with cwd="%s"' ' and ZEEKPATH/BROPATH="%s": %s', package.name, cwd, zeekpath, test_command) with open(outfile, 'w') as test_stdout, open(errfile, 'w') as test_stderr: cmd = subprocess.Popen(test_command, shell=True, cwd=cwd, env=env, stdout=test_stdout, stderr=test_stderr) return ('', cmd.wait() == 0, test_dir) def _stage(self, package, version, clone, stage_script_dir, stage_plugin_dir): metadata_file = _pick_metadata_file(clone.working_dir) # First use raw parser so no value interpolation takes place. raw_metadata_parser = configparser.RawConfigParser() invalid_reason = _parse_package_metadata( raw_metadata_parser, metadata_file) if invalid_reason: return invalid_reason raw_metadata = _get_package_metadata(raw_metadata_parser) requested_user_vars = user_vars(raw_metadata) if requested_user_vars is None: return "package has malformed 'user_vars' metadata field" substitutions = { 'bro_dist': self.zeek_dist, 'zeek_dist': self.zeek_dist, } substitutions.update(self.user_vars) for k, v, _ in requested_user_vars: val_from_env = os.environ.get(k) if val_from_env: substitutions[k] = val_from_env if k not in substitutions: substitutions[k] = v metadata_parser = GoodConfigParser(defaults=substitutions) invalid_reason = _parse_package_metadata( metadata_parser, metadata_file) if invalid_reason: return invalid_reason metadata = _get_package_metadata(metadata_parser) LOG.debug('building "%s": version %s', package, version) build_command = metadata.get('build_command', '') if build_command: LOG.debug('building "%s": running build_command: %s', package, build_command) bufsize = 4096 build = subprocess.Popen(build_command, shell=True, cwd=clone.working_dir, bufsize=bufsize, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: buildlog = self.package_build_log(clone.working_dir) with open(buildlog, 'wb') as f: LOG.info('installing "%s": writing build log: %s', package, buildlog) f.write(u'=== STDERR ===\n'.encode(std_encoding(sys.stderr))) while True: data = build.stderr.read(bufsize) if data: f.write(data) else: break f.write(u'=== STDOUT ===\n'.encode(std_encoding(sys.stdout))) while True: data = build.stdout.read(bufsize) if data: f.write(data) else: break except EnvironmentError as error: LOG.warning( 'installing "%s": failed to write build log %s %s: %s', package, buildlog, error.errno, error.strerror) returncode = build.wait() if returncode != 0: return 'package build_command failed, see log in {}'.format( buildlog) pkg_script_dir = metadata.get('script_dir', '') script_dir_src = os.path.join(clone.working_dir, pkg_script_dir) script_dir_dst = os.path.join(stage_script_dir, package.name) if not os.path.exists(script_dir_src): return str.format("package's 'script_dir' does not exist: {}", pkg_script_dir) pkgload = os.path.join(script_dir_src, '__load__.') # Check if __load__.bro exists for compatibility with older packages if os.path.isfile(pkgload + 'zeek') or os.path.isfile(pkgload + 'bro'): try: symlink_path = os.path.join(os.path.dirname(stage_script_dir), package.name) make_symlink(os.path.join('packages', package.name), symlink_path) for alias in aliases(metadata): symlink_path = os.path.join( os.path.dirname(stage_script_dir), alias) make_symlink(os.path.join('packages', package.name), symlink_path) except OSError as exception: error = 'could not create symlink at {}'.format(symlink_path) error += ': {}: {}'.format(type(exception).__name__, exception) return error error = _copy_package_dir(package, 'script_dir', script_dir_src, script_dir_dst, self.scratch_dir) if error: return error else: if 'script_dir' in metadata: return str.format("no __load__.zeek file found" " in package's 'script_dir' : {}", pkg_script_dir) else: LOG.warning('installing "%s": no __load__.zeek in implicit' ' script_dir, skipped installing scripts', package) pkg_plugin_dir = metadata.get('plugin_dir', 'build') plugin_dir_src = os.path.join(clone.working_dir, pkg_plugin_dir) plugin_dir_dst = os.path.join(stage_plugin_dir, package.name) if not os.path.exists(plugin_dir_src): LOG.info('installing "%s": package "plugin_dir" does not exist: %s', package, pkg_plugin_dir) if pkg_plugin_dir != 'build': # It's common for a package to not have build directory for # for plugins, so don't error out in that case, just log it. return str.format("package's 'plugin_dir' does not exist: {}", pkg_plugin_dir) error = _copy_package_dir(package, 'plugin_dir', plugin_dir_src, plugin_dir_dst, self.scratch_dir) if error: return error def install(self, pkg_path, version=''): """Install a package. Args: pkg_path (str): the full git URL of a package or the shortened path/name that refers to it within a package source. E.g. for a package source called "zeek" with package named "foo" in :file:`alice/zkg.index`, the following inputs may refer to the package: "foo", "alice/foo", or "zeek/alice/foo". version (str): if not given, then the latest git version tag is installed (or if no version tags exist, the "master" branch is installed). If given, it may be either a git version tag, a git branch name, or a git commit hash. Returns: str: empty string if package installation succeeded else an error string explaining why it failed. Raises: IOError: if the manifest can't be written """ pkg_path = canonical_url(pkg_path) LOG.debug('installing "%s"', pkg_path) ipkg = self.find_installed_package(pkg_path) if ipkg: conflict = ipkg.package if conflict.qualified_name().endswith(pkg_path): LOG.debug('installing "%s": re-install: %s', pkg_path, conflict) clonepath = os.path.join(self.package_clonedir, conflict.name) _clone_package(conflict, clonepath, version) return self._install(conflict, version) else: LOG.info( 'installing "%s": matched already installed package: %s', pkg_path, conflict) return str.format( 'package with name "{}" ({}) is already installed', conflict.name, conflict) matches = self.match_source_packages(pkg_path) if not matches: try: package = Package(git_url=pkg_path) return self._install(package, version) except git.exc.GitCommandError as error: LOG.info('installing "%s": invalid git repo path: %s', pkg_path, error) LOG.info('installing "%s": matched no source package', pkg_path) return 'package not found in sources and also not a valid git URL' if len(matches) > 1: matches_string = [match.qualified_name() for match in matches] LOG.info('installing "%s": matched multiple packages: %s', pkg_path, matches_string) return str.format('"{}" matches multiple packages, try a more' ' specific name from: {}', pkg_path, matches_string) try: return self._install(matches[0], version) except git.exc.GitCommandError as error: LOG.warning('installing "%s": source package git repo is invalid', pkg_path) return 'failed to clone package "{}": {}'.format(pkg_path, error) return '' def _install(self, package, version, use_existing_clone=False): """Install a :class:`.package.Package`. Returns: str: empty string if package installation succeeded else an error string explaining why it failed. Raises: git.exc.GitCommandError: if the git repo is invalid IOError: if the package manifest file can't be written """ clonepath = os.path.join(self.package_clonedir, package.name) ipkg = self.find_installed_package(package.name) if use_existing_clone or ipkg: clone = git.Repo(clonepath) else: clone = _clone_package(package, clonepath, version) status = PackageStatus() status.is_loaded = ipkg.status.is_loaded if ipkg else False status.is_pinned = ipkg.status.is_pinned if ipkg else False version_tags = _get_version_tags(clone) if version: if _is_commit_hash(clone, version): status.tracking_method = TRACKING_METHOD_COMMIT elif version in version_tags: status.tracking_method = TRACKING_METHOD_VERSION else: branches = _get_branch_names(clone) if version in branches: status.tracking_method = TRACKING_METHOD_BRANCH else: LOG.info( 'branch "%s" not in available branches: %s', version, branches) return 'no such branch or version tag: "{}"'.format(version) else: if len(version_tags): version = version_tags[-1] status.tracking_method = TRACKING_METHOD_VERSION else: if 'master' not in _get_branch_names(clone): return 'git repo has no "master" branch or version tags' version = 'master' status.tracking_method = TRACKING_METHOD_BRANCH status.current_version = version _git_checkout(clone, version) status.current_hash = clone.head.object.hexsha status.is_outdated = _is_clone_outdated( clone, version, status.tracking_method) metadata_file = _pick_metadata_file(clone.working_dir) # Use raw parser so no value interpolation takes place. raw_metadata_parser = configparser.RawConfigParser() invalid_reason = _parse_package_metadata( raw_metadata_parser, metadata_file) if invalid_reason: return invalid_reason raw_metadata = _get_package_metadata(raw_metadata_parser) fail_msg = self._stage(package, version, clone, self.script_dir, self.plugin_dir) if fail_msg: return fail_msg if not package.source: # If installing directly from git URL, see if it actually is found # in a package source and fill in those details. for pkg in self.source_packages(): if pkg.git_url == package.git_url: package.source = pkg.source package.directory = pkg.directory package.metadata = pkg.metadata break package.metadata = raw_metadata self.installed_pkgs[package.name] = InstalledPackage(package, status) self._write_manifest() LOG.debug('installed "%s"', package) return '' def _normalize_version_tag(tag): # Change vX.Y.Z into X.Y.Z if len(tag) > 1 and tag[0] is 'v' and tag[1].isdigit(): return tag[1:] return tag def _get_version_tags(clone): tags = [] for tagref in clone.tags: tag = str(tagref.name) normal_tag = _normalize_version_tag(tag) try: sv = semver.Version.coerce(normal_tag) except ValueError: # Skip tags that aren't compatible semantic versions. continue else: tags.append((normal_tag, tag, sv)) return [t[1] for t in sorted(tags, key=lambda e: e[2])] def _get_branch_names(clone): rval = [] for ref in clone.references: branch_name = str(ref.name) if not branch_name.startswith('origin/'): continue rval.append(branch_name.split('origin/')[1]) return rval def _is_version_outdated(clone, version): version_tags = _get_version_tags(clone) latest = _normalize_version_tag(version_tags[-1]) return _normalize_version_tag(version) != latest def _is_branch_outdated(clone, branch): it = clone.iter_commits('{0}..origin/{0}'.format(branch)) num_commits_behind = sum(1 for c in it) return num_commits_behind > 0 def _is_clone_outdated(clone, ref_name, tracking_method): if tracking_method == TRACKING_METHOD_VERSION: return _is_version_outdated(clone, ref_name) elif tracking_method == TRACKING_METHOD_BRANCH: return _is_branch_outdated(clone, ref_name) elif tracking_method == TRACKING_METHOD_COMMIT: return False else: raise NotImplementedError def _is_commit_hash(clone, text): try: commit = clone.commit(text) return commit.hexsha.startswith(text) except Exception: return False def _copy_package_dir(package, dirname, src, dst, scratch_dir): """Copy a directory from a package to its installation location. Returns: str: empty string if package dir copy succeeded else an error string explaining why it failed. """ if not os.path.exists(src): return '' if os.path.isfile(src) and tarfile.is_tarfile(src): tmp_dir = os.path.join(scratch_dir, 'untar') delete_path(tmp_dir) make_dir(tmp_dir) try: with tarfile.open(src) as tf: tf.extractall(tmp_dir) except Exception as error: return str(error) ld = os.listdir(tmp_dir) if len(ld) != 1: return 'failed to copy package {}: invalid tarfile'.format(dirname) src = os.path.join(tmp_dir, ld[0]) if not os.path.isdir(src): return 'failed to copy package {}: not a dir or tarfile'.format(dirname) def ignore(_, files): rval = [] for f in files: if f in {'.git', 'bro-pkg.meta', 'zkg.meta'}: rval.append(f) return rval try: copy_over_path(src, dst, ignore=ignore) except shutil.Error as error: errors = error.args[0] reasons = "" for err in errors: src, dst, msg = err reason = 'failed to copy {}: {} -> {}: {}'.format( dirname, src, dst, msg) reasons += '\n' + reason LOG.warning('installing "%s": %s', package, reason) return 'failed to copy package {}: {}'.format(dirname, reasons) return '' def _git_checkout(clone, version): """Checkout a version of a git repo along with any associated submodules. Args: clone (git.Repo): the git clone on which to operate version (str): the branch, tag, or commit to checkout Raises: git.exc.GitCommandError: if the git repo is invalid """ clone.git.checkout(version) clone.git.submodule('sync', '--recursive') clone.git.submodule('update', '--recursive', '--init') def _create_readme(file_path): if os.path.exists(file_path): return with open(file_path, 'w') as f: f.write('WARNING: This directory is managed by zkg.\n') f.write("Don't make direct modifications to anything within it.\n") def _clone_package(package, clonepath, version): """Clone a :class:`.package.Package` git repo. Returns: git.Repo: the cloned package Raises: git.exc.GitCommandError: if the git repo is invalid """ delete_path(clonepath) shallow = not is_sha1(version) return git_clone(package.git_url, clonepath, shallow=shallow) def _get_package_metadata(parser): metadata = {item[0]: item[1] for item in parser.items('package')} return metadata def _pick_metadata_file(directory): rval = os.path.join(directory, METADATA_FILENAME) if os.path.exists(rval): return rval return os.path.join(directory, LEGACY_METADATA_FILENAME) def _parse_package_metadata(parser, metadata_file): """Return string explaining why metadata is invalid, or '' if valid. """ if not parser.read(metadata_file): LOG.warning('%s: missing metadata file', metadata_file) return 'missing {} (or {}) metadata file'.format( METADATA_FILENAME, LEGACY_METADATA_FILENAME) if not parser.has_section('package'): LOG.warning('%s: metadata missing [package]', metadata_file) return '{} is missing [package] section'.format( os.path.basename(metadata_file)) return '' def _info_from_clone(clone, package, status, version): """Retrieves information about a package. Returns: A :class:`.package.PackageInfo` object. """ versions = _get_version_tags(clone) if _is_commit_hash(clone, version): version_type = TRACKING_METHOD_COMMIT elif version in versions: version_type = TRACKING_METHOD_VERSION else: version_type = TRACKING_METHOD_BRANCH metadata_file = _pick_metadata_file(clone.working_dir) # Use raw parser so no value interpolation takes place. metadata_parser = configparser.RawConfigParser() invalid_reason = _parse_package_metadata( metadata_parser, metadata_file) if invalid_reason: return PackageInfo(package=package, invalid_reason=invalid_reason, status=status, versions=versions, metadata_version=version, version_type=version_type, metadata_file=metadata_file) metadata = _get_package_metadata(metadata_parser) return PackageInfo(package=package, invalid_reason=invalid_reason, status=status, metadata=metadata, versions=versions, metadata_version=version, version_type=version_type, metadata_file=metadata_file) package-manager-2.0.7/zeekpkg/source.py0000644000175000017500000001155613551142424017414 0ustar rhaistrhaist""" A module containing the definition of a "package source": a git repository containing a collection of :file:`zkg.index` (or legacy :file:`bro-pkg.index`) files. These are simple INI files that can describe many Zeek packages. Each section of the file names a Zeek package along with the git URL where it is located and metadata tags that help classify/describe it. """ import os import shutil import git try: from backports import configparser except ImportError as err: import configparser from . import LOG from .package import ( name_from_path, Package ) from ._util import git_clone #: The name of package index files. INDEX_FILENAME = 'zkg.index' LEGACY_INDEX_FILENAME = 'bro-pkg.index' #: The name of the package source file where package metadata gets aggregated. AGGREGATE_DATA_FILE = 'aggregate.meta' class Source(object): """A Zeek package source. This class contains properties of a package source like its name, remote git URL, and local git clone. Attributes: name (str): The name of the source as given by a config file key in it's ``[sources]`` section. git_url (str): The git URL of the package source. clone (git.Repo): The local git clone of the package source. """ def __init__(self, name, clone_path, git_url): """Create a package source. Raises: git.exc.GitCommandError: if the git repo is invalid OSError: if the git repo is invalid and can't be re-initialized """ git_url = os.path.expanduser(git_url) self.name = name self.git_url = git_url self.clone = None try: self.clone = git.Repo(clone_path) except git.exc.NoSuchPathError: LOG.debug('creating source clone of "%s" at %s', name, clone_path) self.clone = git_clone(git_url, clone_path, shallow=True) except git.exc.InvalidGitRepositoryError: LOG.debug('deleting invalid source clone of "%s" at %s', name, clone_path) shutil.rmtree(clone_path) self.clone = git_clone(git_url, clone_path, shallow=True) else: LOG.debug('found source clone of "%s" at %s', name, clone_path) old_url = self.clone.git.config('--local', '--get', 'remote.origin.url') if git_url != old_url: LOG.debug( 'url of source "%s" changed from %s to %s, reclone at %s', name, old_url, git_url, clone_path) shutil.rmtree(clone_path) self.clone = git_clone(git_url, clone_path, shallow=True) def __str__(self): return self.git_url def __repr__(self): return self.git_url def package_index_files(self): """Return a list of paths to package index files in the source.""" rval = [] visited_dirs = set() for root, dirs, files in os.walk(self.clone.working_dir, followlinks=True): stat = os.stat(root) visited_dirs.add((stat.st_dev, stat.st_ino)) dirs_to_visit_next = [] for d in dirs: stat = os.stat(os.path.join(root, d)) if (stat.st_dev, stat.st_ino) not in visited_dirs: dirs_to_visit_next.append(d) dirs[:] = dirs_to_visit_next try: dirs.remove('.git') except ValueError: pass for filename in files: if filename == INDEX_FILENAME or filename == LEGACY_INDEX_FILENAME: rval.append(os.path.join(root, filename)) return sorted(rval) def packages(self): """Return a list of :class:`.package.Package` in the source.""" rval = [] # Use raw parser so no value interpolation takes place. parser = configparser.RawConfigParser() aggregate_file = os.path.join( self.clone.working_dir, AGGREGATE_DATA_FILE) parser.read(aggregate_file) for index_file in self.package_index_files(): relative_path = index_file[len(self.clone.working_dir) + 1:] directory = os.path.dirname(relative_path) lines = [] with open(index_file) as f: lines = [line.rstrip('\n') for line in f] for url in lines: pkg_name = name_from_path(url) agg_key = os.path.join(directory, pkg_name) metadata = {} if parser.has_section(agg_key): metadata = {key: value for key, value in parser.items(agg_key)} package = Package(git_url=url, source=self.name, directory=directory, metadata=metadata) rval.append(package) return rval package-manager-2.0.7/zeekpkg/package.py0000644000175000017500000003521013551142424017500 0ustar rhaistrhaist""" A module with various data structures used for interacting with and querying the properties and status of Zeek packages. """ import os import re from ._util import ( remove_trailing_slashes, find_sentence_end, ) #: The name of files used by packages to store their metadata. METADATA_FILENAME = 'zkg.meta' LEGACY_METADATA_FILENAME = 'bro-pkg.meta' TRACKING_METHOD_VERSION = 'version' TRACKING_METHOD_BRANCH = 'branch' TRACKING_METHOD_COMMIT = 'commit' PLUGIN_MAGIC_FILE = '__bro_plugin__' PLUGIN_MAGIC_FILE_DISABLED = '__bro_plugin__.disabled' def name_from_path(path): """Returns the name of a package given a path to its git repository.""" return canonical_url(path).split('/')[-1] def canonical_url(path): """Returns the url of a package given a path to its git repo.""" url = remove_trailing_slashes(path) if url.startswith('.') or url.startswith('/'): url = os.path.realpath(url) return url def aliases(metadata_dict): """Return a list of package aliases found in metadata's 'aliases' field.""" if 'aliases' not in metadata_dict: return [] import re return re.split(',\s*|\s+', metadata_dict['aliases']) def tags(metadata_dict): """Return a list of tag strings found in the metadata's 'tags' field.""" if 'tags' not in metadata_dict: return [] import re return re.split(',\s*', metadata_dict['tags']) def short_description(metadata_dict): """Returns the first sentence of the metadata's 'desciption' field.""" if 'description' not in metadata_dict: return '' description = metadata_dict['description'] lines = description.split('\n') rval = '' for line in lines: line = line.lstrip() rval += ' ' period_idx = find_sentence_end(line) if period_idx == -1: rval += line else: rval += line[:period_idx + 1] break return rval.lstrip() def user_vars(metadata_dict): """Returns a list of (str, str, str) from metadata's 'user_vars' field. Each entry in the returned list is a the name of a variable, it's value, and its description. If the 'user_vars' field is not present, an empty list is returned. If it is malformed, then None is returned. """ text = metadata_dict.get('user_vars') if not text: return [] rval = [] text = text.strip() entries = re.split('(\w+\s+\\[.*\\]\s+".*")\s+', text) entries = list(filter(None, entries)) for entry in entries: m = re.match('(\w+)\s+\\[(.*)\\]\s+"(.*)"', entry) if not m: return None groups = m.groups() if len(groups) != 3: return None rval.append((groups[0], groups[1], groups[2])) return rval def dependencies(metadata_dict, field='depends'): """Returns a dictionary of (str, str) based on metadata's dependency field. The keys indicate the name of a package (shorthand name or full git URL). The names 'zeek' or 'zkg' may also be keys that indicate a dependency on a particular Zeek or zkg version. The values indicate a semantic version requirement. If the dependency field is malformed (e.g. number of keys not equal to number of values), then None is returned. """ if field not in metadata_dict: return dict() rval = dict() depends = metadata_dict[field] parts = depends.split() keys = parts[::2] values = parts[1::2] if len(keys) != len(values): return None for i, k in enumerate(keys): if i < len(values): rval[k] = values[i] return rval class InstalledPackage(object): """An installed package and its current status. Attributes: package (:class:`Package`): the installed package status (:class:`PackageStatus`): the status of the installed package """ def __init__(self, package, status): self.package = package self.status = status def __lt__(self, other): return str(self.package) < str(other.package) class PackageStatus(object): """The status of an installed package. This class contains properties of a package related to how the package manager will operate on it. Attributes: is_loaded (bool): whether a package is marked as "loaded". is_pinned (bool): whether a package is allowed to be upgraded. is_outdated (bool): whether a newer version of the package exists. tracking_method (str): either "branch", "version", or "commit" to indicate (respectively) whether package upgrades should stick to a git branch, use git version tags, or do nothing because the package is to always use a specific git commit hash. current_version (str): the current version of the installed package, which is either a git branch name or a git version tag. current_hash (str): the git sha1 hash associated with installed package's current version/commit. """ def __init__(self, is_loaded=False, is_pinned=False, is_outdated=False, tracking_method=None, current_version=None, current_hash=None): self.is_loaded = is_loaded self.is_pinned = is_pinned self.is_outdated = is_outdated self.tracking_method = tracking_method self.current_version = current_version self.current_hash = current_hash class PackageInfo(object): """Contains information on an arbitrary package. If the package is installed, then its status is also available. Attributes: package (:class:`Package`): the relevant Zeek package status (:class:`PackageStatus`): this attribute is set for installed packages metadata (dict of str -> str): the contents of the package's :file:`zkg.meta` or :file:`bro-pkg.meta` versions (list of str): a list of the package's availabe git version tags metadata_version: the package version that the metadata is from version_type: either 'version', 'branch', or 'commit' to indicate whether the package info/metadata was taken from a release version tag, a branch, or a specific commit hash. invalid_reason (str): this attribute is set when there is a problem with gathering package information and explains what went wrong. metadata_file: the absolute path to the :file:`zkg.meta` or :file:`bro-pkg.meta` for this package. Use this if you'd like to parse the metadata yourself. May not be defined, in which case the value is None. """ def __init__(self, package=None, status=None, metadata=None, versions=None, metadata_version='', invalid_reason='', version_type='', metadata_file=None): self.package = package self.status = status self.metadata = {} if metadata is None else metadata self.versions = [] if versions is None else versions self.metadata_version = metadata_version self.version_type = version_type self.invalid_reason = invalid_reason self.metadata_file = metadata_file def aliases(self): """Return a list of package name aliases. The canonical one is listed first. """ return aliases(self.metadata) def tags(self): """Return a list of keyword tags associated with the package. This will be the contents of the package's `tags` field.""" return tags(self.metadata) def short_description(self): """Return a short description of the package. This will be the first sentence of the package's 'description' field.""" return short_description(self.metadata) def dependencies(self, field='depends'): """Returns a dictionary of dependency -> version strings. The keys indicate the name of a package (shorthand name or full git URL). The names 'zeek' or 'zkg' may also be keys that indicate a dependency on a particular Zeek or zkg version. The values indicate a semantic version requirement. If the dependency field is malformed (e.g. number of keys not equal to number of values), then None is returned. """ return dependencies(self.metadata, field) def user_vars(self): """Returns a list of (str, str, str) from metadata's 'user_vars' field. Each entry in the returned list is a the name of a variable, it's value, and its description. If the 'user_vars' field is not present, an empty list is returned. If it is malformed, then None is returned. """ return user_vars(self.metadata) def best_version(self): """Returns the best/latest version of the package that is available. If the package has any git release tags, this returns the highest one, else it returns the 'master' branch. """ if self.versions: return self.versions[-1] return 'master' class Package(object): """A Zeek package. This class contains properties of a package that are defined by the package git repository itself and the package source it came from. Attributes: git_url (str): the git URL which uniquely identifies where the Zeek package is located name (str): the canonical name of the package, which is always the last component of the git URL path source (str): the package source this package comes from, which may be empty if the package is not a part of a source (i.e. the user is referring directly to the package's git URL). directory (str): the directory within the package source where the :file:`zkg.index` containing this package is located. E.g. if the package source has a package named "foo" declared in :file:`alice/zkg.index`, then `dir` is equal to "alice". It may also be empty if the package is not part of a package source or if it's located in a top-level :file:`zkg.index` file. metadata (dict of str -> str): the contents of the package's :file:`zkg.meta` or :file:`bro-pkg.meta` file. If the package has not been installed then this information may come from the last aggregation of the source's :file:`aggregate.meta` file (it may not be accurate/up-to-date). """ def __init__(self, git_url, source='', directory='', metadata=None, name=None, canonical=False): self.git_url = git_url self.source = source self.directory = directory self.metadata = {} if metadata is None else metadata self.name = name if not canonical: self.git_url = canonical_url(git_url) if not source and os.path.exists(git_url): # Ensures getting real path of relative directories. # e.g. canonical_url catches "./foo" but not "foo" self.git_url = os.path.realpath(self.git_url) self.name = name_from_path(git_url) def __str__(self): return self.qualified_name() def __repr__(self): return self.git_url def __lt__(self, other): return str(self) < str(other) def aliases(self): """Return a list of package name aliases. The canonical one is listed first. """ return aliases(self.metadata) def tags(self): """Return a list of keyword tags associated with the package. This will be the contents of the package's `tags` field and may return results from the source's aggregated metadata if the package has not been installed yet.""" return tags(self.metadata) def short_description(self): """Return a short description of the package. This will be the first sentence of the package's 'description' field and may return results from the source's aggregated metadata if the package has not been installed yet.""" return short_description(self.metadata) def dependencies(self, field='depends'): """Returns a dictionary of dependency -> version strings. The keys indicate the name of a package (shorthand name or full git URL). The names 'zeek' or 'zkg' may also be keys that indicate a dependency on a particular Zeek or zkg version. The values indicate a semantic version requirement. If the dependency field is malformed (e.g. number of keys not equal to number of values), then None is returned. """ return dependencies(self.metadata, field) def user_vars(self): """Returns a list of (str, str, str) from metadata's 'user_vars' field. Each entry in the returned list is a the name of a variable, it's value, and its description. If the 'user_vars' field is not present, an empty list is returned. If it is malformed, then None is returned. """ return user_vars(self.metadata) def name_with_source_directory(self): """Return the package's within its package source. E.g. for a package source with a package named "foo" in :file:`alice/zkg.index`, this method returns "alice/foo". If the package has no source or sub-directory within the source, then just the package name is returned. """ if self.directory: return '{}/{}'.format(self.directory, self.name) return self.name def qualified_name(self): """Return the shortest name that qualifies/distinguishes the package. If the package is part of a source, then this returns "source_name/:meth:`name_with_source_directory()`", else the package's git URL is returned. """ if self.source: return '{}/{}'.format(self.source, self.name_with_source_directory()) return self.git_url def matches_path(self, path): """Return whether this package has a matching path/name. E.g for a package with :meth:`qualified_name()` of "zeek/alice/foo", the following inputs will match: "foo", "alice/foo", "zeek/alice/foo" """ path_parts = path.split('/') if self.source: pkg_path = self.qualified_name() pkg_path_parts = pkg_path.split('/') for i, part in reversed(list(enumerate(path_parts))): ri = i - len(path_parts) if part != pkg_path_parts[ri]: return False return True else: if len(path_parts) == 1 and path_parts[-1] == self.name: return True return path == self.git_url package-manager-2.0.7/.gitignore0000644000175000017500000000005613551142424016063 0ustar rhaistrhaist*.pyc *.egg-info .state doc/_build build dist package-manager-2.0.7/.update-changes.cfg0000644000175000017500000000065113551142424017523 0ustar rhaistrhaist# Automatically adapt version in files. function replace_python_package_version { file=$1 version=$2 cat $file | sed "s#^\\( *__version__ *= *\\)\"\\([0-9.-]\\{1,\\}\\)\"#\1\"$version\"#g" >$file.tmp cat $file.tmp >$file rm -f $file.tmp git add $file } function new_version_hook { version=$1 replace_python_package_version zeekpkg/__init__.py $version make man git add ./doc/man } package-manager-2.0.7/CHANGES0000644000175000017500000007731413551142424015101 0ustar rhaistrhaist 2.0.7 | 2019-10-14 11:56:18 -0700 * Release 2.0.7. 2.0.6-3 | 2019-10-14 11:53:46 -0700 * GH-55: Add fallback option to getting user_vars. (Vlad Grigorescu) This generally fixes errors when installing packages that use "user_vars", but the user's zkg config file doesn't have a corresponding key. And that can happen simply by (separately) installing multiple packages that each use "user_vars". 2.0.6 | 2019-09-20 13:06:48 -0700 * Release 2.0.6. 2.0.5-2 | 2019-09-20 13:03:11 -0700 * GH-54: improve error message for package test failures (Jon Siwek, Corelight) * Improve debug logging for test_command (Jon Siwek, Corelight) 2.0.5 | 2019-09-18 16:16:21 -0700 * Release 2.0.5. 2.0.4-1 | 2019-09-18 16:09:19 -0700 * Improve dependency handling (Jon Siwek, Corelight) It now won't report unsatisfiable dependencies between two nodes in the dependency graph that are already installed. E.g. previously, if one used the --nodeps flag to install package "foo" that had a failed dependency on Zeek, then installing unrelated package "bar" without --nodeps would emit an error about "foo" dependencies being unsatisfiable even though that has no relation to the current operation. 2.0.4 | 2019-08-28 15:50:13 -0700 * Release 2.0.4. 2.0.3-2 | 2019-08-28 15:49:54 -0700 * Bugfix: incorrect arguments to Manager.has_plugin() (Christian Kreibich, Corelight) 2.0.3 | 2019-08-26 14:18:12 -0700 * Release 2.0.3. 2.0.2-2 | 2019-08-26 14:16:36 -0700 * When loading/unloading a Zeek package, also enable/disable any plugin (Christian Kreibich, Corelight) This patch does this via renaming of the __bro_plugin__ magic file that Zeek looks for when scanning ZEEK_PLUGIN_PATH. To disable a plugin, zkg renames the file to __bro_plugin__.disabled, and renames that file back to __bro_plugin__ when enabling. 2.0.2 | 2019-07-17 10:46:14 -0700 * Release 2.0.2. 2.0.1-2 | 2019-07-17 10:44:50 -0700 * Simplify how "current_hash" is determined for installed packages (Jon Siwek, Corelight) For packages installed via version tag, this will also change the meaning of "current_hash" from being the hexsha of the tag object itself to the hexsha of the commit object pointed to by the tag. 2.0.1 | 2019-07-09 10:37:20 -0700 * Release 2.0.1. 2.0.0-1 | 2019-07-09 10:36:27 -0700 * Fix a broken link in docs (Jon Siwek, Corelight) 2.0.0 | 2019-06-11 19:33:36 -0700 * Release 2.0.0. 1.7.0-30 | 2019-06-11 19:33:13 -0700 * Fix version string replacement via update-changes (Jon Siwek, Corelight) 1.7.0-29 | 2019-06-11 19:26:22 -0700 * Update packaging scripts (Jon Siwek, Corelight) The new name on PyPi will be "zkg", but a duplicate package (with the same version number) under the old "bro-pkg" name is still built and uploaded along with it. * Change name of "bro_dist" Manager ctor arg to "zeek_dist" (Jon Siwek, Corelight) Note this is a breaking API change for those that were creating Manager objects using "bro_dist" as a named parameter. * Add manager.zeekpath and manager.zeek_plugin_path methods (Jon Siwek, Corelight) Same as manager.bropath and manager.bro_plugin_path. * Rename internal util functions that had "bro" in them (Jon Siwek, Corelight) * Replace "Bro" with "Zeek" in all documentation (Jon Siwek, Corelight) * Replace "bro" usages in unit tests (Jon Siwek, Corelight) * Support zkg.index files within package sources (Jon Siwek, Corelight) bro-pkg.index files, if they exist within a package source, are also still accepted and treated in the same way as before. * Allow "zeek" and "zkg" in the "depends" metadata field (Jon Siwek, Corelight) These are equivalent to the "bro" and "bro-pkg" dependencies that already existed. * Add preference for zkg.meta over bro-pkg.meta files (Jon Siwek, Corelight) The new, preferred package metadata file name is zkg.meta, but we'll still accept a bro-pkg.meta if that's all that exists. * Add zeek_dist config option (Jon Siwek, Corelight) This is treated the same way "bro_dist" worked, which is also still used as a fallback if no "zeek_dist" value is set. Package metadata files also now allow either "%(bro_dist)s" or "%(zeek_dist)s" substitutions. * Rename example config file to zkg.config (Jon Siwek, Corelight) * Change default package source name/url from bro to zeek (Jon Siwek, Corelight) * Change default config and state dirs to ~/.zkg (Jon Siwek, Corelight) The old, default ~/.bro-pkg will still be used if it already exists so users will be able to keep their current configurations without having to start over from scratch. * Add support for using zeek-config script (Jon Siwek, Corelight) With fallback to using bro-config if it exists. * Support new Zeek environment variables (Jon Siwek, Corelight) - ZEEKPATH (BROPATH still used as fallback) - ZEEK_PLUGIN_PATH (BRO_PLUGIN_PATH still used as fallback) * Rename bro-pkg man page to zkg (Jon Siwek, Corelight) * Allow ZKG_CONFIG_FILE environment variable (Jon Siwek, Corelight) Same functionality as the old BRO_PKG_CONFIG_FILE (which is still used as a fallback). * Rename internal env. var. used by setup.py (Jon Siwek, Corelight) * Remove gh-pages Makefile target (Jon Siwek, Corelight) * Add zeekpkg module (Jon Siwek, Corelight) The old bropkg module now emits deprecation warnings. * Add zkg executable file (Jon Siwek, Corelight) The bro-pkg script still exists as a wrapper that prints a deprecation warning by default. 1.7.0-7 | 2019-05-03 19:59:57 -0700 * Improve test_command failure output (Jon Siwek, Corelight) 1.7.0-6 | 2019-04-26 10:22:58 -0700 * Fix error when upgrading from older bro-pkg (Jon Siwek, Corelight) 1.7.0-5 | 2019-04-25 18:20:58 -0700 * Fix unbundling packages that contain submodules (Jon Siwek, Corelight) * Add more logging to unbundle process (Jon Siwek, Corelight) 1.7.0-3 | 2019-04-16 11:42:43 -0700 * Support .zeek file extension for upcoming Zeek 3.0 release (Daniel Thayer) 1.7.0 | 2019-04-12 09:27:36 -0700 * Release 1.7.0. 1.6.0-1 | 2019-04-12 09:24:54 -0700 * Add support for packages containing git submodules (Jon Siwek, Corelight) 1.6.0 | 2019-04-03 15:10:00 -0700 * Release 1.6.0. 1.5.6-3 | 2019-04-03 15:09:23 -0700 * Include bro-pkg.meta file info in the "bro-pkg info" output. (Christian Kreibich, Corelight) * Track the full bro-pkg.meta filename in package.PackageInfo. (Christian Kreibich, Corelight) 1.5.6 | 2019-03-27 14:18:39 -0700 * Release 1.5.6. 1.5.5-1 | 2019-03-27 14:16:40 -0700 * GH-47: give more bro-pkg commands a non-zero exit code on fail (Jon Siwek, Corelight) 1.5.5 | 2019-03-27 13:53:59 -0700 * Release 1.5.5. 1.5.4-2 | 2019-03-27 13:52:12 -0700 * GH-47: give `bro-pkg install` non-zero exit code on failure (Jon Siwek, Corelight) * Update documentation regarding --bro-dist (Jon Siwek, Corelight) 1.5.4 | 2019-03-21 20:17:31 -0700 * Release 1.5.4. 1.5.3-1 | 2019-03-21 20:12:41 -0700 * GH-46: fix --version to be able to use commit hash (Jon Siwek, Corelight) 1.5.3 | 2019-03-05 18:46:32 -0800 * Release 1.5.3. 1.5.2-2 | 2019-03-05 18:43:19 -0800 * GH-45: improve compatibility with older git versions (Jon Siwek, Corelight) 1.5.2-1 | 2019-02-13 17:26:48 -0800 * Use .bundle extension in bundle/unbundle docs per convention (Jon Siwek, Corelight) 1.5.2 | 2019-01-11 13:18:51 -0600 * Release 1.5.2. 1.5.1-5 | 2019-01-11 13:18:18 -0600 * Add docs for 'credits' metadata field (Jon Siwek, Corelight) 1.5.1-4 | 2019-01-11 11:59:35 -0600 * Swap Zeek in for Bro in Sphinx config (Jon Siwek, Corelight) 1.5.1-3 | 2019-01-10 17:59:09 -0600 * Fix RTD URL (Jon Siwek, Corelight) 1.5.1-2 | 2019-01-10 17:18:58 -0600 * Update URLs and some simple s/Bro/Zeek in README (Jon Siwek, Corelight) 1.5.1-1 | 2019-01-04 17:17:27 -0600 * GH-41: fix man page formatting of positional args (Jon Siwek, Corelight) 1.5.1 | 2018-12-17 12:21:41 -0600 * Release 1.5.1. * GH-40: improve `bro-pkg bundle --help` output (Jon Siwek, Corelight) 1.5.0 | 2018-12-12 15:32:36 -0600 * Release 1.5.0. 1.4.2-2 | 2018-12-12 15:31:47 -0600 * Add an 'aliases' package metadata field (Jon Siwek, Corelight) This is used for specifying alternate package names that one may @load it by, and may be generally useful when renaming package repositories while still supporting existing package installations under the old name. * Fix bro-pkg to be able to upgrade packages no longer in a source (Jon Siwek, Corelight) 1.4.2 | 2018-08-03 16:46:21 -0500 * Release 1.4.2. 1.4.1-1 | 2018-08-03 16:44:45 -0500 * Fix SafeConfigParser deprecation warning (Jon Siwek, Corelight) 1.4.1 | 2018-07-25 14:37:53 -0500 * Release 1.4.1. 1.4.0-1 | 2018-07-25 14:36:23 -0500 * Improve error message for invalid bundle manifest files (Jon Siwek, Corelight) 1.4.0 | 2018-07-12 11:34:00 -0500 * Release 1.4.0. 1.3.12-7 | 2018-07-12 11:31:14 -0500 * GH-35: allow category argument to `bro-pkg info` (Corelight) 1.3.12-6 | 2018-07-12 10:40:13 -0500 * GH-34: exit non-zero from `bro-pkg info` on lookup failure (Corelight) 1.3.12-5 | 2018-07-12 10:26:35 -0500 * Fix typos (Jon Zeolla) 1.3.12-3 | 2018-07-12 10:15:57 -0500 * Refactor tracking method strings into variables (Corelight) * Support installing packages via commit hash (Corelight) * Stabilize a unit test (Corelight) 1.3.12 | 2018-06-18 09:03:06 -0500 * Release 1.3.12. 1.3.11-3 | 2018-06-18 09:02:39 -0500 * Improve default character encoding handling (Corelight) 1.3.11-2 | 2018-06-18 08:22:17 -0500 * Fix --version for branch names containing a slash '/' (Michael Dopheide) 1.3.11 | 2018-05-29 08:31:19 -0500 * Release 1.3.11. 1.3.10-1 | 2018-05-29 08:29:54 -0500 * Fix invalid Package ctor syntax with Python 2 (Corelight) 1.3.10 | 2018-05-25 14:57:09 -0500 * Release 1.3.10. 1.3.9-1 | 2018-05-25 14:52:08 -0500 * Improve package info canonicalization (Corelight) 1.3.9 | 2018-05-24 16:48:19 -0500 * Fix for packages storing a relative path as their git URL (Corelight) 1.3.8-1 | 2018-05-23 16:00:10 -0500 * Fix typo in README (Corelight) 1.3.8 | 2018-05-10 10:42:00 -0500 * Release 1.3.8. 1.3.7-1 | 2018-05-10 10:39:18 -0500 * GH-32: support version tags prefixed by 'v' (e.g. vX.Y.Z) (Corelight) 1.3.7 | 2018-05-05 08:10:54 -0500 * Release 1.3.7. 1.3.6-1 | 2018-05-05 08:09:45 -0500 * GH-31: Improve error message when failing to find a usable git repo (Corelight) 1.3.6 | 2018-04-25 09:56:45 -0500 * Release 1.3.6. 1.3.5-1 | 2018-04-25 09:56:07 -0500 * GH-30: ignore interpolation errors during metadata aggregation (Corelight) 1.3.5 | 2018-03-28 14:13:29 -0500 * Release 1.3.5. 1.3.4-1 | 2018-03-28 14:12:38 -0500 * GH-29: Move requirements.doc.txt to generalized requirements.txt (Corelight) 1.3.4 | 2018-03-05 09:53:23 -0600 * Release 1.3.4. 1.3.3-2 | 2018-03-05 09:52:56 -0600 * Fix building docs on Sphinx 1.7+ (Corelight) 1.3.3-1 | 2018-03-05 09:31:45 -0600 * Fix running install/test/bundle commands on detached git clones (Corelight) 1.3.3 | 2018-02-15 11:04:09 -0600 * Release 1.3.3. 1.3.2-1 | 2018-02-15 11:02:20 -0600 * GH-26: fix error when config_file is not in script_dir or plugin_dir (Corelight) 1.3.2 | 2018-02-01 17:38:15 -0600 * Release 1.3.2. 1.3.1-5 | 2018-02-01 17:37:34 -0600 * Improve logging stream levels for a couple log messages (Corelight) * Change default confirmation prompt answer to 'no' on test failures (Corelight) 1.3.1-3 | 2018-02-01 17:17:54 -0600 * Minor doc improvements (Corelight) 1.3.1-2 | 2018-02-01 16:13:25 -0600 * Teach bro-pkg to emit an error when operating on dirty git clones When trying to use the install, test, or bundle commands on local git clones that have modifications or untracked files, bro-pkg now aborts with an error message. This is to help catch situations where one thinks their uncommitted local changes will be installed by bro-pkg, but actually will not be unless they are committed in the git repo. (Corelight) 1.3.1-1 | 2018-02-01 15:43:19 -0600 * Fix install command to be able to re-install an existing package Before, using 'install' on a previously-installed package would not correctly update the cached git repository so if there were any changes since the last installation they would not be seen after the operation completed. (Corelight) 1.3.1 | 2017-12-07 19:33:23 -0600 * Release 1.3.1. 1.3-1 | 2017-12-07 19:29:35 -0600 * GH-22: fix 'suggests' field in json output to use key-value pairs (Corelight) 1.3 | 2017-11-28 19:34:15 -0600 * Release 1.3. 1.2.6-2 | 2017-11-28 19:29:38 -0600 * GH-22: add a 'suggests' metadata field This new field can be used in the same way as 'depends', but is used for packages that are merely complementary and not strictly required for the suggesting package to function. A "(suggested)" indicator for install, bundle, and upgrade commands will now appear during their bro-pkg confirmation prompts. These commands also now have a --nosuggestions flag to ignore all suggested packages. This changes the bropkg.manager.validate_dependencies() API to return an additional boolean indicating whether a package was included in the list as a result of suggestions. (Corelight) 1.2.6-1 | 2017-11-27 09:46:02 -0600 * GH-24: improve script_dir metadata field documentation (Corelight) 1.2.6 | 2017-11-23 15:27:46 -0600 * Release 1.2.6. 1.2.5-1 | 2017-11-23 15:27:12 -0600 * GH-23: more "Edit on GitHub" breadcrumb customization. (Corelight) 1.2.5 | 2017-11-23 14:42:33 -0600 * Release 1.2.5. 1.2.4-1 | 2017-11-23 14:41:09 -0600 * GH-23: one more "Edit on GitHub" link fix attempt. (Corelight) 1.2.4 | 2017-11-23 13:57:00 -0600 * Release 1.2.4. 1.2.3-1 | 2017-11-23 13:54:11 -0600 * GH-23: another attempt to remove "Edit on GitHub" link. (Corelight) 1.2.3 | 2017-11-23 13:29:07 -0600 * Release 1.2.3. 1.2.2-2 | 2017-11-23 13:28:15 -0600 * GH-23: remove broken "Edit on GitHub" link from stable version of docs. (Corelight) 1.2.2-1 | 2017-11-23 13:27:06 -0600 * Add a missing dependency for building docs. (Corelight) 1.2.2 | 2017-10-16 16:14:02 -0500 * Release 1.2.2. 1.2.1-1 | 2017-10-16 16:11:37 -0500 * GH-21: again trying to give aggregate metadata consistent ordering (Jon Siwek) 1.2.1 | 2017-10-11 15:03:56 -0500 * Release 1.2.1. 1.2-1 | 2017-10-11 15:02:21 -0500 * Fix building PyPI distribution from Python 3+ It should now still include the configparser backport dependency. (Jon Siwek) 1.2 | 2017-09-29 13:15:37 -0500 * Release 1.2. 1.1-1 | 2017-09-29 13:15:00 -0500 * BIT-1852: allow 'bro-pkg' as a 'depends' field value This allows packages to depend on new features of bro-pkg not available in previous version. (Jon Siwek) 1.1 | 2017-09-28 18:46:30 -0500 * Release 1.1. 1.0.4-32 | 2017-09-28 18:45:54 -0500 * BIT-1852: allow plugin_dir to also accept tarfiles This allows packagers to specify a tarfile containing the Bro plugin instead of a directory. May be useful as the default Bro plugin CMake skeleton will end up producing a tarfile as part of the plugin build process that contains the minimal set of files comprising the plugin. (Jon Siwek) 1.0.4-31 | 2017-09-28 16:16:39 -0500 * GH-9: add new 'user_vars' bro-pkg.meta field and config file section This field allows for packages to solicit users for extra configuration settings which may then be referenced in that package's build_command. Useful for asking users for installation paths of external dependencies, which may differ between users/systems. (Jon Siwek) 1.0.4-30 | 2017-09-27 11:14:52 -0500 * GH-9: show 'external_depends' metadata during 'unbundle' command (Jon Siwek) * Add manager.bundle_info() to API. It retrieves information on all packages in a bundle without installing the bundle. (Jon Siwek) 1.0.4-28 | 2017-09-27 10:14:16 -0500 * Remove an error msg when upgrading packages that have no test_command (Jon Siwek) 1.0.4-27 | 2017-09-26 13:27:53 -0500 * GH-9: allow 'external_depends' field in bro-pkg.meta. The contents of this field get displayed to users in the install and upgrade commands to indicate extra pre-requisite software their OS should have installed. (Jon Siwek) 1.0.4-26 | 2017-09-26 11:41:37 -0500 * Put install's "unit test skipped" msg into log stream instead of stdout. (Jon Siwek) 1.0.4-25 | 2017-09-22 12:26:56 -0500 * GH-15: add --nodeps to install, bundle, and upgrade commands This option allows to bypasses dependency resolution/checking, accepting risk of causing broken/unusable package state. (Jon Siwek) 1.0.4-24 | 2017-09-20 13:11:48 -0500 * Code formatting. (Jon Siwek) 1.0.4-23 | 2017-09-20 13:07:57 -0500 * Add '--allvers' option to output metadata for ALL versions when outputting JSON. (Terry Fleury) * Add JSON output as option to 'info' command. (Terry Fleury) 1.0.4-18 | 2017-09-20 12:52:17 -0500 * Add 'info' option to force read metadata from remote GitHub. (Terry Fleury) 1.0.4-16 | 2017-09-20 12:39:21 -0500 * Version numbers should be sorted naturally, e.g., 1.10.0 is newer than 1.9.0. (Justin Azoff, Terry Fleury) 1.0.4-14 | 2017-09-20 12:31:59 -0500 * Add option for 'list' command '--nodesc' to suppress description output. (Terry Fleury) 1.0.4-12 | 2017-09-16 13:36:53 -0500 * GH-13: emit an error when a package uses a malformed 'depends' field (Jon Siwek) 1.0.4-11 | 2017-09-16 12:44:50 -0500 * Fix a typo in an error message. (Jon Siwek) 1.0.4-10 | 2017-09-15 13:04:46 -0500 * GH-12: use active branch as package version for local git repos (Jon Siwek) 1.0.4-9 | 2017-09-15 11:47:09 -0500 * GH-10: add a cross-reference for metadata value interpolation in docs (Jon Siwek) 1.0.4-8 | 2017-09-15 11:39:24 -0500 * GH-10: improve docs regarding metadata value interpolation (Jon Siwek) 1.0.4-6 | 2017-09-14 19:52:21 -0500 * Minor quickstart doc simplification. (Jon Siwek) 1.0.4-5 | 2017-09-14 18:56:16 -0500 * Require extra configparser package only for Python < 3.5 where it is not included (Hilko Bengen) * Fix imports for python3 (Robert Haist) 1.0.4-2 | 2017-09-14 15:05:36 -0500 * Add a docutils config file to prevent sphinx/smartypants "smart quotes" Without it, later versions of sphinx are turning quote characters into smart/curly quotes which breaks the generated man page. (Jon Siwek) 1.0.4-1 | 2017-09-14 15:03:04 -0500 * Quickstart doc improvements, add missing links to dependencies (Jon Siwek) 1.0.4 | 2017-07-18 11:41:56 -0500 * Release 1.0.4. 1.0.3-1 | 2017-07-18 11:39:38 -0500 * Give aggregate metadata file a consistent ordering Addresses https://github.com/bro/package-manager/issues/6 (Jon Siwek) 1.0.3 | 2017-06-23 10:17:26 -0500 * Release 1.0.3. 1.0.2-3 | 2017-06-23 10:15:41 -0500 * Install command now doesn't warn/prompt when package has no test_command. (Jon Siwek) * Fix a unit test. (Jon Siwek) * Improve usage documentation: add offline usage example. (Jon Siwek) 1.0.2 | 2017-06-07 22:11:20 -0500 * Release 1.0.2. 1.0.1-1 | 2017-06-07 22:04:55 -0500 * Improve confirmation prompt behavior. Just pressing enter defaults to "yes" and anything besides y/ye/yes (case insensitive) is treated as "no" and aborts. Addresses https://github.com/bro/package-manager/issues/5 (Jon Siwek) 1.0.1 | 2017-06-05 09:54:59 -0500 * Release 1.0.1. 1.0-3 | 2017-06-05 09:54:31 -0500 * Improved handling of packages without scripts. The 'load' and 'unload' commands now return more precise messages if used for packages without scripts. (Jan Grashoefer) 1.0-1 | 2017-02-06 21:02:31 -0600 * Return a proper "bro-config not found" message when running tests. (Jon Siwek) 1.0 | 2017-01-24 19:53:49 -0600 * Release 1.0. 0.9-24 | 2017-01-24 19:53:27 -0600 * Simply directions for uploading to PyPi. (Jon Siwek) 0.9-23 | 2017-01-24 19:41:22 -0600 * Add dependency on btest. (Jon Siwek) 0.9-22 | 2017-01-24 18:09:28 -0600 * Change manager.validate_dependencies API (Jon Siwek) 0.9-21 | 2017-01-21 12:40:09 -0600 * Doc fixes. (Jon Siwek) 0.9-20 | 2017-01-21 12:29:48 -0600 * Run package tests before install/upgrade. (Jon Siwek) * Add 'test' command and 'test_command' metadata field. (Jon Siwek) 0.9-16 | 2017-01-13 18:50:52 -0600 * Fix non-deterministic order of {installed,loaded}_packages() (Jon Siwek) * Add unit tests. (Jon Siwek) * Fix bundle/unbundle --help output. (Jon Siwek) * Don't copy a package's .git or bro-pkg.meta when installing them. (Jon Siwek) 0.9-8 | 2017-01-12 10:23:26 -0600 * Add 'depends' bro-pkg.meta field. (Jon Siwek) * Teach install/upgrade/bundle commands to operate on package dependencies. These commands now construct a dependency graph based on the packages the user has requested plus any already-installed packages and then validates the graph to make sure there are no dependency conflicts. If there are no conflicts, then the set of requested packages get installed/bundled along with any additional packages that were required to satisfy dependencies. For those additional packages, bro-pkg always tries to pick the latest release version that satisfies all dependency specifications. (Jon Siwek) 0.9-2 | 2017-01-09 11:16:43 -0600 * Improve 'info' output for packages w/ empty metadata files. (Jon Siwek) 0.9-1 | 2016-12-09 11:52:23 -0600 * Fix doc typo. (Jon Siwek) 0.9 | 2016-12-08 21:19:02 -0600 * Release 0.9. 0.8-6 | 2016-12-08 21:15:06 -0600 * Fix tracking package versions/changes via branches. A previous change to use shallow git clones broke the tracking of package versioning via git branches. (Jon Siwek) 0.8-5 | 2016-12-08 20:04:53 -0600 * Fix bundle/unbundle automatic lower-casing of urls in bundle manifest (Jon Siwek) 0.8-4 | 2016-12-08 19:50:49 -0600 * Add new bro-pkg.meta field: config_files This allows package authors to specify a list of files that users may directly modify. The upgrade, remove, install, and bundle operations all work in a way that attempts to either preserve a user's local changes or save a backup of their version. (Jon Siwek) 0.8-3 | 2016-12-07 22:54:49 -0600 * BIT-1768: improve handling of packages that have no Bro scripts (Jon Siwek) 0.8-2 | 2016-12-07 20:46:44 -0600 * BIT-1766: add tags() and short_description() to PackageInfo API (Jon Siwek) 0.8-1 | 2016-12-07 20:09:58 -0600 * BIT-1767: improve handling of non-ascii character encoding (Jon Siwek) 0.8 | 2016-11-29 11:21:29 -0600 * Release 0.8. 0.7-19 | 2016-11-29 11:19:17 -0600 * Improve output of info command and allow offline usage. (Jon Siwek) * Update documentation related to metadata aggregation. (Jon Siwek) * Metadata aggregation changes: - bro-pkg.index is now expected to be a simple list of package urls - add --aggregate flag to refresh command that will crawl packages and aggregate their metadata into an aggregate.meta file within local package source clones - add --sources flag to refresh command to explicitly specify operation on a specific set of package sources - add --push flag to refresh command to push local package source aggregate.meta changes to the remote repo (Jon Siwek) 0.7-10 | 2016-11-21 18:04:08 -0600 * Package versioning doc improvements. (Jon Siwek) 0.7-9 | 2016-11-21 12:15:14 -0600 * Update docs regarding package versioning. (Jon Siwek) 0.7-8 | 2016-10-21 10:13:16 -0500 * Using shutil.move instead of os.rename, which works across file systems. (Robin Sommer) 0.7-6 | 2016-10-11 18:54:50 -0500 * BIT-1725: allow bundling on offline machines When requesting to bundle the set of installed packages, they are directly copied into the bundle instead of trying to clone from the remote git repo. (Jon Siwek) 0.7-5 | 2016-10-04 14:05:50 -0500 * BIT-1713: in python3, show usage info when bro-pkg is given no args (Jon siwek) 0.7-4 | 2016-10-03 18:20:57 -0500 * BIT-1716: do shallow git clones of packages to use less space (Jon Siwek) 0.7-3 | 2016-10-03 17:08:31 -0500 * BIT-1715: improve message when failing to clone a package source And make that situation a warning instead of a fatal error. (Jon Siwek) 0.7-2 | 2016-10-03 16:49:24 -0500 * Allow empty values for paths in config file. (Jon Siwek) 0.7-1 | 2016-10-03 15:55:00 -0500 * BIT-1714: expand '~' and env. vars in config file values. And reject relative paths in [paths] section. (Jon Siwek) 0.7 | 2016-09-29 15:26:14 -0500 * Release 0.7. 0.6-14 | 2016-09-29 15:25:44 -0500 * Improve output of 'info' command. (Jon Siwek) 0.6-13 | 2016-09-29 14:59:31 -0500 * Tweak output of bundle/unbundle. (Jon Siwek) 0.6-12 | 2016-09-29 14:43:11 -0500 * Python 3 compatibility fixes. (Jon Siwek) 0.6-11 | 2016-09-28 18:11:44 -0500 * Add 'bundle' and 'unbundle' commands. (Jon Siwek) 0.6-10 | 2016-09-27 13:56:08 -0500 * Fix potential race condition in package installation. (Jon Siwek) 0.6-9 | 2016-09-26 15:31:50 -0500 * Improve formatting of package short descriptions. (Jon Siwek) 0.6-8 | 2016-09-26 15:15:18 -0500 * BIT-1699: improve package validation and format of output. (Jon Siwek) 0.6-7 | 2016-09-26 13:40:26 -0500 * Add a 'purge' command that removes all installed packages. (Jon Siwek) 0.6-6 | 2016-09-21 12:59:01 -0500 * BIT-1699: Add --force flag for install/remove/upgrade. (Jon Siwek) 0.6-5 | 2016-09-21 12:22:18 -0500 * BIT-1699: Add confirmation prompts for install/upgrade/remove. Removed the --all flag for upgrade command. (Jon Siwek) 0.6-4 | 2016-09-21 10:47:53 -0500 * BIT-1697: Improve output format of list/search. (Jon Siwek) 0.6-3 | 2016-09-19 13:29:27 -0500 * BIT-1698: autoconfig command now directly writes to config file. (Jon Siwek) 0.6-2 | 2016-09-19 12:09:52 -0500 * BIT-1697: Add description field to list/search output. (Jon Siwek) 0.6-1 | 2016-09-19 10:52:51 -0500 * BIT-1701: Improve error message when 'install' can't create symlink. (Jon Siwek) 0.6 | 2016-09-14 15:16:30 -0500 * Release 0.6. 0.5-2 | 2016-09-14 15:15:50 -0500 * Improve docs for script_dir/plugin_dir config file options. (Jon Siwek) 0.5-1 | 2016-09-14 15:09:12 -0500 * Improve automatic relocation of script_dir. (Jon Siwek) 0.5 | 2016-09-05 15:03:14 -0500 * Release 0.5. 0.4-2 | 2016-09-05 15:01:38 -0500 * Improve error message for insufficient permissions (Jon Siwek) 0.4-1 | 2016-09-05 14:09:55 -0500 * Improve output of 'info' command. (Jon Siwek) 0.4 | 2016-08-16 16:55:48 -0500 * Release 0.4. 0.3-4 | 2016-08-16 16:55:20 -0500 * Docs: add 'autoconfig' command (Jon Siwek) 0.3-3 | 2016-08-16 16:10:21 -0500 * Add 'autoconfig' command, remove 'bro_exe' option. bro-pkg can now use bro-config to automatically determine paths. (Jon Siwek) 0.3-2 | 2016-08-15 13:32:18 -0500 * Docs: clarifications (Jon Siwek) 0.3-1 | 2016-08-12 16:20:30 -0500 * Docs: add bro.org link in README (Jon Siwek) 0.3 | 2016-08-12 15:54:18 -0500 * Release 0.3. 0.2-47 | 2016-08-12 15:53:13 -0500 * Docs: add versioning/release guide for developers (Jon Siwek) 0.2-46 | 2016-08-11 19:45:21 -0500 * setup.py packaging improvements (Jon Siwek) 0.2-45 | 2016-08-11 14:35:06 -0500 * Docs: fix sphinx-argparse 'nosubcommands' option (Jon Siwek) 0.2-44 | 2016-08-11 14:16:22 -0500 * Docs: adjust admonitions in README (Jon Siwek) 0.2-43 | 2016-08-11 14:09:44 -0500 * Docs: organize external links (Jon Siwek) 0.2-42 | 2016-08-10 19:58:15 -0500 * Fix `list all` to show installed packages that don't come from a source (Jon Siwek) 0.2-41 | 2016-08-10 19:35:53 -0500 * Docs: add package creation walkthroughs (Jon Siwek) * Don't load packages unless they have a __load__.bro (Jon Siwek) * Improve handling of relative paths in package urls (Jon Siwek) 0.2-38 | 2016-08-10 14:29:30 -0500 * Teach search command to inspect package index tags (Jon Siwek) 0.2-37 | 2016-08-09 18:55:30 -0500 * Delay printing installation activity ticks. (Jon Siwek) 0.2-36 | 2016-08-09 18:41:54 -0500 * Change package sources to use index files instead of git submodules. (Jon Siwek) 0.2-35 | 2016-08-09 13:54:46 -0500 * Install packages in a thread. So it doesn't look like bro-pkg is hanging when installing a package that has to be built. (Jon Siwek) 0.2-34 | 2016-08-09 13:06:24 -0500 * Using ``install --version`` now also pins the package. (Jon Siwek) 0.2-33 | 2016-08-08 16:19:59 -0500 * info command can now take a --version flag. (Jon Siwek) 0.2-32 | 2016-08-08 15:16:54 -0500 * Fix install command to use the right version of bro-pkg.meta. (Jon Siwek) 0.2-31 | 2016-08-08 12:47:02 -0500 * Change config/metadata file option names. (Jon Siwek) bro-pkg.meta: scriptpath -> script_dir pluginpath -> plugin_dir buildcmd -> build_command bro-pkg config file: statedir -> state_dir scriptdir -> script_dir plugindir -> plugin_dir 0.2-30 | 2016-08-07 13:45:00 -0500 * Rename pkg.meta to bro-pkg.meta. (Jon Siwek) 0.2-29 | 2016-08-07 13:08:00 -0500 * upgrade command: require --all when omitting package list. (Jon Siwek) 0.2-28 | 2016-08-07 12:51:20 -0500 * Docs: adjust bro-pkg --help output. (Jon Siwek) 0.2-27 | 2016-08-07 12:35:40 -0500 * Add warnings to not directly modify certain files/dirs. (Jon Siwek) 0.2-26 | 2016-08-07 12:05:40 -0500 * Docs: show actual version in sidebar. (Jon Siwek) 0.2-25 | 2016-08-06 15:08:12 -0500 * Docs: don't restrict argparse-generated table widths. (Jon Siwek) * Docs: misc. style fixes and clarifications. (Jon Siwek) * Docs: switch theme (again) to Read the Docs theme. 0.2-22 | 2016-08-06 13:12:26 -0500 * Docs: clarifications, organization, style. (Jon Siwek) 0.2-21 | 2016-08-05 13:41:47 -0500 * Docs: change theme. (Jon Siwek) * Docs: GitPython and semantic_version build requirements (Jon Siwek) 0.2-19 | 2016-08-04 13:25:48 -0500 * Docs: add requirements file (Jon Siwek) 0.2-18 | 2016-08-03 16:10:18 -0500 * Docs: fix Sphinx argparse extension to check for outdated modules. Sphinx itself should now always be able to determine whether the command-line bro-pkg documentation needs to be rebuilt. (Jon Siwek) 0.2-17 | 2016-08-02 14:46:16 -0500 * Docs: rebuild docs on version string change. (Jon Siwek) 0.2-16 | 2016-08-02 14:34:49 -0500 * Docs: give each bro-pkg command its own section (Jon Siwek) 0.2-15 | 2016-08-01 14:34:06 -0500 * Documentation organization/layout/theming. (Jon Siwek) 0.2-14 | 2016-07-24 14:55:28 -0500 * Clarification to installation doc. (Jon Siwek) 0.2-13 | 2016-07-24 12:18:40 -0500 * Add a 'gh-pages' Makefile target. (Jon Siwek) * Add .nojekyll file to built sphinx docs. (Jon Siwek) 0.2-11 | 2016-07-23 15:32:24 -0500 * Add installation/setup docs. (Jon Siwek) 0.2-10 | 2016-07-23 14:19:09 -0500 * Add a prebuilt man page. (Jon Siwek) 0.2-9 | 2016-07-22 15:07:57 -0500 * Change bro-pkg to not require a config file and add setup.py. (Jon Siwek) 0.2-8 | 2016-07-21 12:14:53 -0500 * Improve speed of checking for changed remote package sources. (Jon Siwek) * Improve how 'info' command locates packages. (Jon Siwek) * Emit an error when package has non-existant 'scriptpath'. (Jon Siwek) * Documentation clarifications. (Jon Siwek) 0.2-4 | 2016-07-21 09:49:15 -0500 * Make output of 'info' command have a stable order. (Jon Siwek) 0.2-3 | 2016-07-21 09:44:42 -0500 * Python3 compatibility fixes. (Jon Siwek) 0.2-2 | 2016-07-20 14:35:38 -0500 * Documentation and other clean up. Sphinx is used docs: manual, api reference, man page. (Jon Siwek) 0.2-1 | 2016-07-14 12:26:56 -0500 * Fix a mistake in --help output. (Jon Siwek) 0.2 | 2016-07-14 10:51:11 -0500 * Release 0.2. package-manager-2.0.7/zkg.config0000644000175000017500000000541513551142424016061 0ustar rhaistrhaist# This is an example config file for zkg to explain what # settings are possible as well as their default values. # The order of precedence for how zkg finds/reads config files: # # (1) zkg --configfile=/path/to/custom/config # (2) the ZKG_CONFIG_FILE environment variable # (3) a config file located at $HOME/.zkg/config # (4) if none of the above exist, then zkg uses builtin/default # values for all settings shown below [sources] # The default package source repository from which zkg fetches # packages. The default source may be removed, changed, or # additional sources may be added as long as they use a unique key # and a value that is a valid git URL. zeek = https://github.com/zeek/packages [paths] # Directory where source repositories are cloned, packages are # installed, and other package manager state information is # maintained. If left blank, this defaults to $HOME/.zkg state_dir = # The directory where package scripts are copied upon installation. # A subdirectory named "packages" is always created within the # specified path and the package manager will copy the directory # specified by the "script_dir" option of each package's zkg.meta # (or legacy bro-pkg.meta) file there. # If left blank, this defaults to /script_dir # A typical path to set here is /share/zeek/site # If you decide to change this location after having already # installed packages, zkg will automatically relocate them # the next time you run any zkg command. script_dir = # The directory where package plugins are copied upon installation. # A subdirectory named "packages" is always created within the # specified path and the package manager will copy the directory # specified by the "plugin_dir" option of each package's zkg.meta # (or legacy bro-pkg.meta) file there. # If left blank, this defaults to /plugin_dir # A typical path to set here is /lib/zeek/plugins # If you decide to change this location after having already # installed packages, zkg will automatically relocate them # the next time you run any zkg command. plugin_dir = # The directory containing Zeek distribution source code. This is only # needed when installing packages that contain Zeek plugins that are # not pre-built. The legacy name of this option is "bro_dist". zeek_dist = [user_vars] # For any key in this section that is matched for value interpolation # in a package's zkg.meta (or legacy bro-pkg.meta) file, the corresponding # value is substituted during execution of the package's `build_command`. # This section is typically automatically populated with the # the answers supplied during package installation prompts # and, as a convenience feature, used to recall the last-used settings # during subsequent operations (e.g. upgrades) on the same package. package-manager-2.0.7/setup.cfg0000644000175000017500000000003413551142424015710 0ustar rhaistrhaist[bdist_wheel] universal = 1 package-manager-2.0.7/doc/0000755000175000017500000000000013551142424014637 5ustar rhaistrhaistpackage-manager-2.0.7/doc/api/0000755000175000017500000000000013551142424015410 5ustar rhaistrhaistpackage-manager-2.0.7/doc/api/manager.rst0000644000175000017500000000021013551142424017545 0ustar rhaistrhaistzeekpkg.manager module ====================== .. automodule:: zeekpkg.manager :members: :undoc-members: :show-inheritance: package-manager-2.0.7/doc/api/package.rst0000644000175000017500000000021013551142424017526 0ustar rhaistrhaistzeekpkg.package module ====================== .. automodule:: zeekpkg.package :members: :undoc-members: :show-inheritance: package-manager-2.0.7/doc/api/source.rst0000644000175000017500000000020513551142424017437 0ustar rhaistrhaistzeekpkg.source module ===================== .. automodule:: zeekpkg.source :members: :undoc-members: :show-inheritance: package-manager-2.0.7/doc/api/index.rst0000644000175000017500000000042713551142424017254 0ustar rhaistrhaistPython API Reference ==================== .. automodule:: zeekpkg :members: :undoc-members: :show-inheritance: The following Python modules are all provided as part of the ``zeekpkg`` public interface: .. toctree:: :maxdepth: 2 manager package source package-manager-2.0.7/doc/conf.py0000644000175000017500000002354613551142424016150 0ustar rhaistrhaist# -*- coding: utf-8 -*- # # Zeek Package Manager documentation build configuration file, created by # sphinx-quickstart on Fri Jul 15 13:46:04 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('./ext')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinxarg.ext', 'sphinx.ext.autodoc', 'sphinxcontrib.napoleon'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Zeek Package Manager' copyright = u'2019, The Zeek Project' author = u'The Zeek Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. with open('../VERSION', 'r') as f: version = f.readline().strip() # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] html_theme_options = { 'collapse_navigation': False, 'display_version': True, } # The name for this set of Sphinx documents. # " v documentation" by default. # html_title = u'Zeek Package Manager Documentation' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] def setup(app): #app.add_javascript("custom.js") app.add_stylesheet("theme_overrides.css") # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # #html_sidebars = {'**': ['custom-sidebar.html']} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'ZeekPackageManagerdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'ZeekPackageManager.tex', u'Zeek Package Manager Documentation', u'The Zeek Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('zkg', 'zkg', u'Zeek Package Manager', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'ZeekPackageManager', u'Zeek Package Manager Documentation', author, 'ZeekPackageManager', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False package-manager-2.0.7/doc/_static/0000755000175000017500000000000013551142424016265 5ustar rhaistrhaistpackage-manager-2.0.7/doc/_static/theme_overrides.css0000644000175000017500000000057613551142424022173 0ustar rhaistrhaist/* Prevent sphinx-argparse option output (e.g. --version) from wrapping. */ span.option { white-space: nowrap; } /* override table width restrictions */ html body div.wy-table-responsive table td, html body div.wy-table-responsive table th { white-space: normal; } html body div.wy-table-responsive { margin-bottom: 24px; max-width: 100%; overflow: visible; } package-manager-2.0.7/doc/developers.rst0000644000175000017500000000641113551142424017543 0ustar rhaistrhaist.. _Sphinx: http://www.sphinx-doc.org .. _Read the Docs: https://docs.zeek.org/projects/package-manager .. _GitHub: https://github.com/zeek/package-manager .. _Google Style Docstrings: http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html .. _zeek-aux: https://github.com/zeek/zeek-aux .. _PyPi: https://pypi.python.org/pypi Developer's Guide ================= This a guide for developers working on the Zeek Package Manager itself. Versioning/Releases ------------------- After making a commit to the *master* branch, you can use the :program:`update-changes` script in the `zeek-aux`_ repository to automatically adapt version numbers and regenerate the :program:`zkg` man page. Make sure to install the `documentation dependencies`_ before using it. Releases are hosted at PyPi_. To build and upload a release: #. Finalize the git repo tag and version with ``update-changes -R `` if not done already. #. Upload the distribution (you will need the credentials for the 'zeek' account on PyPi): .. code-block:: console $ make upload Documentation ------------- Documentation is written in reStructuredText (reST), which Sphinx_ uses to generate HTML documentation and a man page. .. _documentation dependencies: Dependencies ~~~~~~~~~~~~ To build documentation locally, find the requirements in :file:`requirements.txt`: .. literalinclude:: ../requirements.txt They can be installed like: pip install -r requirements.txt Local Build/Preview ~~~~~~~~~~~~~~~~~~~ Use the :file:`Makefile` targets ``make html`` and ``make man`` to build the HTML and man page, respectively. To view the generated HTML output, open :file:`doc/_build/index.html`. The generated man page is located in :file:`doc/man/zkg.1`. If you have also installed :program:`sphinx-autobuild` (e.g. via :program:`pip`), there's a :file:`Makefile` target, ``make livehtml``, you can use to help preview documentation changes as you edit the reST files. Remote Hosting ~~~~~~~~~~~~~~ The GitHub_ repository has a webhook configured to automatically rebuild the HTML documentation hosted at `Read the Docs`_ whenever a commit is pushed. Style Conventions ~~~~~~~~~~~~~~~~~ The following style conventions are (generally) used. ========================== =============================== =========================== Documentation Subject reST Markup Preview ========================== =============================== =========================== File Path ``:file:`path``` :file:`path` File Path w/ Substitution ``:file:`{}/path``` :file:`{}/path` OS-Level Commands ``:command:`cmd``` :command:`cmd` Program Names ``:program:`prog``` :program:`prog` Environment Variables ``:envvar:`VAR``` :envvar:`VAR` Literal Text (e.g. code) ````code```` ``code`` Substituted Literal Text ``:samp:`code {}``` :samp:`code {}` Variable/Type Name ```x``` `x` INI File Option ```name``` `name` ========================== =============================== =========================== Python API docstrings roughly follow the `Google Style Docstrings`_ format. package-manager-2.0.7/doc/quickstart.rst0000644000175000017500000001510313551142424017563 0ustar rhaistrhaist.. _PyPI: https://pypi.python.org/pypi .. _ZeekControl: https://github.com/zeek/zeekctl Quickstart Guide ================ Dependencies ------------ * Python 2.7+ or 3.0+ * git: https://git-scm.com * GitPython: https://pypi.python.org/pypi/GitPython * semantic_version: https://pypi.python.org/pypi/semantic_version * btest: https://pypi.python.org/pypi/btest * configparser backport (not needed when using Python 3.5+): https://pypi.python.org/pypi/configparser Note that following the suggested `Installation`_ process via :program:`pip` will automatically install dependencies for you. Installation ------------ Using the latest stable release on PyPI_: .. code-block:: console $ pip install zkg Using the latest git development version: .. code-block:: console $ pip install git+git://github.com/zeek/package-manager@master Basic Configuration ------------------- After installing via :program:`pip`, additional configuration is required. First, make sure that the :program:`zeek-config` script that gets installed with :program:`zeek` is in your :envvar:`PATH`. Then, as the user you want to run :program:`zkg` with, do: .. code-block:: console $ zkg autoconfig This automatically generates a config file with the following suggested settings that should work for most Zeek deployments: - `script_dir`: set to the location of Zeek's :file:`site` scripts directory (e.g. :file:`{}/share/zeek/site`) - `plugin_dir`: set to the location of Zeek's default plugin directory (e.g. :file:`{}/lib/zeek/plugins`) - `zeek_dist`: set to the location of Zeek's source code. If you didn't build/install Zeek from source code, this field will not be set, but it's only needed if you plan on installing packages that have uncompiled Zeek plugins. With those settings, the package manager will install Zeek scripts, Zeek plugins, and ZeekControl plugins into directories where :program:`zeek` and :program:`zeekctl` will, by default, look for them. ZeekControl clusters will also automatically distribute installed package scripts/plugins to all nodes. .. note:: If your Zeek installation is owned by "root" and you intend to run :program:`zkg` as a different user, then you should grant "write" access to the directories specified by `script_dir` and `plugin_dir`. E.g. you could do something like: .. code-block:: console $ sudo chgrp $USER $(zeek-config --site_dir) $(zeek-config --plugin_dir) $ sudo chmod g+rwX $(zeek-config --site_dir) $(zeek-config --plugin_dir) The final step is to edit your :file:`site/local.zeek`. If you want to have Zeek automatically load the scripts from all :ref:`installed ` packages that are also marked as ":ref:`loaded `" add: .. code-block:: bro @load packages If you prefer to manually pick the package scripts to load, you may instead add lines like :samp:`@load {}`, where :samp:`{}` is the :ref:`shorthand name ` of the desired package. If you want to further customize your configuration, see the `Advanced Configuration`_ section and also check :ref:`here ` for a full explanation of config file options. Otherwise you're ready to use :ref:`zkg `. Advanced Configuration ---------------------- If you prefer to not use the suggested `Basic Configuration`_ settings for `script_dir` and `plugin_dir`, the default configuration will install all package scripts/plugins within :file:`~/.zkg` or you may change them to whatever location you prefer. These will be referred to as "non-standard" locations in the sense that vanilla configurations of either :program:`zeek` or :program:`zeekctl` will not detect scripts/plugins in those locations without additional configuration. When using non-standard location, follow these steps to integrate with :program:`zeek` and :program:`zeekctl`: - To get command-line :program:`zeek` to be aware of Zeek scripts/plugins in a non-standard location, make sure the :program:`zeek-config` script (that gets installed along with :program:`zeek`) is in your :envvar:`PATH` and run: .. code-block:: console $ `zkg env` Note that this sets up the environment only for the current shell session. - To get :program:`zeekctl` to be aware of scripts/plugins in a non-standard location, run: .. code-block:: console $ zkg config script_dir And set the `SitePolicyPath` option in :file:`zeekctl.cfg` based on the output you see. Similarly, run: .. code-block:: console $ zkg config plugin_dir And set the `SitePluginPath` option in :file:`zeekctl.cfg` based on the output you see. Usage ----- Check the output of :ref:`zkg --help ` for an explanation of all available functionality of the command-line tool. Package Upgrades/Versioning ~~~~~~~~~~~~~~~~~~~~~~~~~~~ When installing packages, note that the :ref:`install command `, has a ``--version`` flag that may be used to install specific package versions which may either be git release tags or branch names. The way that :program:`zkg` receives updates for a package depends on whether the package is first installed to track stable releases or a specific git branch. See the :ref:`package upgrade process ` documentation to learn how :program:`zkg` treats each situation. Offline Usage ~~~~~~~~~~~~~ It's common to have limited network/internet access on the systems where Zeek is deployed. To accomodate those scenarios, :program:`zkg` can be used as normally on a system that *does* have network access to create bundles of its package installation environment. Those bundles can then be transferred to the deployment systems via whatever means are appropriate (SSH, USB flash drive, etc). For example, on the package management system you can do typical package management tasks, like install and update packages: .. code-block:: console $ zkg install Then, via the :ref:`bundle command `, create a bundle file which contains a snapshot of all currently installed packages: .. code-block:: console $ zkg bundle zeek-packages.bundle Then transfer :file:`zeek-packages.bundle` to the Zeek deployment management host. For Zeek clusters using ZeekControl_, this will be the system acting as the "manager" node. Then on that system (assuming it already as :program:`zkg` installed and configured): .. code-block:: console $ zkg unbundle zeek-packages.bundle Finally, if you're using ZeekControl_, and the unbundling process was successful, you need to deploy the changes to worker nodes: .. code-block:: console $ zeekctl deploy package-manager-2.0.7/doc/docutils.conf0000644000175000017500000000003313551142424017330 0ustar rhaistrhaist[parsers] smart_quotes: no package-manager-2.0.7/doc/Makefile0000644000175000017500000001740013551142424016301 0ustar rhaistrhaist# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" @echo " dummy to check syntax errors of document sources" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html touch $(BUILDDIR)/html/.nojekyll @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: livehtml livehtml: sphinx-autobuild --ignore "../testing/*" --ignore "*.git/*" --ignore "*.lock" --ignore "*.pyc" --ignore "*.swp" --ignore "*.swpx" --ignore "*.swx" --watch .. --watch ../zeekpkg -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ZeekPackageManager.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ZeekPackageManager.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/ZeekPackageManager" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ZeekPackageManager" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." package-manager-2.0.7/doc/package.rst0000644000175000017500000006703313551142424016775 0ustar rhaistrhaist.. _Zeek Scripting: https://docs.zeek.org/en/stable/examples/scripting/index.html .. _Zeek Plugins: https://docs.zeek.org/en/stable/devel/plugins.html .. _ZeekControl Plugins: https://github.com/zeek/zeekctl#plugins .. _Semantic Version Specification: https://python-semanticversion.readthedocs.io/en/latest/reference.html#version-specifications-the-spec-class .. _btest: https://github.com/zeek/btest .. _configparser interpolation: https://docs.python.org/3/library/configparser.html#interpolation-of-values How-To: Create a Package ======================== A Zeek package may contain Zeek scripts, Zeek plugins, or ZeekControl plugins. Any number or combination of those components may be included within a single package. The minimum requirement for a package is that it be in its own git repository and contain a metadata file named :file:`zkg.meta` at its top-level that begins with the line:: [package] This is the package's metadata file in INI file format and may contain :ref:`additional fields ` that describe the package as well as how it inter-operates with Zeek, the package manager, or other packages. .. note:: :file:`zkg.meta` is the canonical metadata file name used :program:`since zkg v2.0`. The previous metadata file name of :file:`bro-pkg.meta` is also accepted when no :file:`zkg.meta` exists. .. _package-shorthand-name: Note that the shorthand name for your package that may be used by :ref:`zkg ` and Zeek script :samp:`@load {}` directives will be the last component of its git URL. E.g. a package at ``https://github.com/zeek/foo`` may be referred to as **foo** when using :program:`zkg` and a Zeek script that wants to load all the scripts within that package can use: .. code-block:: bro @load foo Walkthroughs ------------ Pure Zeek Script Package ~~~~~~~~~~~~~~~~~~~~~~~~ #. Create a git repository: .. code-block:: console $ mkdir foo && cd foo && git init #. Create a package metadata file, :file:`zkg.meta`: .. code-block:: console $ echo '[package]' > zkg.meta #. Create a :file:`__load__.zeek` script with example code in it: .. code-block:: console $ echo 'event zeek_init() { print "foo is loaded"; }' > __load__.zeek #. (Optional) Relocate your :file:`__load__.zeek` script to any subdirectory: .. code-block:: console $ mkdir scripts && mv __load__.zeek scripts $ echo 'script_dir = scripts' >> zkg.meta #. Commit everything to git: .. code-block:: console $ git add * && git commit -m 'First commit' #. (Optional) Test that Zeek correctly loads the script after installing the package with :program:`zkg`: .. code-block:: console $ zkg install . $ zeek foo $ zkg remove . #. (Optional) :ref:`Create a release version tag `. See `Zeek Scripting`_ for more information on developing Zeek scripts. Binary Zeek Plugin Package ~~~~~~~~~~~~~~~~~~~~~~~~~~ See `Zeek Plugins`_ for more complete information on developing Zeek plugins, though the following step are the essentials needed to create a package. #. Create a plugin skeleton using :file:`aux/zeek-aux/plugin-support/init-plugin` from Zeek's source distribution: .. code-block:: console $ init-plugin ./rot13 Demo Rot13 #. Create a git repository .. code-block:: console $ cd rot13 && git init #. Create a package metadata file, :file:`zkg.meta`:: [package] script_dir = scripts/Demo/Rot13 build_command = ./configure && make .. note:: See :ref:`legacy-bro-support` for notes on configuring packages to support Bro 2.5 or earlier. #. Add example script code: .. code-block:: console $ echo 'event zeek_init() { print "rot13 plugin is loaded"; }' >> scripts/__load__.zeek $ echo 'event zeek_init() { print "rot13 script is loaded"; }' >> scripts/Demo/Rot13/__load__.zeek #. Add an example builtin-function in :file:`src/rot13.bif`: .. code-block:: c++ module Demo; function rot13%(s: string%) : string %{ char* rot13 = copy_string(s->CheckString()); for ( char* p = rot13; *p; p++ ) { char b = islower(*p) ? 'a' : 'A'; *p = (*p - b + 13) % 26 + b; } BroString* bs = new BroString(1, reinterpret_cast(rot13), strlen(rot13)); return new StringVal(bs); %} #. Commit everything to git: .. code-block:: console $ git add * && git commit -m 'First commit' #. (Optional) Test that Zeek correctly loads the plugin after installing the package with :program:`zkg`: .. code-block:: console $ zkg install . $ zeek rot13 -e 'print Demo::rot13("Hello")' $ zkg remove . #. (Optional) :ref:`Create a release version tag `. ZeekControl Plugin Package ~~~~~~~~~~~~~~~~~~~~~~~~~~ #. Create a git repository: .. code-block:: console $ mkdir foo && cd foo && git init #. Create a package metadata file, :file:`zkg.meta`: .. code-block:: console $ echo '[package]' > zkg.meta #. Create an example ZeekControl plugin, :file:`foo.py`: .. code-block:: python import ZeekControl.plugin from ZeekControl import config class Foo(ZeekControl.plugin.Plugin): def __init__(self): super(Foo, self).__init__(apiversion=1) def name(self): return "foo" def pluginVersion(self): return 1 def init(self): self.message("foo plugin is initialized") return True #. Set the `plugin_dir` metadata field to directory where the plugin is located: .. code-block:: console $ echo 'plugin_dir = .' >> zkg.meta #. Commit everything to git: .. code-block:: console $ git add * && git commit -m 'First commit' #. (Optional) Test that ZeekControl correctly loads the plugin after installing the package with :program:`zkg`: .. code-block:: console $ zkg install . $ zeekctl $ zkg remove . #. (Optional) :ref:`Create a release version tag `. See `ZeekControl Plugins`_ for more information on developing ZeekControl plugins. If you want to distribute a ZeekControl plugin along with a Zeek plugin in the same package, you may need to add the ZeekControl plugin's python script to the ``zeek_plugin_dist_files()`` macro in the :file:`CMakeLists.txt` of the Zeek plugin so that it gets copied into :file:`build/` along with the built Zeek plugin. Or you could also modify your `build_command` to copy it there, but what ultimately matters is that the `plugin_dir` field points to a directory that contains both the Zeek plugin and the ZeekControl plugin. Registering to a Package Source ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Registering a package to a package source is always the following basic steps: #) Create a :ref:`Package Index File ` for your package. #) Add the index file to the package source's git repository. The full process and conventions for submitting to the default package source can be found in the :file:`README` at: https://github.com/zeek/packages .. _metadata-fields: Package Metadata ---------------- See the following sub-sections for a full list of available fields that may be used in :file:`zkg.meta` files. `description` field ~~~~~~~~~~~~~~~~~~~ The description field may be used to give users a general overview of the package and its purpose. The :ref:`zkg list ` will display the first sentence of description fields in the listings it displays. An example :file:`zkg.meta` using a description field:: [package] description = Another example package. The description text may span multiple line: when adding line breaks, just indent the new lines so they are parsed as part of the 'description' value. `aliases` field ~~~~~~~~~~~~~~~ The `aliases` field can be used to specify alternative names for a package. Users can then use :samp:`@load {}` for any alias listed in this field. This may be useful when renaming a package's repository on GitHub while still supporting users that already installed the package under the previous name. For example, if package `foo` were renamed to `foo2`, then the `aliases` for it could be:: [package] aliases = foo2 foo Currently, the order does not matter, but you should specify the canonical/current alias first. The list is delimited by commas or whitespace. If this field is not specified, the default behavior is the same as if using a single alias equal to the package's name. The low-level details of the way this field operates is that, for each alias, it simply creates a symlink of the same name within the directory associated with the ``script_dir`` path in the :ref:`config file `. Available :program:`since bro-pkg v1.5`. `credits` field ~~~~~~~~~~~~~~~ The `credits` field contains a comma-delimited set of author/contributor/maintainer names, descriptions, and/or email addresses. It may be used if you have particular requirements or concerns regarding how authors or contributors for your package are credited in any public listings made by external metadata scraping tools (:program:`zkg` does not itself use this data directly for any functional purpose). It may also be useful as a standardized location for users to get contact/support info in case they encounter problems with the package. For example:: [package] credits = A. Sacker ., JSON support added by W00ter (Acme Corporation) `tags` field ~~~~~~~~~~~~ The `tags` field contains a comma-delimited set of metadata tags that further classify and describe the purpose of the package. This is used to help users better discover and search for packages. The :ref:`zkg search ` command will inspect these tags. An example :file:`zkg.meta` using tags:: [package] tags = zeek plugin, zeekctl plugin, scan detection, intel Suggested Tags ^^^^^^^^^^^^^^ Some ideas for what to put in the `tags` field for packages: - zeek scripting - conn - intel - geolocation - file analysis - sumstats, summary statistics - input - log, logging - notices - ** - ** - signatures - zeek plugin - protocol analyzer - file analyzer - bifs - packet source - packet dumper - input reader - log writer - zeekctl plugin `script_dir` field ~~~~~~~~~~~~~~~~~~ The `script_dir` field is a path relative to the root of the package that contains a file named :file:`__load__.zeek` and possibly other Zeek scripts. The files located in this directory are copied into :file:`{}/packages/{}/`, where `` corresponds to the `script_dir` field of the user's :ref:`config file ` (typically :file:`{}/share/zeek/site`). When the package is :ref:`loaded `, an :samp:`@load {}` directive is added to :file:`{}/packages/packages.zeek`. You may place any valid Zeek script code within :file:`__load__.zeek`, but a package that contains many Zeek scripts will typically have :file:`__load__.zeek` just contain a list of ``@load`` directives to load other Zeek scripts within the package. E.g. if you have a package named **foo** installed, then it's :file:`__load__.zeek` will be what Zeek loads when doing ``@load foo`` or running ``zeek foo`` on the command-line. An example :file:`zkg.meta`:: [package] script_dir = scripts For a :file:`zkg.meta` that looks like the above, the package should have a file called :file:`scripts/__load__.zeek`. If the `script_dir` field is not present in :file:`zkg.meta`, it defaults to checking the top-level directory of the package for a :file:`__load__.zeek` script. If it's found there, :program:`zkg` use the top-level package directory as the value for `script_dir`. If it's not found, then :program:`zkg` assumes the package contains no Zeek scripts (which may be the case for some plugins). `plugin_dir` field ~~~~~~~~~~~~~~~~~~ The `plugin_dir` field is a path relative to the root of the package that contains either pre-built `Zeek Plugins`_, `ZeekControl Plugins`_, or both. An example :file:`zkg.meta`:: [package] script_dir = scripts plugin_dir = plugins For the above example, Zeek and ZeekControl will load any plugins found in the installed package's :file:`plugins/` directory. If the `plugin_dir` field is not present in :file:`zkg.meta`, it defaults to a directory named :file:`build/` at the top-level of the package. This is the default location where Zeek binary plugins get placed when building them from source code (see the `build_command field`_). This field may also be set to the location of a tarfile that has a single top- level directory inside it containing the Zeek plugin. The default CMake skeleton for Zeek plugins produces such a tarfile located at :file:`build/_.tgz`. This is a good choice to use for packages that will be published to a wider audience as installing from this tarfile contains the minimal set of files needed for the plugin to work whereas some extra files will get installed to user systems if the `plugin_dir` uses the default :file:`build/` directory. `build_command` field ~~~~~~~~~~~~~~~~~~~~~ The `build_command` field is an arbitrary shell command that the package manager will run before installing the package. This is useful for distributing `Zeek Plugins`_ as source code and having the package manager take care of building it on the user's machine before installing the package. An example :file:`zkg.meta`:: [package] script_dir = scripts/Demo/Rot13 build_command = ./configure && make .. note:: See :ref:`legacy-bro-support` for notes on configuring packages to support Bro 2.5 or earlier. The default CMake skeleton for Zeek plugins will use :file:`build/` as the directory for the final/built version of the plugin, which matches the defaulted value of the omitted `plugin_dir` metadata field. The `script_dir` field is set to the location where the author has placed custom scripts for their plugin. When a package has both a Zeek plugin and Zeek script components, the "plugin" part is always unconditionally loaded by Zeek, but the "script" components must either be explicitly loaded (e.g. :samp:`@load {}`) or the package marked as :ref:`loaded `. .. _legacy-bro-support: Supporting Older Bro Versions ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Plugin skeletons generated before Bro v2.6 and also any packages that generally want to support such Bro versions need to pass an additional configuration option such as:: build_command = ./configure --bro-dist=%(bro_dist)s && make See the :ref:`Value Interpolation ` section for more information on what the ``%(bro_dist)s`` string does, but a brief explanation is that it will expand to a path containing the Bro source-code on the user's system. For newer versions of Bro, packages are able to work entirely with the installation path and don't require original source code. Also note that other various Zeek scripting and CMake infrastructure may have changed between Bro v2.6 and Zeek v3.0. So if you plan to support older version of Bro (before the Zeek rename), then you should keep an eye out for various things that got renamed. For example, the `zeek_init` event won't exist in any version before Zeek v3.0, nor will any CMake macros that start with `zeek_plugin`. .. _metadata-interpolation: Value Interpolation ^^^^^^^^^^^^^^^^^^^ The `build_command field`_ may reference the settings any given user has in their customized :ref:`package manager config file `. For example, if a metadata field's value contains the ``%(bro_dist)s`` string, then :program:`zkg` operations that use that field will automatically substitute the actual value of `bro_dist` that the user has in their local config file. Note the trailing 's' character at the end of the interpolation string, ``%(bro_dist)s``, is intended/necessary for all such interpolation usages. Note that :program:`since zkg v2.0`, `zeek_dist` is the canonical name for `bro_dist` within the :ref:`zkg config file `, but either one means the same thing and should work. To support older versions of :program:`bro-pkg`, you'd want to use `bro_dist` in package metadata files. Besides the `bro_dist`/`zeek_dist` config keys, any key inside the `user_vars` sections of their :ref:`package manager config file ` that matches the key of an entry in the package's `user_vars field`_ will be interpolated. Internally, the value substitution and metadata parsing is handled by Python's `configparser interpolation`_. See its documentation if you're interested in the details of how the interpolation works. `user_vars` field ~~~~~~~~~~~~~~~~~ The `user_vars` field is used to solicit feedback from users for use during execution of the `build_command field`_. An example :file:`zkg.meta`:: [package] build_command = ./configure --with-librdkafka=%(LIBRDKAFKA_ROOT)s --with-libdub=%(LIBDBUS_ROOT)s && make user_vars = LIBRDKAFKA_ROOT [/usr] "Path to librdkafka installation" LIBDBUS_ROOT [/usr] "Path to libdbus installation" The format of the field is a sequence entries of the format:: key [value] "description" The `key` is the string that should match what you want to be interpolated within the `build_command field`_. The `value` is provided as a convenient default value that you'd typically expect to work for most users. The `description` is provided as an explanation for what the value will be used for. Here's what a typical user would see:: $ zkg install zeek-test-package The following packages will be INSTALLED: zeek/jsiwek/zeek-test-package (1.0.5) Proceed? [Y/n] y zeek/jsiwek/zeek-test-package asks for LIBRDKAFKA_ROOT (Path to librdkafka installation) ? [/usr] /usr/local Saved answers to config file: /Users/jon/.zkg/config Installed "zeek/jsiwek/zeek-test-package" (master) Loaded "zeek/jsiwek/zeek-test-package" The :program:`zkg` command will iterate over the `user_vars` field of all packages involved in the operation and prompt the user to provide a value that will work for their system. If a user is using the ``--force`` option to :program:`zkg` commands or they are using the Python API directly, it will first look within the `user_vars` section of the user's :ref:`package manager config file ` and, if it can't find the key there, it will fallback to use the default value from the package's metadata. In any case, the user may choose to supply the value of a `user_vars` key via an environment variable, in which case, prompts are skipped for any keys located in the environment. The environment is also given priority over any values in the user's :ref:`package manager config file `. Available :program:`since bro-pkg v1.1`. `test_command` field ~~~~~~~~~~~~~~~~~~~~ The `test_command` field is an arbitrary shell command that the package manager will run when a user either manually runs the :ref:`test command ` or before the package is installed or upgraded. An example :file:`zkg.meta`:: [package] test_command = cd testing && btest -d tests The recommended test framework for writing package unit tests is `btest`_. See its documentation for further explanation and examples. `config_files` field ~~~~~~~~~~~~~~~~~~~~ The `config_files` field may be used to specify a list of files that users are intended to directly modify after installation. Then, on operations that would otherwise destroy a user's local modifications to a config file, such as upgrading to a newer package version, :program:`zkg` can instead save a backup and possibly prompt the user to review the differences. An example :file:`zkg.meta`:: [package] script_dir = scripts config_files = scripts/foo_config.zeek, scripts/bar_config.zeek The value of `config_files` is a comma-delimited string of config file paths that are relative to the root directory of the package. Config files should either be located within the `script_dir` or `plugin_dir`. .. _package-dependencies: `depends` field ~~~~~~~~~~~~~~~ The `depends` field may be used to specify a list of dependencies that the package requires. An example :file:`zkg.meta`:: [package] depends = zeek >=2.5.0 foo * https://github.com/zeek/bar >=2.0.0 package_source/path/bar branch=name_of_git_branch The field is a list of dependency names and their version requirement specifications. A dependency name may be either `zeek`, `zkg`, `bro`, `bro-pkg`, a full git URL of the package, or a :ref:`package shorthand name `. - The special `zeek` and `bro` dependencies refers not to a package, but the version of Zeek that the package requires in order to function. If the user has :program:`zeek-config` or :program:`bro-config` in their :envvar:`PATH` when installing/upgrading a package that specifies a `zeek` or `bro` dependency, then :program:`zkg` will enforce that the requirement is satisfied. .. note:: In this context, `zeek` and `bro` mean the same thing -- the later is maintained for backwards compatibility while the former became available :program:`since zkg v2.0`. - The special `zkg` and `bro-pkg` dependencies refers to the version of the package manager that is required by the package. E.g. if a package takes advantage of new features that are not present in older versions of the package manager, then it should indicate that so users of those old version will see an error message an know to upgrade instead of seeing a cryptic error/exception, or worse, seeing no errors, but without the desired functionality being performed. .. note:: This feature itself, via use of a `bro-pkg` dependency, is only available :program:`since bro-pkg v1.2` while a `zkg` dependency is only recognized :program:`since zkg v2.0`. Otherwise, `zkg` and `bro-pkg` mean the same thing in this context. - The full git URL may be directly specified in the `depends` metadata if you want to force the dependency to always resolve to a single, canonical git repository. Typically this is the safe approach to take when listing package dependencies and for publicly visible packages. - When using shorthand package dependency names, the user's :program:`zkg` will try to resolve the name into a full git URL based on the package sources they have configured. Typically this approach may be most useful for internal or testing environments. A version requirement may be either a git branch name or a semantic version specification. When using a branch as a version requirement, prefix the branchname with ``branch=``, else see the `Semantic Version Specification`_ documentation for the complete rule set of acceptable version requirement strings. Here's a summary: - ``*``: any version (this will also satisfy/match on git branches) - ``<1.0.0``: versions less than 1.0.0 - ``<=1.0.0``: versions less than or equal to 1.0.0 - ``>1.0.0``: versions greater than 1.0.0 - ``>=1.0.0``: versions greater than or equal to 1.0.0 - ``==1.0.0``: exactly version 1.0.0 - ``!=1.0.0``: versions not equal to 1.0.0 - ``^1.3.4``: versions between 1.3.4 and 2.0.0 (not including 2.0.0) - ``~1.2.3``: versions between 1.2.3 and 1.3.0 (not including 1.3.0) - ``~=2.2``: versions between 2.2.0 and 3.0.0 (not included 3.0.0) - ``~=1.4.5``: versions between 1.4.5 and 1.5.0 (not including 3.0.0) - Any of the above may be combined by a separating comma to logically "and" the requirements together. E.g. ``>=1.0.0,<2.0.0`` means "greater or equal to 1.0.0 and less than 2.0.0". Note that these specifications are strict semantic versions. Even if a given package chooses to use the ``vX.Y.Z`` format for its :ref:`git version tags `, do not use the 'v' prefix in the version specifications here as that is not part of the semantic version. `external_depends` field ~~~~~~~~~~~~~~~~~~~~~~~~ The `external_depends` field follows the same format as the :ref:`depends field `, but the dependency names refer to external/third-party software packages. E.g. these would be set to typical package names you'd expect the package manager from any given operating system to use, like 'libpng-dev'. The version specification should also generally be given in terms of semantic versioning where possible. In any case, the name and version specification for an external dependency are only used for display purposes -- to help users understand extra pre-requisites that are needed for proceeding with package installation/upgrades. Available :program:`since bro-pkg v1.1`. `suggests` field ~~~~~~~~~~~~~~~~ The `suggests` field follows the same format as the :ref:`depends field `, but it's used for specifying optional packages that users may want to additionally install. This is helpful for suggesting complementary packages that aren't strictly required for the suggesting package to function properly. A package in `suggests` is functionaly equivalent to a package in `depends` except in the way it's presented to users in various prompts during :program:`zkg` operations. Users also have the option to ignore suggestions by supplying an additional ``--nosuggestions`` flag to :program:`zkg` commands. Available :program:`since bro-pkg v1.3`. .. _package-versioning: Package Versioning ------------------ Creating New Package Release Versions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Package's should use git tags for versioning their releases. Use the `Semantic Versioning `_ numbering scheme here. For example, to create a new tag for a package: .. code-block:: console $ git tag -a 1.0.0 -m 'Release 1.0.0' The tag name may also be of the ``vX.Y.Z`` form (prefixed by 'v'). Choose whichever you prefer. Then, assuming you've already set up a public/remote git repository (e.g. on GitHub) for your package, remember to push the tag to the remote repository: .. code-block:: console $ git push --tags Alternatively, if you expect to have a simple development process for your package, you may choose to not create any version tags and just always make commits directly to your package's *master* branch. Users will receive package updates differently depending on whether you decide to use release version tags or not. See the :ref:`package upgrade process ` documentation for more details on the differences. .. _package-upgrade-process: Package Upgrade Process ~~~~~~~~~~~~~~~~~~~~~~~ The :ref:`install command ` will either install a stable release version or the latest commit on a specific git branch of a package. The default installation behavior of :program:`zkg` is to look for the latest release version tag and install that. If there are no such version tags, it will fall back to installing the latest commit of the package's *master* branch Upon installing a package via a :ref:`git version tag `, the :ref:`upgrade command ` will only upgrade the local installation of that package if a greater version tag is available. In other words, you only receive stable release upgrades for packages installed in this way. Upon installing a package via a git branch name, the :ref:`upgrade command ` will upgrade the local installation of the package whenever a new commit becomes available at the end of the branch. This method of tracking packages is suitable for testing out development/experimental versions of packages. If a package was installed via a specific commit hash, then the package will never be eligible for automatic upgrades. package-manager-2.0.7/doc/ext/0000755000175000017500000000000013551142424015437 5ustar rhaistrhaistpackage-manager-2.0.7/doc/ext/sphinxarg/0000755000175000017500000000000013551142424017442 5ustar rhaistrhaistpackage-manager-2.0.7/doc/ext/sphinxarg/README0000644000175000017500000000041213551142424020317 0ustar rhaistrhaistModified version of sphinx-argparse 0.1.15: https://github.com/ribozz/sphinx-argparse Added Sphinx extension hooks to check for whether the python module associated with an argparse directive is outdated and so the .rst file it is used in needs to be re-read. package-manager-2.0.7/doc/ext/sphinxarg/__init__.py0000644000175000017500000000000013551142424021541 0ustar rhaistrhaistpackage-manager-2.0.7/doc/ext/sphinxarg/parser.py0000644000175000017500000001052513551142424021313 0ustar rhaistrhaistfrom argparse import _HelpAction, _SubParsersAction import re class NavigationException(Exception): pass def parser_navigate(parser_result, path, current_path=None): if isinstance(path, str): if path == '': return parser_result path = re.split('\s+', path) current_path = current_path or [] if len(path) == 0: return parser_result if 'children' not in parser_result: raise NavigationException( 'Current parser have no children elements. (path: %s)' % ' '.join(current_path)) next_hop = path.pop(0) for child in parser_result['children']: if child['name'] == next_hop: current_path.append(next_hop) return parser_navigate(child, path, current_path) raise NavigationException( 'Current parser have no children element with name: %s (path: %s)' % ( next_hop, ' '.join(current_path))) def _try_add_parser_attribute(data, parser, attribname): attribval = getattr(parser, attribname, None) if attribval is None: return if not isinstance(attribval, str): return if len(attribval) > 0: data[attribname] = attribval def _format_usage_without_prefix(parser): """ Use private argparse APIs to get the usage string without the 'usage: ' prefix. """ fmt = parser._get_formatter() fmt.add_usage(parser.usage, parser._actions, parser._mutually_exclusive_groups, prefix='') return fmt.format_help().strip() def parse_parser(parser, data=None, **kwargs): if data is None: data = { 'name': '', 'usage': parser.format_usage().strip(), 'bare_usage': _format_usage_without_prefix(parser), 'prog': parser.prog, } _try_add_parser_attribute(data, parser, 'description') _try_add_parser_attribute(data, parser, 'epilog') for action in parser._get_positional_actions(): if isinstance(action, _HelpAction): continue if isinstance(action, _SubParsersAction): helps = {} for item in action._choices_actions: helps[item.dest] = item.help # commands which share an existing parser are an alias, # don't duplicate docs subsection_alias = {} subsection_alias_names = set() for name, subaction in action._name_parser_map.items(): if subaction not in subsection_alias: subsection_alias[subaction] = [] else: subsection_alias[subaction].append(name) subsection_alias_names.add(name) for name, subaction in action._name_parser_map.items(): if name in subsection_alias_names: continue subalias = subsection_alias[subaction] subaction.prog = '%s %s' % (parser.prog, name) subdata = { 'name': name if not subalias else '%s (%s)' % (name, ', '.join(subalias)), 'help': helps.get(name, ''), 'usage': subaction.format_usage().strip(), 'bare_usage': _format_usage_without_prefix(subaction), } parse_parser(subaction, subdata, **kwargs) data.setdefault('children', []).append(subdata) continue if 'args' not in data: data['args'] = [] arg = { 'name': action.dest, 'help': action.help or '', 'metavar': action.metavar } if action.choices: arg['choices'] = action.choices data['args'].append(arg) show_defaults = ( ('skip_default_values' not in kwargs) or (kwargs['skip_default_values'] is False)) for action in parser._get_optional_actions(): if isinstance(action, _HelpAction): continue if 'options' not in data: data['options'] = [] option = { 'name': action.option_strings, 'default': action.default if show_defaults else '==SUPPRESS==', 'help': action.help or '' } if action.choices: option['choices'] = action.choices if "==SUPPRESS==" not in option['help']: data['options'].append(option) return data package-manager-2.0.7/doc/ext/sphinxarg/LICENSE0000644000175000017500000000206713551142424020454 0ustar rhaistrhaistThe MIT License (MIT) Copyright (c) 2013 Alex Rudakov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package-manager-2.0.7/doc/ext/sphinxarg/ext.py0000644000175000017500000004157113551142424020624 0ustar rhaistrhaistfrom argparse import ArgumentParser import os from docutils import nodes from docutils.statemachine import StringList from docutils.parsers.rst.directives import flag, unchanged try: # Removed as of Sphinx 1.7 from sphinx.util.compat import Directive except ImportError as err: from docutils.parsers.rst import Directive from sphinx.util.nodes import nested_parse_with_titles from sphinxarg.parser import parse_parser, parser_navigate def map_nested_definitions(nested_content): if nested_content is None: raise Exception('Nested content should be iterable, not null') # build definition dictionary definitions = {} for item in nested_content: if not isinstance(item, nodes.definition_list): continue for subitem in item: if not isinstance(subitem, nodes.definition_list_item): continue if not len(subitem.children) > 0: continue classifier = '@after' idx = subitem.first_child_matching_class(nodes.classifier) if idx is not None: ci = subitem[idx] if len(ci.children) > 0: classifier = ci.children[0].astext() if classifier is not None and classifier not in ( '@replace', '@before', '@after'): raise Exception('Unknown classifier: %s' % classifier) idx = subitem.first_child_matching_class(nodes.term) if idx is not None: ch = subitem[idx] if len(ch.children) > 0: term = ch.children[0].astext() idx = subitem.first_child_matching_class(nodes.definition) if idx is not None: def_node = subitem[idx] def_node.attributes['classifier'] = classifier definitions[term] = def_node return definitions def print_arg_list(data, nested_content): definitions = map_nested_definitions(nested_content) items = [] if 'args' in data: for arg in data['args']: my_def = [nodes.paragraph(text=arg['help'])] if arg['help'] else [] name = arg['name'] my_def = apply_definition(definitions, my_def, name) if len(my_def) == 0: my_def.append(nodes.paragraph(text='Undocumented')) if 'choices' in arg: my_def.append(nodes.paragraph( text=('Possible choices: %s' % ', '.join([str(c) for c in arg['choices']])))) argname = name if arg['metavar']: argname = arg['metavar'] items.append( nodes.option_list_item( '', nodes.option_group('', nodes.option('', nodes.option_string(text=argname))), nodes.description('', *my_def))) return nodes.option_list('', *items) if items else None def print_opt_list(data, nested_content): definitions = map_nested_definitions(nested_content) items = [] if 'options' in data: for opt in data['options']: names = [] my_def = [nodes.paragraph(text=opt['help'])] if opt['help'] else [] for name in opt['name']: option_declaration = [nodes.option_string(text=name)] if opt['default'] is not None \ and opt['default'] != '==SUPPRESS==': option_declaration += nodes.option_argument( '', text='=' + str(opt['default'])) names.append(nodes.option('', *option_declaration)) my_def = apply_definition(definitions, my_def, name) if len(my_def) == 0: my_def.append(nodes.paragraph(text='Undocumented')) if 'choices' in opt: my_def.append(nodes.paragraph( text=('Possible choices: %s' % ', '.join([str(c) for c in opt['choices']])))) items.append( nodes.option_list_item( '', nodes.option_group('', *names), nodes.description('', *my_def))) return nodes.option_list('', *items) if items else None def print_command_args_and_opts(arg_list, opt_list, sub_list=None): items = [] if arg_list: items.append(nodes.definition_list_item( '', nodes.term(text='Positional arguments:'), nodes.definition('', arg_list))) if opt_list: items.append(nodes.definition_list_item( '', nodes.term(text='Options:'), nodes.definition('', opt_list))) if sub_list and len(sub_list): items.append(nodes.definition_list_item( '', nodes.term(text='Sub-commands:'), nodes.definition('', sub_list))) return nodes.definition_list('', *items) def apply_definition(definitions, my_def, name): if name in definitions: definition = definitions[name] classifier = definition['classifier'] if classifier == '@replace': return definition.children if classifier == '@after': return my_def + definition.children if classifier == '@before': return definition.children + my_def raise Exception('Unknown classifier: %s' % classifier) return my_def def print_subcommand_list(data, nested_content): definitions = map_nested_definitions(nested_content) items = [] if 'children' in data: for child in data['children']: my_def = [nodes.paragraph( text=child['help'])] if child['help'] else [] name = child['name'] my_def = apply_definition(definitions, my_def, name) if len(my_def) == 0: my_def.append(nodes.paragraph(text='Undocumented')) if 'description' in child: my_def.append(nodes.paragraph(text=child['description'])) my_def.append(nodes.literal_block(text=child['usage'])) my_def.append(print_command_args_and_opts( print_arg_list(child, nested_content), print_opt_list(child, nested_content), print_subcommand_list(child, nested_content) )) items.append( nodes.definition_list_item( '', nodes.term('', '', nodes.strong(text=name)), nodes.definition('', *my_def) ) ) return nodes.definition_list('', *items) class ArgParseDirective(Directive): has_content = True option_spec = dict(module=unchanged, func=unchanged, ref=unchanged, prog=unchanged, path=unchanged, nodefault=flag, manpage=unchanged, nosubcommands=unchanged, passparser=flag) def _construct_manpage_specific_structure(self, parser_info): """ Construct a typical man page consisting of the following elements: NAME (automatically generated, out of our control) SYNOPSIS DESCRIPTION OPTIONS FILES SEE ALSO BUGS """ # SYNOPSIS section synopsis_section = nodes.section( '', nodes.title(text='Synopsis'), nodes.literal_block(text=parser_info["bare_usage"]), ids=['synopsis-section']) # DESCRIPTION section description_section = nodes.section( '', nodes.title(text='Description'), nodes.paragraph(text=parser_info.get( 'description', parser_info.get( 'help', "undocumented").capitalize())), ids=['description-section']) nested_parse_with_titles( self.state, self.content, description_section) if parser_info.get('epilog'): # TODO: do whatever sphinx does to understand ReST inside # docstrings magically imported from other places. The nested # parse method invoked above seem to be able to do this but # I haven't found a way to do it for arbitrary text description_section += nodes.paragraph( text=parser_info['epilog']) # OPTIONS section options_section = nodes.section( '', nodes.title(text='Options'), ids=['options-section']) if 'args' in parser_info: options_section += nodes.paragraph() options_section += nodes.subtitle(text='Positional arguments:') options_section += self._format_positional_arguments(parser_info) if 'options' in parser_info: options_section += nodes.paragraph() options_section += nodes.subtitle(text='Optional arguments:') options_section += self._format_optional_arguments(parser_info) items = [ # NOTE: we cannot generate NAME ourselves. It is generated by # docutils.writers.manpage synopsis_section, description_section, # TODO: files # TODO: see also # TODO: bugs ] if len(options_section.children) > 1: items.append(options_section) if 'nosubcommands' not in self.options: # SUBCOMMANDS section (non-standard) subcommands_section = nodes.section( '', nodes.title(text='Sub-Commands'), ids=['subcommands-section']) if 'children' in parser_info: subcommands_section += self._format_subcommands(parser_info) if len(subcommands_section) > 1: items.append(subcommands_section) if os.getenv("INCLUDE_DEBUG_SECTION"): import json # DEBUG section (non-standard) debug_section = nodes.section( '', nodes.title(text="Argparse + Sphinx Debugging"), nodes.literal_block(text=json.dumps(parser_info, indent=' ')), ids=['debug-section']) items.append(debug_section) return items def _format_positional_arguments(self, parser_info): assert 'args' in parser_info items = [] for arg in parser_info['args']: arg_items = [] if arg['help']: arg_items.append(nodes.paragraph(text=arg['help'])) else: arg_items.append(nodes.paragraph(text='Undocumented')) if 'choices' in arg: arg_items.append( nodes.paragraph( text='Possible choices: ' + ', '.join(arg['choices']))) items.append( nodes.option_list_item( '', nodes.option_group( '', nodes.option( '', nodes.option_string(text=arg['metavar']) ) ), nodes.description('', *arg_items))) return nodes.option_list('', *items) def _format_optional_arguments(self, parser_info): assert 'options' in parser_info items = [] for opt in parser_info['options']: names = [] opt_items = [] for name in opt['name']: option_declaration = [nodes.option_string(text=name)] if opt['default'] is not None \ and opt['default'] != '==SUPPRESS==': option_declaration += nodes.option_argument( '', text='=' + str(opt['default'])) names.append(nodes.option('', *option_declaration)) if opt['help']: opt_items.append(nodes.paragraph(text=opt['help'])) else: opt_items.append(nodes.paragraph(text='Undocumented')) if 'choices' in opt: opt_items.append( nodes.paragraph( text='Possible choices: ' + ', '.join(opt['choices']))) items.append( nodes.option_list_item( '', nodes.option_group('', *names), nodes.description('', *opt_items))) return nodes.option_list('', *items) def _format_subcommands(self, parser_info): assert 'children' in parser_info items = [] for subcmd in parser_info['children']: subcmd_items = [] if subcmd['help']: subcmd_items.append(nodes.paragraph(text=subcmd['help'])) else: subcmd_items.append(nodes.paragraph(text='Undocumented')) items.append( nodes.definition_list_item( '', nodes.term('', '', nodes.strong( text=subcmd['bare_usage'])), nodes.definition('', *subcmd_items))) return nodes.definition_list('', *items) def _nested_parse_paragraph(self, text): content = nodes.paragraph() self.state.nested_parse(StringList(text.split("\n")), 0, content) return content def run(self): if 'module' in self.options and 'func' in self.options: module_name = self.options['module'] attr_name = self.options['func'] elif 'ref' in self.options: _parts = self.options['ref'].split('.') module_name = '.'.join(_parts[0:-1]) attr_name = _parts[-1] else: raise self.error( ':module: and :func: should be specified, or :ref:') mod = __import__(module_name, globals(), locals(), [attr_name]) file_dependency = mod.__file__ if file_dependency.endswith('.pyc'): file_dependency = file_dependency[:-1] env = self.state.document.settings.env if not hasattr(env, 'argparse_usages'): env.argparse_usages = [] env.argparse_usages.append({ 'docname': env.docname, 'lineno': self.lineno, 'dependency_file': file_dependency, 'dependency_mtime': os.stat(file_dependency).st_mtime, }) if not hasattr(mod, attr_name): raise self.error(( 'Module "%s" has no attribute "%s"\n' 'Incorrect argparse :module: or :func: values?' ) % (module_name, attr_name)) func = getattr(mod, attr_name) if isinstance(func, ArgumentParser): parser = func elif 'passparser' in self.options: parser = ArgumentParser() func(parser) else: parser = func() if 'path' not in self.options: self.options['path'] = '' path = str(self.options['path']) if 'prog' in self.options: parser.prog = self.options['prog'] result = parse_parser( parser, skip_default_values='nodefault' in self.options) result = parser_navigate(result, path) if 'manpage' in self.options: return self._construct_manpage_specific_structure(result) nested_content = nodes.paragraph() self.state.nested_parse( self.content, self.content_offset, nested_content) nested_content = nested_content.children items = [] # add common content between for item in nested_content: if not isinstance(item, nodes.definition_list): items.append(item) if 'description' in result: items.append(self._nested_parse_paragraph(result['description'])) items.append(nodes.literal_block(text=result['usage'])) if 'nosubcommands' in self.options: subcommands = None else: subcommands = print_subcommand_list(result, nested_content) items.append(print_command_args_and_opts( print_arg_list(result, nested_content), print_opt_list(result, nested_content), subcommands )) if 'epilog' in result: items.append(self._nested_parse_paragraph(result['epilog'])) return items def env_get_outdated_hook(app, env, added, changed, removed): rval = set() if not hasattr(env, 'argparse_usages'): return [] for usage in env.argparse_usages: docname = usage['docname'] dep_file = usage['dependency_file'] dep_mtime = usage['dependency_mtime'] current_mtime = os.stat(dep_file).st_mtime if current_mtime > dep_mtime and docname not in removed: rval.add(docname) for docname in rval: from sphinx.util.console import blue msg = blue('found outdated argparse doc: {0}'.format(docname)) app.builder.info(msg) return list(rval) def env_purge_doc_hook(app, env, docname): if not hasattr(env, 'argparse_usages'): return env.argparse_usages = [ usage for usage in env.argparse_usages if usage['docname'] != docname] def setup(app): app.add_directive('argparse', ArgParseDirective) app.connect('env-get-outdated', env_get_outdated_hook) app.connect('env-purge-doc', env_purge_doc_hook) package-manager-2.0.7/doc/source.rst0000644000175000017500000001003113551142424016664 0ustar rhaistrhaist.. _Zeek Packages Git Repository: https://github.com/zeek/packages How-To: Create a Package Source =============================== :ref:`zkg `, by default, is configured to obtain packages from a single "package source", the `Zeek Packages Git Repository`_, which is hosted by and loosely curated by the Zeek Team. However, users may :ref:`configure zkg ` to use other package sources: either ones they've set up themselves for organization purposes or those hosted by other third parties. Package Source Setup -------------------- In order to set up such a package source, one simply has to create a git repository and then add :ref:`Package Index Files ` to it. These files may be created at any path in the package source's git repository. E.g. the `Zeek Packages Git Repository`_ organizes package index files hierarchically based on package author names such as :file:`alice/zkg.index` or :file:`bob/zkg.index` where ``alice`` and ``bob`` are usually GitHub usernames or some unique way of identifying the organization/person that maintains Zeek packages. However, a source is free to use a flat organization with a single, top-level :file:`zkg.index`. .. note:: The magic index file name of :file:`zkg.index` is available :program:`since zkg v2.0`. For compatibility purposes, the old index file name of :file:`bro-pkg.index` is also still supported. After creating a git repo for the package source and adding package index files to it, it's ready to be used by :ref:`zkg `. .. _package-index-file: Package Index Files ------------------- Files named :file:`zkg.index` (or the legacy :file:`bro-pkg.index`) are used to describe the :doc:`Zeek Packages ` found within the package source. They are simply a list of git URLs pointing to the git repositories of packages. For example:: https://github.com/zeek/foo https://github.com/zeek/bar https://github.com/zeek/baz Local filesystem paths are also valid if the package source is only meant for your own private usage or testing. Adding Packages --------------- Adding packages is as simple as adding new :ref:`Package Index Files ` or extending existing ones with new URLs and then commiting/pushing those changes to the package source git repository. :ref:`zkg ` will see new packages listed the next time it uses the :ref:`refresh command `. Removing Packages ----------------- Just remove the package's URL from the :ref:`Package Index File ` that it's contained within. After the next time :program:`zkg` uses the :ref:`refresh command `, it will no longer see the now-removed package when viewing package listings via by the :ref:`list command `. Users that had previously installed the now-removed package may continue to use it and receive updates for it. Aggregating Metadata -------------------- The maintainer/operator of a package source may choose to periodically aggregate the metadata contained in its packages' :file:`zkg.meta` (and legacy :file:`bro-pkg.meta`) files. The :ref:`zkg refresh ` is used to perform the task. For example: .. code-block:: console $ zkg refresh --aggregate --push --sources my_source The optional ``--push`` flag is helpful for setting up cron jobs to automatically perform this task periodically, assuming you've set up your git configuration to push changesets without interactive prompts. E.g. to set up pushing to remote servers you could set up SSH public key authentication. Aggregated metadata gets written to a file named :file:`aggregate.meta` at the top-level of a package source and the :ref:`list `, :ref:`search `, and :ref:`info ` all may access this file. Having access to the aggregated metadata in this way is beneficial to all :program:`zkg` users because they then will not have to crawl the set of packages listed in a source in order to obtain this metadata as it will have already been pre-aggregated by the operator of the package source. package-manager-2.0.7/doc/man/0000755000175000017500000000000013551142424015412 5ustar rhaistrhaistpackage-manager-2.0.7/doc/man/zkg.10000644000175000017500000006041513551142424016275 0ustar rhaistrhaist.\" Man page generated from reStructuredText. . .TH "ZKG" "1" "Oct 14, 2019" "2.0.7" "Zeek Package Manager" .SH NAME zkg \- Zeek Package Manager . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .sp A command\-line package manager for Zeek. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg [\-h] [\-\-version] [\-\-configfile CONFIGFILE] [\-\-verbose] {test,install,bundle,unbundle,remove,purge,refresh,upgrade,load,unload,pin,unpin,list,search,info,config,autoconfig,env} ... .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Options: .INDENT 7.0 .TP .B \-\-version show program\(aqs version number and exit .TP .B \-\-configfile Path to Zeek Package Manager config file. .sp See \fI\%Config File\fP\&. .TP .B \-\-verbose=0\fP,\fB \-v=0 Increase program output for debugging. Use multiple times for more output (e.g. \-vvv). .UNINDENT .UNINDENT .sp Environment Variables: .INDENT 0.0 .INDENT 3.5 \fBZKG_CONFIG_FILE\fP: Same as \fB\-\-configfile\fP option, but has less precedence. .UNINDENT .UNINDENT .SH COMMANDS .SS test .sp Runs the unit tests for the specified Zeek packages. In most cases, the "zeek" and "zeek\-config" programs will need to be in PATH before running this command. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg test [\-h] [\-\-version VERSION] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-version The version of the package to test. Only one package may be specified at a time when using this flag. A version tag, branch name, or commit hash may be specified here. If the package name refers to a local git repo with a working tree, then its currently active branch is used. The default for other cases is to use the latest version tag, or if a package has none, the "master" branch. .UNINDENT .UNINDENT .SS install .sp Installs packages from a configured package source or directly from a git URL. After installing, the package is marked as being "loaded" (see the \fBload\fP command). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg install [\-h] [\-\-force] [\-\-skiptests] [\-\-nodeps] [\-\-nosuggestions] [\-\-version VERSION] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .TP .B \-\-skiptests=False Skip running unit tests for packages before installation. .TP .B \-\-nodeps=False Skip all dependency resolution/checks. Note that using this option risks putting your installed package collection into a broken or unusable state. .TP .B \-\-nosuggestions=False Skip automatically installing suggested packages. .TP .B \-\-version The version of the package to install. Only one package may be specified at a time when using this flag. A version tag, branch name, or commit hash may be specified here. If the package name refers to a local git repo with a working tree, then its currently active branch is used. The default for other cases is to use the latest version tag, or if a package has none, the "master" branch. .UNINDENT .UNINDENT .SS remove .sp Unloads (see the \fBunload\fP command) and uninstalls a previously installed package. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg remove [\-h] [\-\-force] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .UNINDENT .UNINDENT .SS purge .sp Unloads (see the \fBunload\fP command) and uninstalls all previously installed packages. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg purge [\-h] [\-\-force] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .UNINDENT .UNINDENT .SS bundle .sp This command creates a bundle file containing a collection of Zeek packages. If \fB\-\-manifest\fP is used, the user supplies the list of packages to put in the bundle, else all currently installed packages are put in the bundle. A bundle file can be unpacked on any target system, resulting in a repeatable/specific set of packages being installed on that target system (see the \fBunbundle\fP command). This command may be useful for those that want to manage packages on a system that otherwise has limited network connectivity. E.g. one can use a system with an internet connection to create a bundle, transport that bundle to the target machine using whatever means are appropriate, and finally unbundle/install it on the target machine. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg bundle [\-h] [\-\-force] [\-\-nodeps] [\-\-nosuggestions] [\-\-manifest MANIFEST [MANIFEST ...] \-\-] filename.bundle .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B filename.bundle The path of the bundle file to create. It will be overwritten if it already exists. Note that if \-\-manifest is used before this filename is specified, you should use a double\-dash, \-\-, to first terminate that argument list. .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .TP .B \-\-nodeps=False Skip all dependency resolution/checks. Note that using this option risks creating a bundle of packages that is in a broken or unusable state. .TP .B \-\-nosuggestions=False Skip automatically bundling suggested packages. .TP .B \-\-manifest This may either be a file name or a list of packages to include in the bundle. If a file name is supplied, it should be in INI format with a single \(ga\(ga[bundle]\(ga\(ga section. The keys in that section correspond to package names and their values correspond to git version tags, branch names, or commit hashes. The values may be left blank to indicate that the latest available version should be used. .UNINDENT .UNINDENT .SS unbundle .sp This command unpacks a bundle file formerly created by the \fBbundle\fP command and installs all the packages contained within. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg unbundle [\-h] [\-\-force] [\-\-replace] filename.bundle .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B filename.bundle The path of the bundle file to install. .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .TP .B \-\-replace=False Using this flag first removes all installed packages before then installing the packages from the bundle. .UNINDENT .UNINDENT .SS refresh .sp Retrieve latest package metadata from sources and checks whether any installed packages have available upgrades. Note that this does not actually upgrade any packages (see the \fBupgrade\fP command for that). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg refresh [\-h] [\-\-aggregate] [\-\-push] [\-\-sources SOURCES [SOURCES ...]] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Options: .INDENT 7.0 .TP .B \-\-aggregate=False Crawls the urls listed in package source zkg.index (or legacy bro\-pkg.index) files and aggregates the metadata found in their zkg.meta (or legacy bro\-pkg.meta) files. The aggregated metadata is stored in the local clone of the package source that zkg uses internally locating package metadata. For each package, the metadata is taken from the highest available git version tag or the master branch if no version tags exist .TP .B \-\-push=False Push all local changes to package sources to upstream repos .TP .B \-\-sources A list of package source names to operate on. If this argument is not used, then the command will operate on all configured sources. .UNINDENT .UNINDENT .SS upgrade .sp Uprades the specified package(s) to latest available version. If no specific packages are specified, then all installed packages that are outdated and not pinned are upgraded. For packages that are installed with \fB\-\-version\fP using a git branch name, the package is updated to the latest commit on that branch, else the package is updated to the highest available git version tag. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg upgrade [\-h] [\-\-force] [\-\-skiptests] [\-\-nodeps] [\-\-nosuggestions] [package [package ...]] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-force=False Skip the confirmation prompt. .TP .B \-\-skiptests=False Skip running unit tests for packages before installation. .TP .B \-\-nodeps=False Skip all dependency resolution/checks. Note that using this option risks putting your installed package collection into a broken or unusable state. .TP .B \-\-nosuggestions=False Skip automatically installing suggested packages. .UNINDENT .UNINDENT .SS load .sp The Zeek Package Manager keeps track of all packages that are marked as "loaded" and maintains a single Zeek script that, when loaded by Zeek (e.g. via \fB@load packages\fP), will load the scripts from all "loaded" packages at once. This command adds a set of packages to the "loaded packages" list. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg load [\-h] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package Name(s) of package(s) to load. .UNINDENT .UNINDENT .SS unload .sp The Zeek Package Manager keeps track of all packages that are marked as "loaded" and maintains a single Zeek script that, when loaded by Zeek, will load the scripts from all "loaded" packages at once. This command removes a set of packages from the "loaded packages" list. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg unload [\-h] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .UNINDENT .SS pin .sp Pinned packages are ignored by the \fBupgrade\fP command. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg pin [\-h] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .UNINDENT .SS unpin .sp Packages that are not pinned are automatically upgraded by the \fBupgrade\fP command .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg unpin [\-h] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". .UNINDENT .UNINDENT .SS list .sp Outputs a list of packages that match a given category. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg list [\-h] [\-\-nodesc] [{all,installed,not_installed,loaded,unloaded,outdated}] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B category Package category used to filter listing. .sp Possible choices: all, installed, not_installed, loaded, unloaded, outdated .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-nodesc=False Do not display description text, just the package name(s). .UNINDENT .UNINDENT .SS search .sp Perform a substring search on package names and metadata tags. Surround search text with slashes to indicate it is a regular expression (e.g. \fB/text/\fP). .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg search [\-h] search_text [search_text ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B search_text The text(s) or pattern(s) to look for. .UNINDENT .UNINDENT .SS info .sp Shows detailed information/metadata for given packages. If the package is currently installed, additional information about the status of it is displayed. E.g. the installed version or whether it is currently marked as "pinned" or "loaded." .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg info [\-h] [\-\-version VERSION] [\-\-nolocal] [\-\-json] [\-\-jsonpretty SPACES] [\-\-allvers] package [package ...] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B package The name(s) of package(s) to operate on. The package may be named in several ways. If the package is part of a package source, it may be referred to by the base name of the package (last component of git URL) or its path within the package source. If two packages in different package sources have conflicting paths, then the package source name may be prepended to the package path to resolve the ambiguity. A full git URL may also be used to refer to a package that does not belong to a source. E.g. for a package source called "zeek" that has a package named "foo" located in either "alice/zkg.index" or "alice/bro\-pkg.index", the following names work: "foo", "alice/foo", "zeek/alice/foo". If a single name is given and matches one of the same categories as the "list" command, then it is automatically expanded to be the names of all packages which match the given category. .UNINDENT .TP .B Options: .INDENT 7.0 .TP .B \-\-version The version of the package metadata to inspect. A version tag, branch name, or commit hash and only one package at a time may be given when using this flag. If unspecified, the behavior depends on whether the package is currently installed. If installed, the metadata will be pulled from the installed version. If not installed, the latest version tag is used, or if a package has no version tags, the "master" branch is used. .TP .B \-\-nolocal=False Do not read information from locally installed packages. Instead read info from remote GitHub. .TP .B \-\-json=False Output package information as JSON. .TP .B \-\-jsonpretty Optional number of spaces to indent for pretty\-printed JSON output. .TP .B \-\-allvers=False When outputting package information as JSON, show metadata for all versions. This option can be slow since remote repositories may be cloned multiple times. Also, installed packages will show metadata only for the installed version unless the \-\-nolocal option is given. .UNINDENT .UNINDENT .SS config .sp The default output of this command is a valid package manager config file that corresponds to the one currently being used, but also with any defaulted field values filled in. This command also allows for only the value of a specific field to be output if the name of that field is given as an argument to the command. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg config [\-h] [{all,sources,user_vars,state_dir,script_dir,plugin_dir,zeek_dist,bro_dist}] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .TP .B Positional arguments: .INDENT 7.0 .TP .B config_param Name of a specific config file field to output. .sp Possible choices: all, sources, user_vars, state_dir, script_dir, plugin_dir, zeek_dist, bro_dist .UNINDENT .UNINDENT .SS autoconfig .sp The output of this command is a valid package manager config file that is generated by using the \fBzeek\-config\fP script that is installed along with Zeek. It is the suggested configuration to use for most Zeek installations. For this command to work, the \fBzeek\-config\fP (or \fBbro\-config\fP) script must be in \fBPATH\fP\&. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg autoconfig [\-h] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .UNINDENT .SS env .sp This command returns shell commands that, when executed, will correctly set \fBZEEKPATH\fP and \fBZEEK_PLUGIN_PATH\fP (also \fBBROPATH\fP and \fBBRO_PLUGIN_PATH\fP for legacy compatibility) to use scripts and plugins from packages installed by the package manager. For this command to function properly, either have the \fBzeek\-config\fP script (installed by zeek) in \fBPATH\fP, or have the \fBZEEKPATH\fP and \fBZEEK_PLUGIN_PATH\fP (or \fBBROPATH\fP and \fBBRO_PLUGIN_PATH\fP) environment variables already set so this command can append package\-specific paths to them. .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C usage: zkg env [\-h] .ft P .fi .UNINDENT .UNINDENT .INDENT 0.0 .UNINDENT .SH CONFIG FILE .sp The \fBzkg\fP command\-line tool uses an INI\-format config file to allow users to customize their Package Sources, Package installation paths, Zeek executable/source paths, and other \fBzkg\fP options. .sp See the default/example config file below for explanations of the available options and how to customize them: .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C # This is an example config file for zkg to explain what # settings are possible as well as their default values. # The order of precedence for how zkg finds/reads config files: # # (1) zkg \-\-configfile=/path/to/custom/config # (2) the ZKG_CONFIG_FILE environment variable # (3) a config file located at $HOME/.zkg/config # (4) if none of the above exist, then zkg uses builtin/default # values for all settings shown below [sources] # The default package source repository from which zkg fetches # packages. The default source may be removed, changed, or # additional sources may be added as long as they use a unique key # and a value that is a valid git URL. zeek = https://github.com/zeek/packages [paths] # Directory where source repositories are cloned, packages are # installed, and other package manager state information is # maintained. If left blank, this defaults to $HOME/.zkg state_dir = # The directory where package scripts are copied upon installation. # A subdirectory named "packages" is always created within the # specified path and the package manager will copy the directory # specified by the "script_dir" option of each package\(aqs zkg.meta # (or legacy bro\-pkg.meta) file there. # If left blank, this defaults to /script_dir # A typical path to set here is /share/zeek/site # If you decide to change this location after having already # installed packages, zkg will automatically relocate them # the next time you run any zkg command. script_dir = # The directory where package plugins are copied upon installation. # A subdirectory named "packages" is always created within the # specified path and the package manager will copy the directory # specified by the "plugin_dir" option of each package\(aqs zkg.meta # (or legacy bro\-pkg.meta) file there. # If left blank, this defaults to /plugin_dir # A typical path to set here is /lib/zeek/plugins # If you decide to change this location after having already # installed packages, zkg will automatically relocate them # the next time you run any zkg command. plugin_dir = # The directory containing Zeek distribution source code. This is only # needed when installing packages that contain Zeek plugins that are # not pre\-built. The legacy name of this option is "bro_dist". zeek_dist = [user_vars] # For any key in this section that is matched for value interpolation # in a package\(aqs zkg.meta (or legacy bro\-pkg.meta) file, the corresponding # value is substituted during execution of the package\(aqs \(gabuild_command\(ga. # This section is typically automatically populated with the # the answers supplied during package installation prompts # and, as a convenience feature, used to recall the last\-used settings # during subsequent operations (e.g. upgrades) on the same package. .ft P .fi .UNINDENT .UNINDENT .SH AUTHOR The Zeek Project .SH COPYRIGHT 2019, The Zeek Project .\" Generated by docutils manpage writer. . package-manager-2.0.7/doc/_templates/0000755000175000017500000000000013551142424016774 5ustar rhaistrhaistpackage-manager-2.0.7/doc/_templates/layout.html0000644000175000017500000000035613551142424021203 0ustar rhaistrhaist{% extends "!layout.html" %} {% if READTHEDOCS and current_version %} {% if current_version == "latest" or current_version == "stable" %} {% set current_version = current_version ~ " (" ~ version ~ ")" %} {% endif %} {% endif %} package-manager-2.0.7/doc/_templates/breadcrumbs.html0000644000175000017500000000120413551142424022150 0ustar rhaistrhaist{% extends "!breadcrumbs.html" %} {% block breadcrumbs_aside %}
  • {% if pagename != "search" %} {% if display_github %} {% if github_version == "master" %} {{ _('Edit on GitHub') }} {% endif %} {% elif show_source and has_source and sourcename %} {{ _('View page source') }} {% endif %} {% endif %}
  • {% endblock %} package-manager-2.0.7/doc/zkg.rst0000644000175000017500000000544513551142424016174 0ustar rhaistrhaist.. _bro-pkg: .. _zkg: zkg Command-Line Tool ===================== .. argparse:: :module: zkg :func: argparser :prog: zkg :nosubcommands: --configfile : @after See :ref:`zkg-config-file`. Commands -------- .. _test-command: test ~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: test .. _install-command: install ~~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: install .. _remove-command: remove ~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: remove .. _purge-command: purge ~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: purge .. _bundle-command: bundle ~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: bundle .. _unbundle-command: unbundle ~~~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: unbundle .. _refresh-command: refresh ~~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: refresh .. _upgrade-command: upgrade ~~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: upgrade .. _load-command: load ~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: load .. _unload-command: unload ~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: unload .. _pin-command: pin ~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: pin .. _unpin-command: unpin ~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: unpin .. _list-command: list ~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: list .. _search-command: search ~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: search .. _info-command: info ~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: info .. _config-command: config ~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: config .. _autoconfig-command: autoconfig ~~~~~~~~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: autoconfig .. _env-command: env ~~~ .. argparse:: :module: zkg :func: argparser :prog: zkg :path: env .. _bro-pkg-config-file: .. _zkg-config-file: Config File ----------- The :program:`zkg` command-line tool uses an INI-format config file to allow users to customize their :doc:`Package Sources `, :doc:`Package ` installation paths, Zeek executable/source paths, and other :program:`zkg` options. See the default/example config file below for explanations of the available options and how to customize them: .. literalinclude:: ../zkg.config package-manager-2.0.7/doc/overview.rst0000644000175000017500000000216313551142424017241 0ustar rhaistrhaist.. _Zeek: https://www.zeek.org .. _Zeek package source: https://github.com/zeek/packages .. _documentation: https://docs.zeek.org/projects/package-manager Zeek Package Manager ==================== The Zeek Package Manager makes it easy for Zeek users to install and manage third party scripts as well as plugins for Zeek and ZeekControl. The command-line tool is preconfigured to download packages from the `Zeek package source`_ , a GitHub repository that has been set up such that any developer can request their Zeek package be included. See the ``README`` file of that repository for information regarding the package submission process. :note: It's left up to users to decide for themselves via code review, GitHub comments/stars, or other metrics whether any given package is trustworthy as there is no implied guarantees that it's secure just because it's been accepted into the default package source. See the package manager documentation_ for further usage information, how-to guides, and walkthroughs. For offline reading, it's also available in the ``doc/`` directory of the source code distribution. package-manager-2.0.7/doc/index.rst0000644000175000017500000000022013551142424016472 0ustar rhaistrhaist.. include:: overview.rst .. toctree:: :hidden: :numbered: quickstart zkg package source api/index developers package-manager-2.0.7/setup.py0000644000175000017500000000253413551142424015610 0ustar rhaistrhaistfrom setuptools import setup from sys import version_info import os install_requires = ['gitpython', 'semantic_version', 'btest'] if version_info[0] + version_info[1] / 10 < 3.5 or os.environ.get('ZKG_PYPI_DIST'): install_requires.append("configparser") setup( name='bro-pkg' if os.environ.get('ZKG_PYPI_DIST_LEGACY') else 'zkg', version=open('VERSION').read().replace('-', '.dev', 1).strip(), description='The Zeek Package Manager', long_description=open('README').read(), license='University of Illinois/NCSA Open Source License', keywords='zeek bro zeekctl zeekcontrol broctl brocontrol package manager scripts plugins security', maintainer='The Zeek Project', maintainer_email='info@zeek.org', url='https://github.com/zeek/package-manager', scripts=['bro-pkg', 'zkg'], packages=['bropkg', 'zeekpkg'], install_requires=install_requires, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'License :: OSI Approved :: University of Illinois/NCSA Open Source License', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS :: MacOS X', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Topic :: System :: Networking :: Monitoring', 'Topic :: Utilities', ], ) package-manager-2.0.7/VERSION0000644000175000017500000000000613551142424015136 0ustar rhaistrhaist2.0.7 package-manager-2.0.7/README.rst0000777000175000017500000000000013551142424020723 2doc/overview.rstustar rhaistrhaistpackage-manager-2.0.7/zkg.py0000777000175000017500000000000013551142424015752 2zkgustar rhaistrhaistpackage-manager-2.0.7/bropkg/0000755000175000017500000000000013551142424015356 5ustar rhaistrhaistpackage-manager-2.0.7/bropkg/__init__.py0000644000175000017500000000050313551142424017465 0ustar rhaistrhaistfrom __future__ import print_function import sys from zeekpkg import __version__ from zeekpkg import __all__ from zeekpkg import LOG from zeekpkg.manager import * from zeekpkg.package import * from zeekpkg.source import * print("Warning: the 'bropkg' module is deprecated use 'zeekpkg' instead.", file=sys.stderr) package-manager-2.0.7/bropkg/_util.py0000644000175000017500000000027513551142424017050 0ustar rhaistrhaistfrom __future__ import print_function import sys from zeekpkg._util import * print("Warning: the 'bropkg._util' module is deprecated use 'zeekpkg._util' instead.", file=sys.stderr) package-manager-2.0.7/bropkg/manager.py0000644000175000017500000000030313551142424017336 0ustar rhaistrhaistfrom __future__ import print_function import sys from zeekpkg.manager import * print("Warning: the 'bropkg.manager' module is deprecated use 'zeekpkg.manager' instead.", file=sys.stderr) package-manager-2.0.7/bropkg/source.py0000644000175000017500000000030013551142424017221 0ustar rhaistrhaistfrom __future__ import print_function import sys from zeekpkg.source import * print("Warning: the 'bropkg.source' module is deprecated use 'zeekpkg.source' instead.", file=sys.stderr) package-manager-2.0.7/bropkg/package.py0000644000175000017500000000030313551142424017317 0ustar rhaistrhaistfrom __future__ import print_function import sys from zeekpkg.package import * print("Warning: the 'bropkg.package' module is deprecated use 'zeekpkg.package' instead.", file=sys.stderr) package-manager-2.0.7/bro-pkg.py0000777000175000017500000000000013551142424017266 2bro-pkgustar rhaistrhaistpackage-manager-2.0.7/Makefile0000644000175000017500000000164713551142424015542 0ustar rhaistrhaistVERSION=`cat VERSION` .PHONY: all all: .PHONY: doc doc: man html .PHONY: man man: (cd doc && make man && mkdir -p man && cp _build/man/zkg.1 man) .PHONY: html html: (cd doc && make html) .PHONY: livehtml livehtml: (cd doc && make livehtml) .PHONY: test test: @( cd testing && make ) .PHONY: dist dist: ZKG_PYPI_DIST=yes python setup.py bdist_wheel ZKG_PYPI_DIST=yes ZKG_PYPI_DIST_LEGACY=yes python setup.py bdist_wheel .PHONY: upload upload: twine-check dist twine upload -u zeek dist/zkg-$(VERSION)-py2.py3-none-any.whl twine upload -u zeek dist/bro_pkg-$(VERSION)-py2.py3-none-any.whl .PHONY: twine-check twine-check: @type twine > /dev/null 2>&1 || \ { \ echo "Uploading to PyPi requires 'twine' and it's not found in PATH."; \ echo "Install it and/or make sure it is in PATH."; \ echo "E.g. you could use the following command to install it:"; \ echo "\tpip install twine"; \ echo ; \ exit 1; \ } package-manager-2.0.7/COPYING0000644000175000017500000000351613551142424015132 0ustar rhaistrhaistCopyright (c) 2016 The Board of Trustees of the University of Illinois. All rights reserved. Developed by: Cybersecurity Directorate National Center for Supercomputing Applications University of Illinois http://illinois.edu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. * Neither the names of the National Center for Supercomputing Applications, the University of Illinois, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. Note that some files in the distribution may carry their own copyright notices. package-manager-2.0.7/zkg0000755000175000017500000023254413551142424014625 0ustar rhaistrhaist#! /usr/bin/env python from __future__ import print_function from zeekpkg._util import ( make_dir, make_symlink, find_program, read_zeek_config_line, std_encoding, ) from zeekpkg.package import ( TRACKING_METHOD_VERSION, ) import os import io import sys import errno import argparse import logging import threading import tarfile import filecmp import shutil import subprocess import json try: from backports import configparser except ImportError as err: import configparser if ( sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 2) ): from configparser import SafeConfigParser as GoodConfigParser else: # SafeConfigParser renamed to ConfigParser in Python >= 3.2 from configparser import ConfigParser as GoodConfigParser import git import zeekpkg def get_input(prompt): if sys.version_info[0] < 3: prompt_bytes = prompt.encode(std_encoding(sys.stdout)) return raw_input(prompt_bytes).decode(std_encoding(sys.stdin)) return input(prompt) def confirmation_prompt(prompt, default_to_yes=True): yes = {'y', 'ye', 'yes'} if default_to_yes: prompt += ' [Y/n] ' else: prompt += ' [N/y] ' choice = get_input(prompt).lower() if not choice: return default_to_yes if choice in yes: return True print('Abort.') return False def prompt_for_user_vars(manager, config, configfile, force, pkg_infos): answers = {} for info in pkg_infos: name = info.package.qualified_name() requested_user_vars = info.user_vars() if requested_user_vars is None: print_error(str.format('error: malformed user_vars in "{}"', name)) sys.exit(1) if not requested_user_vars: continue for key, value, desc in requested_user_vars: from_env = False default_value = os.environ.get(key) if default_value: from_env = True else: if config.has_section('user_vars'): v = config.get('user_vars', key, fallback=None) if v: default_value = v else: default_value = value else: default_value = value if force: answers[key] = default_value else: if from_env: print(str.format( '{} will use value of {} ({}) from environment: {}', name, key, desc, default_value)) answers[key] = default_value else: prompt = '{} asks for {} ({}) ? [{}] '.format( name, key, desc, default_value) response = get_input(prompt) if response: answers[key] = response else: answers[key] = default_value if not force and answers: for key, value in answers.items(): if not config.has_section('user_vars'): config.add_section('user_vars') config.set('user_vars', key, value) if configfile: with io.open(configfile, 'w', encoding=std_encoding(sys.stdout)) as f: config.write(f) print('Saved answers to config file: {}'.format(configfile)) manager.user_vars = answers def print_error(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) def config_items(config, section): # Same as config.items(section), but exclude default keys. defaults = {key for key, _ in config.items('DEFAULT')} items = sorted(config.items(section)) return [(key, value) for (key, value) in items if key not in defaults] def file_is_not_empty(path): return os.path.isfile(path) and os.path.getsize(path) > 0 def find_configfile(): configfile = os.environ.get('ZKG_CONFIG_FILE') if not configfile: # For backward compatibility with old bro-pkg. configfile = os.environ.get('BRO_PKG_CONFIG_FILE') if configfile and file_is_not_empty(configfile): return configfile configfile = os.path.join(default_config_dir(), 'config') if file_is_not_empty(configfile): return configfile configfile = os.path.join(legacy_config_dir(), 'config') if file_is_not_empty(configfile): return configfile return None def default_config_dir(): return os.path.join(os.path.expanduser('~'), '.zkg') def legacy_config_dir(): return os.path.join(os.path.expanduser('~'), '.bro-pkg') def default_state_dir(): return os.path.join(os.path.expanduser('~'), '.zkg') def legacy_state_dir(): return os.path.join(os.path.expanduser('~'), '.bro-pkg') def create_config(configfile): config = GoodConfigParser() if configfile: if not os.path.isfile(configfile): print_error('error: invalid config file "{}"'.format(configfile)) sys.exit(1) config.read(configfile) if not config.has_section('sources'): config.add_section('sources') if not config.has_section('paths'): config.add_section('paths') if not configfile: config.set('sources', 'zeek', 'https://github.com/zeek/packages') def config_option_set(config, section, option): return config.has_option(section, option) and config.get(section, option) def get_option(config, section, option, default): if config_option_set(config, section, option): return config.get(section, option) return default state_dir = get_option(config, 'paths', 'state_dir', os.path.join(default_state_dir())) script_dir = get_option(config, 'paths', 'script_dir', os.path.join(state_dir, 'script_dir')) plugin_dir = get_option(config, 'paths', 'plugin_dir', os.path.join(state_dir, 'plugin_dir')) zeek_dist = get_option(config, 'paths', 'zeek_dist', '') if not zeek_dist: zeek_dist = get_option(config, 'paths', 'bro_dist', '') config.set('paths', 'state_dir', state_dir) config.set('paths', 'script_dir', script_dir) config.set('paths', 'plugin_dir', plugin_dir) config.set('paths', 'zeek_dist', zeek_dist) def expand_config_values(config, section): for key, value in config.items(section): value = os.path.expandvars(os.path.expanduser(value)) config.set(section, key, value) expand_config_values(config, 'sources') expand_config_values(config, 'paths') for key, value in config.items('paths'): if value and not os.path.isabs(value): print_error(str.format('error: invalid config file value for key' ' "{}" in section [paths]: "{}" is not' ' an absolute path', key, value)) sys.exit(1) return config def active_git_branch(path): try: repo = git.Repo(path) except git.exc.NoSuchPathError as error: return None if not repo.working_tree_dir: return None try: rval = repo.active_branch except TypeError as error: # return detached commit rval = repo.head.commit if not rval: return None rval = str(rval) return rval def is_git_repo_dirty(git_url): if not git_url.startswith('.') and not git_url.startswith('/'): return False try: repo = git.Repo(git_url) except git.exc.NoSuchPathError as error: return False return repo.is_dirty(untracked_files=True) def create_manager(config): state_dir = config.get('paths', 'state_dir') script_dir = config.get('paths', 'script_dir') plugin_dir = config.get('paths', 'plugin_dir') zeek_dist = config.get('paths', 'zeek_dist') if state_dir == default_state_dir(): if not os.path.exists(state_dir) and os.path.exists(legacy_state_dir()): make_symlink(legacy_state_dir(), state_dir) try: manager = zeekpkg.Manager(state_dir=state_dir, script_dir=script_dir, plugin_dir=plugin_dir, zeek_dist=zeek_dist) except (OSError, IOError) as error: if error.errno == errno.EACCES: print_error('{}: {}'.format(type(error).__name__, error)) def check_permission(d): if os.access(d, os.W_OK): return print_error( 'error: user does not have write access in {}'.format(d)) check_permission(state_dir) check_permission(script_dir) check_permission(plugin_dir) sys.exit(1) raise for key, value in config_items(config, 'sources'): error = manager.add_source(name=key, git_url=value) if error: print_error(str.format( 'warning: skipped using package source named "{}": {}', key, error)) return manager class InstallWorker(threading.Thread): def __init__(self, manager, package_name, package_version): super(InstallWorker, self).__init__() self.manager = manager self.package_name = package_name self.package_version = package_version self.error = '' def run(self): self.error = self.manager.install( self.package_name, self.package_version) def cmd_test(manager, args, config, configfile): if args.version and len(args.package) > 1: print_error( 'error: "install --version" may only be used for a single package') sys.exit(1) package_infos = [] for name in args.package: if is_git_repo_dirty(name): print_error('error: local git clone at {} is dirty'.format(name)) sys.exit(1) version = args.version if args.version else active_git_branch(name) package_info = manager.info(name, version=version, prefer_installed=False) if package_info.invalid_reason: print_error(str.format('error: invalid package "{}": {}', name, package_info.invalid_reason)) sys.exit(1) if not version: version = package_info.best_version() package_infos.append((package_info, version)) all_passed = True for info, version in package_infos: name = info.package.qualified_name() if 'test_command' not in info.metadata: print(str.format('{}: no test_command found in metadata, skipping', name)) continue error_msg, passed, test_dir = manager.test(name, version) if error_msg: all_passed = False print_error(str.format('error: failed to run tests for "{}": {}', name, error_msg)) continue if passed: print(str.format('{}: all tests passed', name)) else: all_passed = False clone_dir = os.path.join(os.path.join(test_dir, "clones"), info.package.name) print_error(str.format('error: package "{}" tests failed, inspect' ' contents of {} for details, especially' ' any "zkg.test_command.{{stderr,stdout}}"' ' files within {}', name, test_dir, clone_dir)) if not all_passed: sys.exit(1) def cmd_install(manager, args, config, configfile): if args.version and len(args.package) > 1: print_error( 'error: "install --version" may only be used for a single package') sys.exit(1) package_infos = [] for name in args.package: if is_git_repo_dirty(name): print_error('error: local git clone at {} is dirty'.format(name)) sys.exit(1) version = args.version if args.version else active_git_branch(name) package_info = manager.info(name, version=version, prefer_installed=False) if package_info.invalid_reason: print_error(str.format('error: invalid package "{}": {}', name, package_info.invalid_reason)) sys.exit(1) if not version: version = package_info.best_version() package_infos.append((package_info, version, False)) new_pkgs = [] if not args.nodeps: to_validate = [(info.package.qualified_name(), version) for info, version, _ in package_infos] invalid_reason, new_pkgs = manager.validate_dependencies( to_validate, ignore_suggestions=args.nosuggestions) if invalid_reason: print_error('error: failed to resolve dependencies:', invalid_reason) sys.exit(1) if not args.force: package_listing = '' for info, version, _ in package_infos: name = info.package.qualified_name() package_listing += ' {} ({})\n'.format(name, version) print('The following packages will be INSTALLED:') print(package_listing) if new_pkgs: dependency_listing = '' for info, version, suggested in new_pkgs: name = info.package.qualified_name() dependency_listing += ' {} ({})'.format( name, version) if suggested: dependency_listing += ' (suggested)' dependency_listing += '\n' print('The following dependencies will be INSTALLED:') print(dependency_listing) allpkgs = package_infos + new_pkgs extdep_listing = '' for info, version, _ in allpkgs: name = info.package.qualified_name() extdeps = info.dependencies(field='external_depends') if extdeps is None: extdep_listing += ' from {} ({}):\n \n'.format( name, version) continue if extdeps: extdep_listing += ' from {} ({}):\n'.format(name, version) for extdep, semver in sorted(extdeps.items()): extdep_listing += ' {} {}\n'.format(extdep, semver) if extdep_listing: print('Verify the following REQUIRED external dependencies:\n' '(Ensure their installation on all relevant systems before' ' proceeding):') print(extdep_listing) if not confirmation_prompt('Proceed?'): return package_infos += new_pkgs prompt_for_user_vars(manager, config, configfile, args.force, [info for info, _, _ in package_infos]) if not args.skiptests: for info, version, _ in package_infos: name = info.package.qualified_name() if 'test_command' not in info.metadata: zeekpkg.LOG.info( 'Skipping unit tests for "%s": no test_command in metadata', name) continue print('Running unit tests for "{}"'.format(name)) error, passed, test_dir = manager.test(name, version) error_msg = '' if error: error_msg = str.format( 'failed to run tests for {}: {}', name, error) elif not passed: clone_dir = os.path.join(os.path.join(test_dir, "clones"), info.package.name) error_msg = str.format('"{}" tests failed, inspect contents of' ' {} for details, especially any' ' "zkg.test_command.{{stderr,stdout}}"' ' files within {}', name, test_dir, clone_dir) if error_msg: print_error('error: {}'.format(error_msg)) if args.force: continue if not confirmation_prompt('Proceed to install anyway?', default_to_yes=False): return join_timeout = 0.01 tick_interval = 1 installs_failed = [] for info, version, _ in package_infos: name = info.package.qualified_name() time_accumulator = 0 tick_count = 0 is_overwriting = False ipkg = manager.find_installed_package(name) if ipkg: is_overwriting = True modifications = manager.modified_config_files(ipkg) backup_files = manager.backup_modified_files(name, modifications) prev_upstream_config_files = manager.save_temporary_config_files( ipkg) worker = InstallWorker(manager, name, version) worker.start() while worker.isAlive(): worker.join(join_timeout) time_accumulator += join_timeout if time_accumulator >= tick_interval: if tick_count == 0: print('Installing "{}"'.format(name), end='') else: print('.', end='') sys.stdout.flush() tick_count += 1 time_accumulator -= tick_interval if tick_count != 0: print('') if worker.error: print('Failed installing "{}": {}'.format(name, worker.error)) installs_failed.append((name, version)) continue ipkg = manager.find_installed_package(name) print('Installed "{}" ({})'.format(name, ipkg.status.current_version)) if is_overwriting: for i, mf in enumerate(modifications): next_upstream_config_file = mf[1] if not os.path.isfile(next_upstream_config_file): print("\tConfig file no longer exists:") print("\t\t" + next_upstream_config_file) print("\tPrevious, locally modified version backed up to:") print("\t\t" + backup_files[i]) continue prev_upstream_config_file = prev_upstream_config_files[i][1] if filecmp.cmp(prev_upstream_config_file, next_upstream_config_file): # Safe to restore user's version shutil.copy2(backup_files[i], next_upstream_config_file) continue print("\tConfig file has been overwritten with a different version:") print("\t\t" + next_upstream_config_file) print("\tPrevious, locally modified version backed up to:") print("\t\t" + backup_files[i]) if manager.has_scripts(ipkg): load_error = manager.load(name) if load_error: print('Failed loading "{}": {}'.format(name, load_error)) else: print('Loaded "{}"'.format(name)) if installs_failed: print_error('error: incomplete installation, the follow packages' ' failed to be installed:') for n, v in installs_failed: print_error(' {} ({})'.format(n, v)) sys.exit(1) def cmd_bundle(manager, args, config, configfile): packages_to_bundle = [] prefer_existing_clones = False if args.manifest: if len(args.manifest) == 1 and os.path.isfile(args.manifest[0]): config = GoodConfigParser(delimiters='=') config.optionxform = str if config.read(args.manifest[0]) and config.has_section('bundle'): packages = config.items('bundle') else: print_error('error: "{}" is not a valid manifest file'.format( args.manifest[0])) sys.exit(1) else: packages = [(name, '') for name in args.manifest] to_validate = [] new_pkgs = [] for name, version in packages: if is_git_repo_dirty(name): print_error('error: local git clone at {} is dirty'.format(name)) sys.exit(1) if not version: version = active_git_branch(name) info = manager.info(name, version=version, prefer_installed=False) if info.invalid_reason: print_error(str.format('error: invalid package "{}": {}', name, info.invalid_reason)) sys.exit(1) if not version: version = info.best_version() to_validate.append((info.package.qualified_name(), version)) packages_to_bundle.append((info.package.qualified_name(), info.package.git_url, version, False, False)) if not args.nodeps: invalid_reason, new_pkgs = manager.validate_dependencies( to_validate, True, ignore_suggestions=args.nosuggestions) if invalid_reason: print_error('error: failed to resolve dependencies:', invalid_reason) sys.exit(1) for info, version, suggested in new_pkgs: packages_to_bundle.append((info.package.qualified_name(), info.package.git_url, version, True, suggested)) else: prefer_existing_clones = True for ipkg in manager.installed_packages(): packages_to_bundle.append((ipkg.package.qualified_name(), ipkg.package.git_url, ipkg.status.current_version, False, False)) if not packages_to_bundle: print_error('error: no packages to put in bundle') sys.exit(1) if not args.force: package_listing = '' for name, _, version, is_dependency, is_suggestion in packages_to_bundle: package_listing += ' {} ({})'.format(name, version) if is_suggestion: package_listing += ' (suggested)' elif is_dependency: package_listing += ' (dependency)' package_listing += '\n' print('The following packages will be BUNDLED into {}:'.format( args.bundle_filename)) print(package_listing) if not confirmation_prompt('Proceed?'): return git_urls = [(git_url, version) for _, git_url, version, _, _ in packages_to_bundle] error = manager.bundle(args.bundle_filename, git_urls, prefer_existing_clones=prefer_existing_clones) if error: print_error('error: failed to create bundle: {}'.format(error)) sys.exit(1) print('Bundle successfully written: {}'.format(args.bundle_filename)) def cmd_unbundle(manager, args, config, configfile): prev_load_status = {} for ipkg in manager.installed_packages(): prev_load_status[ipkg.package.git_url] = ipkg.status.is_loaded if args.replace: cmd_purge(manager, args, config, configfile) error, bundle_info = manager.bundle_info(args.bundle_filename) if error: print_error('error: failed to unbundle {}: {}'.format( args.bundle_filename, error)) sys.exit(1) for git_url, version, pkg_info in bundle_info: if pkg_info.invalid_reason: name = pkg_info.package.qualified_name() print_error('error: bundle {} contains invalid package {}: {}'.format( args.bundle_filename, name, pkg_info.invalid_reason)) sys.exit(1) if not bundle_info: print('No packages in bundle.') return if not args.force: package_listing = '' for git_url, version, _ in bundle_info: name = git_url for pkg in manager.source_packages(): if pkg.git_url == git_url: name = pkg.qualified_name() break package_listing += ' {} ({})\n'.format(name, version) print('The following packages will be INSTALLED:') print(package_listing) extdep_listing = '' for git_url, version, info in bundle_info: name = git_url for pkg in manager.source_packages(): if pkg.git_url == git_url: name = pkg.qualified_name() break extdeps = info.dependencies(field='external_depends') if extdeps is None: extdep_listing += ' from {} ({}):\n \n'.format( name, version) continue if extdeps: extdep_listing += ' from {} ({}):\n'.format(name, version) for extdep, semver in sorted(extdeps.items()): extdep_listing += ' {} {}\n'.format(extdep, semver) if extdep_listing: print('Verify the following REQUIRED external dependencies:\n' '(Ensure their installation on all relevant systems before' ' proceeding):') print(extdep_listing) if not confirmation_prompt('Proceed?'): return prompt_for_user_vars(manager, config, configfile, args.force, [info for _, _, info in bundle_info]) error = manager.unbundle(args.bundle_filename) if error: print_error('error: failed to unbundle {}: {}'.format( args.bundle_filename, error)) sys.exit(1) for git_url, _, _ in bundle_info: if git_url in prev_load_status: need_load = prev_load_status[git_url] else: need_load = True ipkg = manager.find_installed_package(git_url) if not ipkg: print('Skipped loading "{}": failed to install'.format(git_url)) continue name = ipkg.package.qualified_name() if not need_load: print('Skipped loading "{}"'.format(name)) continue load_error = manager.load(name) if load_error: print('Failed loading "{}": {}'.format(name, load_error)) else: print('Loaded "{}"'.format(name)) print('Unbundling complete.') def cmd_remove(manager, args, config, configfile): packages_to_remove = [] for name in args.package: ipkg = manager.find_installed_package(name) if not ipkg: print_error( 'error: package "{}" is not installed'.format(name)) sys.exit(1) packages_to_remove.append(ipkg) if not args.force: package_listing = '' for ipkg in packages_to_remove: name = ipkg.package.qualified_name() package_listing += ' {}\n'.format(name) print('The following packages will be REMOVED:') print(package_listing) if not confirmation_prompt('Proceed?'): return had_failure = False for ipkg in packages_to_remove: name = ipkg.package.qualified_name() modifications = manager.modified_config_files(ipkg) backup_files = manager.backup_modified_files(name, modifications) if manager.remove(name): print('Removed "{}"'.format(name)) if backup_files: print('\tCreated backups of locally modified config files:') for backup_file in backup_files: print('\t' + backup_file) else: print('Failed removing "{}": no such package installed'.format(name)) had_failure = True if had_failure: sys.exit(1) def cmd_purge(manager, args, config, configfile): packages_to_remove = manager.installed_packages() if not packages_to_remove: print('No packages to remove.') return if not args.force: package_listing = '' names_to_remove = [ipkg.package.qualified_name() for ipkg in packages_to_remove] for name in names_to_remove: package_listing += ' {}\n'.format(name) print('The following packages will be REMOVED:') print(package_listing) if not confirmation_prompt('Proceed?'): return had_failure = False for ipkg in packages_to_remove: name = ipkg.package.qualified_name() modifications = manager.modified_config_files(ipkg) backup_files = manager.backup_modified_files(name, modifications) if manager.remove(name): print('Removed "{}"'.format(name)) if backup_files: print('\tCreated backups of locally modified config files:') for backup_file in backup_files: print('\t' + backup_file) else: print('Unknown error removing "{}"'.format(name)) had_failure = True if had_failure: sys.exit(1) def outdated(manager): return [ipkg.package.qualified_name() for ipkg in manager.installed_packages() if ipkg.status.is_outdated] def cmd_refresh(manager, args, config, configfile): if not args.sources: args.sources = list(manager.sources.keys()) had_failure = False for source in args.sources: print('Refresh package source: {}'.format(source)) src_pkgs_before = {i.qualified_name() for i in manager.source_packages()} error = manager.refresh_source( source, aggregate=args.aggregate, push=args.push) if error: had_failure = True print_error( 'error: failed to refresh "{}": {}'.format(source, error)) continue src_pkgs_after = {i.qualified_name() for i in manager.source_packages()} if src_pkgs_before == src_pkgs_after: print('\tNo changes') else: print('\tChanges:') diff = src_pkgs_before.symmetric_difference(src_pkgs_after) for name in diff: change = 'Added' if name in src_pkgs_after else 'Removed' print('\t\t{} {}'.format(change, name)) if args.aggregate: print('\tMetadata aggregated') if args.push: print('\tPushed aggregated metadata') outdated_before = {i for i in outdated(manager)} print('Refresh installed packages') manager.refresh_installed_packages() outdated_after = {i for i in outdated(manager)} if outdated_before == outdated_after: print('\tNo new outdated packages') else: print('\tNew outdated packages:') diff = outdated_before.symmetric_difference(outdated_after) for name in diff: ipkg = manager.find_installed_package(name) version_change = version_change_string(manager, ipkg) print('\t\t{} {}'.format(name, version_change)) if had_failure: sys.exit(1) def version_change_string(manager, installed_package): old_version = installed_package.status.current_version new_version = old_version version_change = '' if installed_package.status.tracking_method == TRACKING_METHOD_VERSION: versions = manager.package_versions(installed_package) if len(versions): new_version = versions[-1] version_change = '({} -> {})'.format(old_version, new_version) else: version_change = '({})'.format(new_version) return version_change def cmd_upgrade(manager, args, config, configfile): if args.package: pkg_list = args.package else: pkg_list = outdated(manager) outdated_packages = [] package_listing = '' for name in pkg_list: ipkg = manager.find_installed_package(name) if not ipkg: print_error('error: package "{}" is not installed'.format(name)) sys.exit(1) name = ipkg.package.qualified_name() if not ipkg.status.is_outdated: continue if not manager.match_source_packages(name): name = ipkg.package.git_url info = manager.info(name, version=ipkg.status.current_version, prefer_installed=False) if info.invalid_reason: print_error(str.format('error: invalid package "{}": {}', name, info.invalid_reason)) sys.exit(1) next_version = ipkg.status.current_version if ( ipkg.status.tracking_method == TRACKING_METHOD_VERSION and info.versions ): next_version = info.versions[-1] outdated_packages.append((info, next_version, False)) version_change = version_change_string(manager, ipkg) package_listing += ' {} {}\n'.format(name, version_change) if not outdated_packages: print('All packages already up-to-date.') return new_pkgs = [] if not args.nodeps: to_validate = [(info.package.qualified_name(), next_version) for info, next_version, _ in outdated_packages] invalid_reason, new_pkgs = manager.validate_dependencies( to_validate, ignore_suggestions=args.nosuggestions) if invalid_reason: print_error('error: failed to resolve dependencies:', invalid_reason) sys.exit(1) allpkgs = outdated_packages + new_pkgs if not args.force: print('The following packages will be UPGRADED:') print(package_listing) if new_pkgs: dependency_listing = '' for info, version, suggestion in new_pkgs: name = info.package.qualified_name() dependency_listing += ' {} ({})'.format(name, version) if suggestion: dependency_listing += ' (suggested)' dependency_listing += '\n' print('The following dependencies will be INSTALLED:') print(dependency_listing) extdep_listing = '' for info, version, _ in allpkgs: name = info.package.qualified_name() extdeps = info.dependencies(field='external_depends') if extdeps is None: extdep_listing += ' from {} ({}):\n \n'.format( name, version) continue if extdeps: extdep_listing += ' from {} ({}):\n'.format(name, version) for extdep, semver in sorted(extdeps.items()): extdep_listing += ' {} {}\n'.format(extdep, semver) if extdep_listing: print('Verify the following REQUIRED external dependencies:\n' '(Ensure their installation on all relevant systems before' ' proceeding):') print(extdep_listing) if not confirmation_prompt('Proceed?'): return prompt_for_user_vars(manager, config, configfile, args.force, [info for info, _, _ in allpkgs]) if not args.skiptests: to_test = [(info, next_version) for info, next_version, _ in outdated_packages] for info, version, _ in new_pkgs: to_test.append((info, version)) for info, version in to_test: name = info.package.qualified_name() if 'test_command' not in info.metadata: zeekpkg.LOG.info( 'Skipping unit tests for "%s": no test_command in metadata', name) continue print('Running unit tests for "{}"'.format(name)) error, passed, test_dir = manager.test(name, version) error_msg = '' if error: error_msg = str.format( 'failed to run tests for {}: {}', name, error) elif not passed: clone_dir = os.path.join(os.path.join(test_dir, "clones"), info.package.name) error_msg = str.format('"{}" tests failed, inspect contents of' ' {} for details, especially any' ' "zkg.test_command.{{stderr,stdout}}"' ' files within {}', name, test_dir, clone_dir) if error_msg: print_error('error: {}'.format(error_msg)) if args.force: continue if not confirmation_prompt('Proceed to install anyway?', default_to_yes=False): return join_timeout = 0.01 tick_interval = 1 for info, version, _ in new_pkgs: name = info.package.qualified_name() time_accumulator = 0 tick_count = 0 worker = InstallWorker(manager, name, version) worker.start() while worker.isAlive(): worker.join(join_timeout) time_accumulator += join_timeout if time_accumulator >= tick_interval: if tick_count == 0: print('Installing "{}"'.format(name), end='') else: print('.', end='') sys.stdout.flush() tick_count += 1 time_accumulator -= tick_interval if tick_count != 0: print('') if worker.error: print('Failed installing "{}": {}'.format(name, worker.error)) continue ipkg = manager.find_installed_package(name) print('Installed "{}" ({})'.format(name, ipkg.status.current_version)) if manager.has_scripts(ipkg): load_error = manager.load(name) if load_error: print('Failed loading "{}": {}'.format(name, load_error)) else: print('Loaded "{}"'.format(name)) had_failure = False for info, next_version, _ in outdated_packages: name = info.package.qualified_name() bdir = name if not manager.match_source_packages(name): name = info.package.git_url bdir = zeekpkg.package.name_from_path(name) ipkg = manager.find_installed_package(name) modifications = manager.modified_config_files(ipkg) backup_files = manager.backup_modified_files(name, modifications) prev_upstream_config_files = manager.save_temporary_config_files(ipkg) res = manager.upgrade(name) if res: print('Failed upgrading "{}": {}'.format(name, res)) had_failure = True else: ipkg = manager.find_installed_package(name) print('Upgraded "{}" ({})'.format( name, ipkg.status.current_version)) for i, mf in enumerate(modifications): next_upstream_config_file = mf[1] if not os.path.isfile(next_upstream_config_file): print("\tConfig file no longer exists:") print("\t\t" + next_upstream_config_file) print("\tPrevious, locally modified version backed up to:") print("\t\t" + backup_files[i]) continue prev_upstream_config_file = prev_upstream_config_files[i][1] if filecmp.cmp(prev_upstream_config_file, next_upstream_config_file): # Safe to restore user's version shutil.copy2(backup_files[i], next_upstream_config_file) continue print("\tConfig file has been updated to a newer version:") print("\t\t" + next_upstream_config_file) print("\tPrevious, locally modified version backed up to:") print("\t\t" + backup_files[i]) if had_failure: sys.exit(1) def cmd_load(manager, args, config, configfile): had_failure = False for name in args.package: ipkg = manager.find_installed_package(name) if not ipkg: had_failure = True print('Failed to load "{}": no such package installed'.format(name)) continue if not manager.has_scripts(ipkg): print('The package "{}" does not contain scripts to load.'.format(name)) continue name = ipkg.package.qualified_name() load_error = manager.load(name) if load_error: had_failure = True print('Failed to load "{}": {}'.format(name, load_error)) else: print('Loaded "{}"'.format(name)) if had_failure: sys.exit(1) def cmd_unload(manager, args, config, configfile): had_failure = False for name in args.package: ipkg = manager.find_installed_package(name) if not ipkg: had_failure = True print('Failed to unload "{}": no such package installed'.format(name)) continue if not manager.has_scripts(ipkg): print('The package "{}" does not contain scripts to unload.'.format(name)) continue name = ipkg.package.qualified_name() if manager.unload(name): print('Unloaded "{}"'.format(name)) else: had_failure = True print( 'Failed unloading "{}": no such package installed'.format(name)) if had_failure: sys.exit(1) def cmd_pin(manager, args, config, configfile): had_failure = False for name in args.package: ipkg = manager.find_installed_package(name) if not ipkg: had_failure = True print('Failed to pin "{}": no such package installed'.format(name)) continue name = ipkg.package.qualified_name() ipkg = manager.pin(name) if ipkg: print('Pinned "{}" at version: {} ({})'.format( name, ipkg.status.current_version, ipkg.status.current_hash)) else: had_failure = True print('Failed pinning "{}": no such package installed'.format(name)) if had_failure: sys.exit(1) def cmd_unpin(manager, args, config, configfile): had_failure = False for name in args.package: ipkg = manager.find_installed_package(name) if not ipkg: had_failure = True print('Failed to unpin "{}": no such package installed'.format(name)) continue name = ipkg.package.qualified_name() ipkg = manager.unpin(name) if ipkg: print('Unpinned "{}" from version: {} ({})'.format( name, ipkg.status.current_version, ipkg.status.current_hash)) else: had_failure = True print( 'Failed unpinning "{}": no such package installed'.format(name)) if had_failure: sys.exit(1) def _get_filtered_packages(manager, category): pkg_dict = dict() for ipkg in manager.installed_packages(): pkg_dict[ipkg.package.qualified_name()] = ipkg for pkg in manager.source_packages(): pkg_qn = pkg.qualified_name() if pkg_qn not in pkg_dict: pkg_dict[pkg_qn] = pkg if category == 'all': filtered_pkgs = pkg_dict elif category == 'installed': filtered_pkgs = {key: value for key, value in pkg_dict.items() if isinstance(value, zeekpkg.InstalledPackage)} elif category == 'not_installed': filtered_pkgs = {key: value for key, value in pkg_dict.items() if not isinstance(value, zeekpkg.InstalledPackage)} elif category == 'loaded': filtered_pkgs = {key: value for key, value in pkg_dict.items() if isinstance(value, zeekpkg.InstalledPackage) and value.status.is_loaded} elif category == 'unloaded': filtered_pkgs = {key: value for key, value in pkg_dict.items() if isinstance(value, zeekpkg.InstalledPackage) and not value.status.is_loaded} elif category == 'outdated': filtered_pkgs = {key: value for key, value in pkg_dict.items() if isinstance(value, zeekpkg.InstalledPackage) and value.status.is_outdated} else: raise NotImplementedError return filtered_pkgs def cmd_list(manager, args, config, configfile): filtered_pkgs = _get_filtered_packages(manager, args.category) for pkg_name, val in sorted(filtered_pkgs.items()): if isinstance(val, zeekpkg.InstalledPackage): pkg = val.package out = '{} (installed: {})'.format( pkg_name, val.status.current_version) else: pkg = val out = pkg_name if not args.nodesc: desc = pkg.short_description() if desc: out += ' - ' + desc print(out) def cmd_search(manager, args, config, configfile): src_pkgs = manager.source_packages() matches = set() for search_text in args.search_text: if search_text[0] == '/' and search_text[-1] == '/': import re try: regex = re.compile(search_text[1:-1]) except re.error as error: print('invalid regex: {}'.format(error)) sys.exit(1) else: for pkg in src_pkgs: if regex.search(pkg.name_with_source_directory()): matches.add(pkg) for tag in pkg.tags(): if regex.search(tag): matches.add(pkg) else: for pkg in src_pkgs: if search_text in pkg.name_with_source_directory(): matches.add(pkg) for tag in pkg.tags(): if search_text in tag: matches.add(pkg) if matches: for match in sorted(matches): out = match.qualified_name() ipkg = manager.find_installed_package(match.qualified_name()) if ipkg: out += ' (installed: {})'.format(ipkg.status.current_version) desc = match.short_description() if desc: out += ' - ' + desc print(out) else: print("no matches") def cmd_info(manager, args, config, configfile): if args.version and len(args.package) > 1: print_error( 'error: "info --version" may only be used for a single package') sys.exit(1) # Dictionary for storing package info to output as JSON pkginfo = dict() had_invalid_package = False if len(args.package) == 1: try: filtered_pkgs = _get_filtered_packages(manager, args.package[0]) package_names = [pkg_name for pkg_name, _ in sorted(filtered_pkgs.items())] except NotImplementedError: package_names = args.package else: package_names = args.package for name in package_names: info = manager.info(name, version=args.version, prefer_installed=(args.nolocal != True)) if info.package: name = info.package.qualified_name() if args.json: pkginfo[name] = dict() pkginfo[name]['metadata'] = dict() else: print('"{}" info:'.format(name)) if info.invalid_reason: if args.json: pkginfo[name]['invalid'] = info.invalid_reason else: print('\tinvalid package: {}\n'.format(info.invalid_reason)) had_invalid_package = True continue if args.json: pkginfo[name]['url'] = info.package.git_url pkginfo[name]['versions'] = info.versions else: print('\turl: {}'.format(info.package.git_url)) print('\tversions: {}'.format(info.versions)) if info.status: if args.json: pkginfo[name]['install_status'] = dict() for key, value in sorted(info.status.__dict__.items()): pkginfo[name]['install_status'][key] = value else: print('\tinstall status:') for key, value in sorted(info.status.__dict__.items()): print('\t\t{} = {}'.format(key, value)) if args.json: if info.metadata_file: pkginfo[name]['metadata_file'] = info.metadata_file pkginfo[name]['metadata'][info.metadata_version] = dict() else: if info.metadata_file: print('\tmetadata file: {}'.format(info.metadata_file)) print('\tmetadata (from version "{}"):'.format( info.metadata_version)) if len(info.metadata) == 0: if args.json != True: print('\t\t') else: if args.json: _fill_metadata_version( pkginfo[name]['metadata'][info.metadata_version], info.metadata) else: for key, value in sorted(info.metadata.items()): value = value.replace('\n', '\n\t\t\t') print('\t\t{} = {}'.format(key, value)) # If --json and --allvers given, check for multiple versions and # add the metadata for each version to the pkginfo. if args.json and args.allvers: for vers in info.versions: # Skip the version that was already processed if vers != info.metadata_version: info2 = manager.info(name, vers, prefer_installed=(args.nolocal != True)) pkginfo[name]['metadata'][info2.metadata_version] = dict() if info2.metadata_file: pkginfo[name]['metadata_file'] = info2.metadata_file _fill_metadata_version( pkginfo[name]['metadata'][info2.metadata_version], info2.metadata) if not args.json: print() if args.json: print(json.dumps(pkginfo, indent=args.jsonpretty, sort_keys=True)) if had_invalid_package: sys.exit(1) def _fill_metadata_version(pkginfo_name_metadata_version, info_metadata): """ Fill a dict with metadata information. This helper function is called by cmd_info to fill metadata information for a specific package version. Args: pkginfo_name_metadata_version (dict of str -> dict): Corresponds to pkginfo[name]['metadata'][info.metadata_version] in cmd_info. info_metadata (dict of str->str): Corresponds to info.metadata in cmd_info. Side effect: New dict entries are added to pkginfo_name_metadata_version. """ for key, value in info_metadata.items(): if key == 'depends' or key == 'suggests': pkginfo_name_metadata_version[key] = dict() deps = value.split('\n') for i in range(1, len(deps)): deplist = deps[i].split(' ') pkginfo_name_metadata_version[key][deplist[0]] =\ deplist[1] else: pkginfo_name_metadata_version[key] = value def cmd_config(manager, args, config, configfile): if args.config_param == 'all': if sys.version_info[0] < 3: from StringIO import StringIO else: from io import StringIO out = StringIO() config.write(out) print(out.getvalue()) out.close() elif args.config_param == 'sources': for key, value in config_items(config, 'sources'): print('{} = {}'.format(key, value)) elif args.config_param == 'user_vars': if config.has_section('user_vars'): for key, value in config_items(config, 'user_vars'): print('{} = {}'.format(key, value)) else: print(config.get('paths', args.config_param)) def cmd_autoconfig(manager, args, config, configfile): zeek_config = find_program('zeek-config') if zeek_config: have_zeek_config = True else: have_zeek_config = False zeek_config = find_program('bro-config') if not zeek_config: print_error('error: no "zeek-config" or "bro-config" not found in PATH') sys.exit(1) dist_option = '--zeek_dist' if have_zeek_config else '--bro_dist' cmd = subprocess.Popen([zeek_config, '--site_dir', '--plugin_dir', dist_option], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) script_dir = read_zeek_config_line(cmd.stdout) plugin_dir = read_zeek_config_line(cmd.stdout) zeek_dist = read_zeek_config_line(cmd.stdout) if configfile: config_dir = os.path.dirname(configfile) else: config_dir = default_config_dir() configfile = os.path.join(config_dir, 'config') make_dir(config_dir) config_file_exists = os.path.isfile(configfile) def change_config_value(config, section, option, new_value, use_prompt): if not use_prompt: config.set(section, option, new_value) return old_value = config.get(section, option) if old_value == new_value: return prompt = u'Set "{}" config option to: {} ?'.format(option, new_value) if old_value: prompt += u'\n(previous value: {})'.format(old_value) if confirmation_prompt(prompt): config.set(section, option, new_value) change_config_value(config, 'paths', 'script_dir', script_dir, config_file_exists) change_config_value(config, 'paths', 'plugin_dir', plugin_dir, config_file_exists) change_config_value(config, 'paths', 'zeek_dist', zeek_dist, config_file_exists) with io.open(configfile, 'w', encoding=std_encoding(sys.stdout)) as f: config.write(f) print('Successfully wrote config file to {}'.format(configfile)) def cmd_env(manager, args, config, configfile): zeek_config = find_program('zeek-config') path_option = '--zeekpath' if not zeek_config: path_option = '--bropath' zeek_config = find_program('bro-config') zeekpath = os.environ.get('ZEEKPATH') if not zeekpath: zeekpath = os.environ.get('BROPATH') pluginpath = os.environ.get('ZEEK_PLUGIN_PATH') if not pluginpath: pluginpath = os.environ.get('BRO_PLUGIN_PATH') if zeek_config: cmd = subprocess.Popen([zeek_config, path_option, '--plugin_dir'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True) line1 = read_zeek_config_line(cmd.stdout) line2 = read_zeek_config_line(cmd.stdout) if not zeekpath: zeekpath = line1 if not pluginpath: pluginpath = line2 zeekpaths = [p for p in zeekpath.split(':')] if zeekpath else [] pluginpaths = [p for p in pluginpath.split(':')] if pluginpath else [] zeekpaths.append(manager.zeekpath()) pluginpaths.append(manager.zeek_plugin_path()) if os.environ['SHELL'].endswith('csh'): print(u'setenv BROPATH {}'.format(':'.join(zeekpaths))) print(u'setenv BRO_PLUGIN_PATH {}'.format(':'.join(pluginpaths))) print(u'setenv ZEEKPATH {}'.format(':'.join(zeekpaths))) print(u'setenv ZEEK_PLUGIN_PATH {}'.format(':'.join(pluginpaths))) else: print(u'export BROPATH={}'.format(':'.join(zeekpaths))) print(u'export BRO_PLUGIN_PATH={}'.format(':'.join(pluginpaths))) print(u'export ZEEKPATH={}'.format(':'.join(zeekpaths))) print(u'export ZEEK_PLUGIN_PATH={}'.format(':'.join(pluginpaths))) class BundleHelpFormatter(argparse.ArgumentDefaultsHelpFormatter): # Workaround for underlying argparse bug: https://bugs.python.org/issue9338 def _format_args(self, action, default_metavar): rval = super(BundleHelpFormatter, self)._format_args( action, default_metavar) if action.nargs == argparse.ZERO_OR_MORE: rval += " --" elif action.nargs == argparse.ONE_OR_MORE: rval += " --" return rval def top_level_parser(): top_parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description='A command-line package manager for Zeek.', epilog='Environment Variables:\n\n' ' ``ZKG_CONFIG_FILE``:\t' 'Same as ``--configfile`` option, but has less precedence.' ) top_parser.add_argument('--version', action='version', version='%(prog)s ' + zeekpkg.__version__) top_parser.add_argument('--configfile', help='Path to Zeek Package Manager config file.') top_parser.add_argument('--verbose', '-v', action='count', default=0, help='Increase program output for debugging.' ' Use multiple times for more output (e.g. -vvv).') return top_parser def argparser(): pkg_name_help = 'The name(s) of package(s) to operate on. The package' \ ' may be named in several ways. If the package is part' \ ' of a package source, it may be referred to by the' \ ' base name of the package (last component of git URL)' \ ' or its path within the package source.' \ ' If two packages in different package sources' \ ' have conflicting paths, then the package source' \ ' name may be prepended to the package path to resolve' \ ' the ambiguity. A full git URL may also be used to refer' \ ' to a package that does not belong to a source. E.g. for' \ ' a package source called "zeek" that has a package named' \ ' "foo" located in either "alice/zkg.index" or' \ ' "alice/bro-pkg.index", the following' \ ' names work: "foo", "alice/foo", "zeek/alice/foo".' top_parser = top_level_parser() command_parser = top_parser.add_subparsers( title='commands', dest='command', help='See `%(prog)s -h` for per-command usage info.') command_parser.required = True # test sub_parser = command_parser.add_parser( 'test', help='Runs unit tests for Zeek packages.', description='Runs the unit tests for the specified Zeek packages.' ' In most cases, the "zeek" and "zeek-config" programs will' ' need to be in PATH before running this command.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_test) sub_parser.add_argument( 'package', nargs='+', help=pkg_name_help) sub_parser.add_argument( '--version', default=None, help='The version of the package to test. Only one package may be' ' specified at a time when using this flag. A version tag, branch' ' name, or commit hash may be specified here.' ' If the package name refers to a local git repo with a working tree,' ' then its currently active branch is used.' ' The default for other cases is to use' ' the latest version tag, or if a package has none, the "master"' ' branch.') # install sub_parser = command_parser.add_parser( 'install', help='Installs Zeek packages.', description='Installs packages from a configured package source or' ' directly from a git URL. After installing, the package' ' is marked as being "loaded" (see the ``load`` command).', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_install) sub_parser.add_argument( 'package', nargs='+', help=pkg_name_help) sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') sub_parser.add_argument( '--skiptests', action='store_true', help='Skip running unit tests for packages before installation.') sub_parser.add_argument( '--nodeps', action='store_true', help='Skip all dependency resolution/checks. Note that using this' ' option risks putting your installed package collection into a' ' broken or unusable state.') sub_parser.add_argument( '--nosuggestions', action='store_true', help='Skip automatically installing suggested packages.') sub_parser.add_argument( '--version', default=None, help='The version of the package to install. Only one package may be' ' specified at a time when using this flag. A version tag, branch' ' name, or commit hash may be specified here.' ' If the package name refers to a local git repo with a working tree,' ' then its currently active branch is used.' ' The default for other cases is to use' ' the latest version tag, or if a package has none, the "master"' ' branch.') # bundle sub_parser = command_parser.add_parser( 'bundle', help='Creates a bundle file containing a collection of Zeek packages.', description='This command creates a bundle file containing a collection' ' of Zeek packages. If ``--manifest`` is used, the user' ' supplies the list of packages to put in the bundle, else' ' all currently installed packages are put in the bundle.' ' A bundle file can be unpacked on any target system,' ' resulting in a repeatable/specific set of packages' ' being installed on that target system (see the' ' ``unbundle`` command). This command may be useful for' ' those that want to manage packages on a system that' ' otherwise has limited network connectivity. E.g. one can' ' use a system with an internet connection to create a' ' bundle, transport that bundle to the target machine' ' using whatever means are appropriate, and finally' ' unbundle/install it on the target machine.', formatter_class=BundleHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_bundle) sub_parser.add_argument( 'bundle_filename', metavar='filename.bundle', help='The path of the bundle file to create. It will be overwritten' ' if it already exists. Note that if --manifest is used before' ' this filename is specified, you should use a double-dash, --,' ' to first terminate that argument list.') sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') sub_parser.add_argument( '--nodeps', action='store_true', help='Skip all dependency resolution/checks. Note that using this' ' option risks creating a bundle of packages that is in a' ' broken or unusable state.') sub_parser.add_argument( '--nosuggestions', action='store_true', help='Skip automatically bundling suggested packages.') sub_parser.add_argument( '--manifest', nargs='+', help='This may either be a file name or a list of packages to include' ' in the bundle. If a file name is supplied, it should be in INI' ' format with a single ``[bundle]`` section. The keys in that section' ' correspond to package names and their values correspond to git' ' version tags, branch names, or commit hashes. The values may be' ' left blank to indicate that the latest available version should be' ' used.') # unbundle sub_parser = command_parser.add_parser( 'unbundle', help='Unpacks Zeek packages from a bundle file and installs them.', description='This command unpacks a bundle file formerly created by the' ' ``bundle`` command and installs all the packages' ' contained within.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_unbundle) sub_parser.add_argument( 'bundle_filename', metavar='filename.bundle', help='The path of the bundle file to install.') sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') sub_parser.add_argument( '--replace', action='store_true', help='Using this flag first removes all installed packages before then' ' installing the packages from the bundle.') # remove sub_parser = command_parser.add_parser( 'remove', help='Uninstall a package.', description='Unloads (see the ``unload`` command) and uninstalls a' ' previously installed package.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_remove) sub_parser.add_argument('package', nargs='+', help=pkg_name_help) sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') # purge sub_parser = command_parser.add_parser( 'purge', help='Uninstall all packages.', description='Unloads (see the ``unload`` command) and uninstalls all' ' previously installed packages.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_purge) sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') # refresh sub_parser = command_parser.add_parser( 'refresh', help='Retrieve updated package metadata.', description='Retrieve latest package metadata from sources and checks' ' whether any installed packages have available upgrades.' ' Note that this does not actually upgrade any packages (see the' ' ``upgrade`` command for that).', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_refresh) sub_parser.add_argument( '--aggregate', action='store_true', help='Crawls the urls listed in package source zkg.index' ' (or legacy bro-pkg.index) files and' ' aggregates the metadata found in their zkg.meta (or legacy' ' bro-pkg.meta) files. The aggregated metadata is stored in the local' ' clone of the package' ' source that zkg uses internally locating package metadata.' ' For each package, the metadata is taken from the highest available' ' git version tag or the master branch if no version tags exist') sub_parser.add_argument( '--push', action='store_true', help='Push all local changes to package sources to upstream repos') sub_parser.add_argument('--sources', nargs='+', help='A list of package source names to operate on. If this argument' ' is not used, then the command will operate on all configured' ' sources.') # upgrade sub_parser = command_parser.add_parser( 'upgrade', help='Upgrade installed packages to latest versions.', description='Uprades the specified package(s) to latest available' ' version. If no specific packages are specified, then all installed' ' packages that are outdated and not pinned are upgraded. For packages' ' that are installed with ``--version`` using a git branch name, the' ' package is updated to the latest commit on that branch, else the' ' package is updated to the highest available git version tag.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_upgrade) sub_parser.add_argument( 'package', nargs='*', default=[], help=pkg_name_help) sub_parser.add_argument( '--force', action='store_true', help='Skip the confirmation prompt.') sub_parser.add_argument( '--skiptests', action='store_true', help='Skip running unit tests for packages before installation.') sub_parser.add_argument( '--nodeps', action='store_true', help='Skip all dependency resolution/checks. Note that using this' ' option risks putting your installed package collection into a' ' broken or unusable state.') sub_parser.add_argument( '--nosuggestions', action='store_true', help='Skip automatically installing suggested packages.') # load sub_parser = command_parser.add_parser( 'load', help='Register packages to be be auto-loaded by Zeek.', description='The Zeek Package Manager keeps track of all packages that' ' are marked as "loaded" and maintains a single Zeek script that, when' ' loaded by Zeek (e.g. via ``@load packages``), will load the scripts' ' from all "loaded" packages at once.' ' This command adds a set of packages to the "loaded packages" list.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_load) sub_parser.add_argument( 'package', nargs='+', default=[], help='Name(s) of package(s) to load.') # unload sub_parser = command_parser.add_parser( 'unload', help='Unregister packages to be be auto-loaded by Zeek.', description='The Zeek Package Manager keeps track of all packages that' ' are marked as "loaded" and maintains a single Zeek script that, when' ' loaded by Zeek, will load the scripts from all "loaded" packages at' ' once. This command removes a set of packages from the "loaded' ' packages" list.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_unload) sub_parser.add_argument( 'package', nargs='+', default=[], help=pkg_name_help) # pin sub_parser = command_parser.add_parser( 'pin', help='Prevent packages from being automatically upgraded.', description='Pinned packages are ignored by the ``upgrade`` command.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_pin) sub_parser.add_argument( 'package', nargs='+', default=[], help=pkg_name_help) # unpin sub_parser = command_parser.add_parser( 'unpin', help='Allows packages to be automatically upgraded.', description='Packages that are not pinned are automatically upgraded' ' by the ``upgrade`` command', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_unpin) sub_parser.add_argument( 'package', nargs='+', default=[], help=pkg_name_help) # list sub_parser = command_parser.add_parser( 'list', help='Lists packages.', description='Outputs a list of packages that match a given category.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_list) sub_parser.add_argument('category', nargs='?', default='installed', choices=['all', 'installed', 'not_installed', 'loaded', 'unloaded', 'outdated'], help='Package category used to filter listing.') sub_parser.add_argument( '--nodesc', action='store_true', help='Do not display description text, just the package name(s).') # search sub_parser = command_parser.add_parser( 'search', help='Search packages for matching names.', description='Perform a substring search on package names and metadata' ' tags. Surround search text with slashes to indicate it is a regular' ' expression (e.g. ``/text/``).', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_search) sub_parser.add_argument( 'search_text', nargs='+', default=[], help='The text(s) or pattern(s) to look for.') # info sub_parser = command_parser.add_parser( 'info', help='Display package information.', description='Shows detailed information/metadata for given packages.' ' If the package is currently installed, additional information about' ' the status of it is displayed. E.g. the installed version or whether' ' it is currently marked as "pinned" or "loaded."', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_info) sub_parser.add_argument( 'package', nargs='+', default=[], help=pkg_name_help + ' If a single name is given and matches one of the same categories' ' as the "list" command, then it is automatically expanded to be the' ' names of all packages which match the given category.') sub_parser.add_argument( '--version', default=None, help='The version of the package metadata to inspect. A version tag,' ' branch name, or commit hash and only one package at a time may be' ' given when using this flag. If unspecified, the behavior depends' ' on whether the package is currently installed. If installed,' ' the metadata will be pulled from the installed version. If not' ' installed, the latest version tag is used, or if a package has no' ' version tags, the "master" branch is used.') sub_parser.add_argument( '--nolocal', action='store_true', help='Do not read information from locally installed packages.' ' Instead read info from remote GitHub.') sub_parser.add_argument( '--json', action='store_true', help='Output package information as JSON.') sub_parser.add_argument( '--jsonpretty', type=int, default=None, metavar='SPACES', help='Optional number of spaces to indent for pretty-printed' ' JSON output.') sub_parser.add_argument( '--allvers', action='store_true', help='When outputting package information as JSON, show metadata for' ' all versions. This option can be slow since remote repositories' ' may be cloned multiple times. Also, installed packages will show' ' metadata only for the installed version unless the --nolocal ' ' option is given.') # config sub_parser = command_parser.add_parser( 'config', help='Show Zeek Package Manager configuration info.', description='The default output of this command is a valid package' ' manager config file that corresponds to the one currently being used,' ' but also with any defaulted field values filled in. This command' ' also allows for only the value of a specific field to be output if' ' the name of that field is given as an argument to the command.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_config) sub_parser.add_argument( 'config_param', nargs='?', default='all', choices=['all', 'sources', 'user_vars', 'state_dir', 'script_dir', 'plugin_dir', 'zeek_dist', 'bro_dist'], help='Name of a specific config file field to output.') # autoconfig sub_parser = command_parser.add_parser( 'autoconfig', help='Generate a Zeek Package Manager configuration file.', description='The output of this command is a valid package manager' ' config file that is generated by using the ``zeek-config`` script' ' that is installed along with Zeek. It is the suggested configuration' ' to use for most Zeek installations. For this command to work, the' ' ``zeek-config`` (or ``bro-config``) script must be in ``PATH``.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_autoconfig) # env sub_parser = command_parser.add_parser( 'env', help='Show the value of environment variables that need to be set for' ' Zeek to be able to use installed packages.', description='This command returns shell commands that, when executed,' ' will correctly set ``ZEEKPATH`` and ``ZEEK_PLUGIN_PATH`` (also' ' ``BROPATH`` and ``BRO_PLUGIN_PATH`` for legacy compatibility) to use' ' scripts and plugins from packages installed by the package manager.' ' For this command to function properly, either have the ``zeek-config``' ' script (installed by zeek) in ``PATH``, or have the ``ZEEKPATH`` and' ' ``ZEEK_PLUGIN_PATH`` (or ``BROPATH`` and ``BRO_PLUGIN_PATH``)' ' environment variables already set so this command' ' can append package-specific paths to them.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) sub_parser.set_defaults(run_cmd=cmd_env) return top_parser def main(): args = argparser().parse_args() if args.verbose > 0: formatter = logging.Formatter( '%(asctime)s %(levelname)-8s %(message)s', '%Y-%m-%d %H:%M:%S') handler = logging.StreamHandler() handler.setFormatter(formatter) if args.verbose == 1: zeekpkg.LOG.setLevel(logging.WARNING) elif args.verbose == 2: zeekpkg.LOG.setLevel(logging.INFO) elif args.verbose == 3: zeekpkg.LOG.setLevel(logging.DEBUG) zeekpkg.LOG.addHandler(handler) configfile = args.configfile if not configfile: configfile = find_configfile() config = create_config(configfile) manager = create_manager(config) args.run_cmd(manager, args, config, configfile) if __name__ == '__main__': main() sys.exit(0) package-manager-2.0.7/testing/0000755000175000017500000000000013551142424015547 5ustar rhaistrhaistpackage-manager-2.0.7/testing/tests/0000755000175000017500000000000013551142424016711 5ustar rhaistrhaistpackage-manager-2.0.7/testing/tests/plugin0000644000175000017500000000171713551142424020140 0ustar rhaistrhaist# @TEST-REQUIRES: type zeek-config # @TEST-REQUIRES: test -n "$(zeek-config --zeek_dist)" # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install rot13 # @TEST-EXEC: btest-diff plugins/packages/rot13/__bro_plugin__ # @TEST-EXEC: btest-diff scripts/packages/rot13/__load__.zeek # Unloading the package should also disable the plugin, which we # detect via the renamed __bro_plugin__ magic file. # @TEST-EXEC: zkg unload rot13 # @TEST-EXEC: test ! -f plugins/packages/rot13/__bro_plugin__ # @TEST-EXEC: btest-diff plugins/packages/rot13/__bro_plugin__.disabled # (Re-)loading the package should also (re-)enable the plugin. # @TEST-EXEC: zkg load rot13 # @TEST-EXEC: test ! -f plugins/packages/rot13/__bro_plugin__.disabled # @TEST-EXEC: cp plugins/packages/rot13/__bro_plugin__ __bro_plugin__.after_enabling # @TEST-EXEC: btest-diff __bro_plugin__.after_enabling echo "$(pwd)/packages/rot13" >> sources/one/bob/zkg.index cd sources/one git commit -am 'add rot13 package' package-manager-2.0.7/testing/tests/metadata-depends0000644000175000017500000000256413551142424022043 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo # @TEST-EXEC: zkg list installed > no_depends.out # @TEST-EXEC: btest-diff no_depends.out # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg upgrade foo # @TEST-EXEC: zkg list installed > upgrade_depends.out # @TEST-EXEC: btest-diff upgrade_depends.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg install foo # @TEST-EXEC: zkg list installed > install_depends.out # @TEST-EXEC: btest-diff install_depends.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg bundle test.bundle --manifest foo # @TEST-EXEC: zkg unbundle test.bundle # @TEST-EXEC: zkg list installed > bundle_depends.out # @TEST-EXEC: btest-diff bundle_depends.out cd packages/foo echo 'depends = bar *' >> zkg.meta git commit -am 'new stuff' cd ../bar echo 'depends = baz >=1.0.0' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 cd ../baz echo 'depends = grault branch=master' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "2.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 2.0.0 -m 2.0.0 cd ../grault echo 'depends = corge ==1.0.1' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 cd ../corge git tag -a 1.0.0 -m 1.0.0 echo 'depends = foo * bar *' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "2.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 2.0.0 -m 2.0.0 package-manager-2.0.7/testing/tests/metadata-script_dir0000644000175000017500000000036013551142424022553 0ustar rhaistrhaist# @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install foo # @TEST-EXEC: btest-diff scripts/foo/__load__.zeek cd packages/foo mkdir scripts echo 'script_dir = scripts' >> zkg.meta mv __load__.zeek scripts/ git add * git commit -m 'new stuff' package-manager-2.0.7/testing/tests/package-submodule0000644000175000017500000000127613551142424022232 0ustar rhaistrhaist# @TEST-EXEC: bash setup_foo # @TEST-EXEC: zkg install foo # @TEST-EXEC: cp scripts/packages/foo/__load__.zeek foo.install # @TEST-EXEC: btest-diff foo.install # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg upgrade # @TEST-EXEC: cp scripts/packages/foo/__load__.zeek foo.upgrade # @TEST-EXEC: btest-diff foo.upgrade @TEST-START-FILE setup_foo cd packages/foo echo 'script_dir = scripts' >> zkg.meta git submodule add $(pwd)/../bar scripts git commit -am 'add submodule' @TEST-END-FILE cd packages/bar echo 'event zeek_init() { print "bar upgrade is loaded"; }' > __load__.zeek git commit -am 'new stuff' cd ../foo/scripts git pull cd .. git commit -am 'update submodule' package-manager-2.0.7/testing/tests/user_vars0000644000175000017500000000076313551142424020653 0ustar rhaistrhaist# @TEST-EXEC: bash %INPUT # @TEST-EXEC: LAST_VAR=/home/jon/sandbox zkg install foo # @TEST-EXEC: btest-diff state/logs/foo-build.log cd packages/foo echo 'user_vars =' >> zkg.meta echo ' TEST_VAR [/usr] "First description is here"' >> zkg.meta echo ' ANOTHER_VAR [/usr/local] "Second description is here"' >> zkg.meta echo ' LAST_VAR [/opt] "Last description is here"' >> zkg.meta echo 'build_command = echo "%(LAST_VAR)s\n%(ANOTHER_VAR)s\n%(TEST_VAR)s"' >> zkg.meta git commit -am 'new stuff' package-manager-2.0.7/testing/tests/metadata-suggests0000644000175000017500000000260113551142424022255 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo # @TEST-EXEC: zkg list installed > no_suggests.out # @TEST-EXEC: btest-diff no_suggests.out # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg upgrade foo # @TEST-EXEC: zkg list installed > upgrade_suggests.out # @TEST-EXEC: btest-diff upgrade_suggests.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg install foo # @TEST-EXEC: zkg list installed > install_suggests.out # @TEST-EXEC: btest-diff install_suggests.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg bundle test.bundle --manifest foo # @TEST-EXEC: zkg unbundle test.bundle # @TEST-EXEC: zkg list installed > bundle_suggests.out # @TEST-EXEC: btest-diff bundle_suggests.out cd packages/foo echo 'suggests = bar *' >> zkg.meta git commit -am 'new stuff' cd ../bar echo 'suggests = baz >=1.0.0' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 cd ../baz echo 'suggests = grault branch=master' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "2.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 2.0.0 -m 2.0.0 cd ../grault echo 'suggests = corge ==1.0.1' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 cd ../corge git tag -a 1.0.0 -m 1.0.0 echo 'suggests = foo * bar *' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "2.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 2.0.0 -m 2.0.0 package-manager-2.0.7/testing/tests/plugin_tarfile0000644000175000017500000000075613551142424021650 0ustar rhaistrhaist# @TEST-REQUIRES: type zeek-config # @TEST-REQUIRES: test -n "$(zeek-config --zeek_dist)" # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install rot13 # @TEST-EXEC: btest-diff plugins/packages/rot13/__bro_plugin__ # @TEST-EXEC: btest-diff scripts/packages/rot13/__load__.zeek cd packages/rot13 echo "plugin_dir = build/Demo_Rot13.tgz" >> zkg.meta git commit -am 'new stuff' cd ../.. echo "$(pwd)/packages/rot13" >> sources/one/bob/zkg.index cd sources/one git commit -am 'add rot13 package' package-manager-2.0.7/testing/tests/refresh0000644000175000017500000000212213551142424020267 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg list outdated > outdated.out # @TEST-EXEC: btest-diff outdated.out # @TEST-EXEC: zkg list all > list.out # @TEST-EXEC: btest-diff list.out # @TEST-EXEC: zkg refresh --aggregate # @TEST-EXEC: zkg search lucky > search.out # @TEST-EXEC: btest-diff search.out # @TEST-EXEC: zkg list all > list_after_agg.out # @TEST-EXEC: btest-diff list_after_agg.out # note: foo's description isn't in the list output since the installed # version's metadata has no 'description' field cd packages/foo echo 'tags = esoteric lucky land' >> zkg.meta echo 'description = This is the foo package description' >> zkg.meta git commit -am 'new stuff' cd .. mkdir new_pkg cd new_pkg git init echo '[package]' > zkg.meta echo 'tags = esoteric lucky land' >> zkg.meta echo 'description = This is the new_pkg package description' >> zkg.meta echo 'print "hello";' >> __load__.zeek git add * git commit -m 'init' cd ../.. echo "$(pwd)/packages/new_pkg" >> sources/one/alice/zkg.index cd sources/one git commit -am 'add new_pkg' package-manager-2.0.7/testing/tests/upgrade0000644000175000017500000000216613551142424020270 0ustar rhaistrhaist# @TEST-EXEC: bash setup_foo # @TEST-EXEC: zkg install foo bar # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg upgrade # @TEST-EXEC: btest-diff scripts/packages/foo/__load__.zeek # @TEST-EXEC: btest-diff scripts/packages/bar/__load__.zeek @TEST-START-FILE setup_foo cd packages/foo echo 'print "foo 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "foo 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "foo 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 @TEST-END-FILE cd packages/foo echo 'print "foo 1.0.3";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.3 -m 1.0.3 echo 'print "foo 1.0.4";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.4 -m 1.0.4 echo 'print "foo master";' > __load__.zeek git commit -am 'new stuff' cd ../bar echo 'print "bar+ loaded";' > __load__.zeek git commit -am 'new stuff' echo 'print "bar 1.0.0 loaded";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "bar++ loaded";' > __load__.zeek git commit -am 'new stuff' package-manager-2.0.7/testing/tests/list0000644000175000017500000000161413551142424017611 0ustar rhaistrhaist# @TEST-EXEC: zkg list all > all.out # @TEST-EXEC: btest-diff all.out # @TEST-EXEC: zkg install foo bar # @TEST-EXEC: zkg unload bar # @TEST-EXEC: zkg list installed > installed.out # @TEST-EXEC: btest-diff installed.out # @TEST-EXEC: zkg list > list.out # @TEST-EXEC: cmp list.out installed.out # @TEST-EXEC: zkg list loaded > loaded.out # @TEST-EXEC: btest-diff loaded.out # @TEST-EXEC: zkg list unloaded > unloaded.out # @TEST-EXEC: btest-diff unloaded.out # @TEST-EXEC: zkg list not_installed > not_installed.out # @TEST-EXEC: btest-diff not_installed.out # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg list outdated > outdated.out # @TEST-EXEC: btest-diff outdated.out # @TEST-EXEC: zkg upgrade # @TEST-EXEC: zkg list outdated > after_upgrade.out # @TEST-EXEC: btest-diff after_upgrade.out cd packages/foo echo 'print "hello";' >> __load__.zeek git commit -am 'new stuff' package-manager-2.0.7/testing/tests/bundle0000644000175000017500000000256313551142424020113 0ustar rhaistrhaist# @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install foo baz # @TEST-EXEC: zkg install bar --version 1.0.0 # @TEST-EXEC: zkg bundle test.bundle # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg unbundle test.bundle # @TEST-EXEC: zkg list installed > snapshot.out # @TEST-EXEC: btest-diff snapshot.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg bundle test.bundle --manifest foo bar baz # @TEST-EXEC: zkg unbundle test.bundle # @TEST-EXEC: zkg list installed > args.out # @TEST-EXEC: btest-diff args.out # @TEST-EXEC: zkg purge # @TEST-EXEC: zkg bundle test.bundle --manifest manifest.txt # @TEST-EXEC: zkg unbundle test.bundle # @TEST-EXEC: zkg list installed > manifest.out # @TEST-EXEC: btest-diff manifest.out cd packages/foo echo 'print "foo 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "foo 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "foo 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 cd ../bar echo 'print "bar 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "bar 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "bar 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 @TEST-START-FILE manifest.txt [bundle] foo = 1.0.0 bar = 1.0.1 baz = master @TEST-END-FILE package-manager-2.0.7/testing/tests/purge0000644000175000017500000000065113551142424017760 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo bar baz # @TEST-EXEC: test -f scripts/packages/foo/__load__.zeek # @TEST-EXEC: test -f scripts/packages/bar/__load__.zeek # @TEST-EXEC: test -f scripts/packages/baz/__load__.zeek # @TEST-EXEC: zkg purge # @TEST-EXEC: test ! -d scripts/packages/foo # @TEST-EXEC: test ! -d scripts/packages/bar # @TEST-EXEC: test ! -d scripts/packages/baz # @TEST-EXEC: btest-diff scripts/packages/packages.zeek package-manager-2.0.7/testing/tests/test0000644000175000017500000000131113551142424017607 0ustar rhaistrhaist# @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg test rot13 # @TEST-EXEC: bash we_need_to_go_deeper # @TEST-EXEC-FAIL: zkg test rot13 echo "$(pwd)/packages/rot13" >> sources/one/bob/zkg.index cd sources/one git commit -am 'add rot13 package' cd ../../packages/rot13 echo 'depends = bar *' >> zkg.meta echo -e "@load bar\n$(cat scripts/Demo/Rot13/__load__.zeek)" > scripts/Demo/Rot13/__load__.zeek cd testing/Baseline/tests.main echo "rot13 plugin is loaded" > output echo "bar loaded" >> output echo "rot13 script is loaded" >> output git commit -am 'new stuff' @TEST-START-FILE we_need_to_go_deeper cd packages/rot13 echo 'hello' > testing/Baseline/tests.rot13/output git commit -am 'new stuff' @TEST-END-FILE package-manager-2.0.7/testing/tests/install0000644000175000017500000000426413551142424020310 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo # @TEST-EXEC: btest-diff scripts/packages/foo/__load__.zeek # @TEST-EXEC: zkg install alice/bar # @TEST-EXEC: btest-diff scripts/packages/bar/__load__.zeek # @TEST-EXEC: zkg install one/alice/baz # @TEST-EXEC: btest-diff scripts/packages/baz/__load__.zeek # @TEST-EXEC: zkg install $(pwd)/packages/qux # @TEST-EXEC: btest-diff scripts/packages/qux/__load__.zeek # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install corge # @TEST-EXEC: btest-diff scripts/packages/corge/__load__.zeek # @TEST-EXEC: zkg remove corge # @TEST-EXEC: zkg install corge --version=master # @TEST-EXEC: cp scripts/packages/corge/__load__.zeek corge.master # @TEST-EXEC: btest-diff corge.master cd packages/corge git tag -a 1.0.0 -m 1.0.0 echo 'print "hello";' >> __load__.zeek git commit -am 'new stuff' cd ../grault echo 'print "grault 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "grault 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a v1.0.1 -m 1.0.1 echo 'print "grault 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 # @TEST-EXEC: zkg install grault # @TEST-EXEC: btest-diff scripts/packages/grault/__load__.zeek # @TEST-EXEC: zkg remove grault # @TEST-EXEC: zkg install grault --version=v1.0.1 # @TEST-EXEC: cp scripts/packages/grault/__load__.zeek grault.1.0.1 # @TEST-EXEC: btest-diff grault.1.0.1 # # @TEST-EXEC: zkg remove grault # @TEST-EXEC: (cd packages/grault && git checkout --detach) # @TEST-EXEC: zkg install $(pwd)/packages/grault # @TEST-EXEC: cp scripts/packages/grault/__load__.zeek grault.detached # @TEST-EXEC: btest-diff grault.detached # @TEST-EXEC: zkg info grault | grep tracking_method > grault.info # @TEST-EXEC: btest-diff grault.info # # @TEST-EXEC: zkg remove grault # @TEST-EXEC: (cd packages/grault && git checkout 1.0.0) # @TEST-EXEC: (cd packages/grault && git rev-parse HEAD) > grault.head.hash # @TEST-EXEC: zkg install grault --version=$(cat grault.head.hash) # @TEST-EXEC: cp scripts/packages/grault/__load__.zeek grault.detached.head # @TEST-EXEC: btest-diff grault.detached.head # @TEST-EXEC: zkg info grault | grep tracking_method > grault.info # @TEST-EXEC: btest-diff grault.info package-manager-2.0.7/testing/tests/metadata-aliases0000644000175000017500000000045313551142424022035 0ustar rhaistrhaist # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg install foo # @TEST-EXEC: btest-diff scripts/foo/__load__.zeek # @TEST-EXEC: btest-diff scripts/foo2/__load__.zeek # @TEST-EXEC: btest-diff scripts/foo3/__load__.zeek cd packages/foo echo 'aliases = foo foo2 foo3' >> zkg.meta git commit -am 'new stuff' package-manager-2.0.7/testing/tests/remove0000644000175000017500000000034213551142424020130 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo bar baz # @TEST-EXEC: test -f scripts/packages/foo/__load__.zeek # @TEST-EXEC: zkg remove foo # @TEST-EXEC: test ! -d scripts/packages/foo # @TEST-EXEC: btest-diff scripts/packages/packages.zeek package-manager-2.0.7/testing/tests/info0000644000175000017500000000242013551142424017565 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg install bar # @TEST-EXEC: zkg info foo | grep -v 'url:' | grep -v 'current_hash' > foo.info # @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff foo.info # @TEST-EXEC: zkg info bar | grep -v 'url:' | grep -v 'current_hash' > bar.info # @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff bar.info # @TEST-EXEC: zkg remove bar # @TEST-EXEC: zkg info installed | grep -v 'url:' | grep -v 'current_hash' > installed.info # @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff installed.info cd packages/foo echo 'print "foo 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "foo 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "foo 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 cd ../bar echo 'print "bar+ loaded";' > __load__.zeek git commit -am 'new stuff' echo 'print "bar 1.0.0 loaded";' > __load__.zeek echo 'tags = sega sunset' >> zkg.meta echo 'description = bar bar bar' >> zkg.meta git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "bar++ loaded";' > __load__.zeek git commit -am 'new stuff' package-manager-2.0.7/testing/tests/pin0000644000175000017500000000267613551142424017435 0ustar rhaistrhaist# @TEST-EXEC: bash setup_foo # @TEST-EXEC: zkg install foo bar # @TEST-EXEC: zkg pin foo bar # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC-FAIL: zkg upgrade # @TEST-EXEC: btest-diff scripts/packages/foo/__load__.zeek # @TEST-EXEC: btest-diff scripts/packages/bar/__load__.zeek # @TEST-EXEC: zkg unpin foo bar # @TEST-EXEC: zkg upgrade # @TEST-EXEC: cp scripts/packages/foo/__load__.zeek foo_after_unpin.out # @TEST-EXEC: cp scripts/packages/bar/__load__.zeek bar_after_unpin.out # @TEST-EXEC: btest-diff foo_after_unpin.out # @TEST-EXEC: btest-diff bar_after_unpin.out @TEST-START-FILE setup_foo cd packages/foo echo 'print "foo 1.0.0";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "foo 1.0.1";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.1 -m 1.0.1 echo 'print "foo 1.0.2";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.2 -m 1.0.2 @TEST-END-FILE cd packages/foo echo 'print "foo 1.0.3";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.3 -m 1.0.3 echo 'print "foo 1.0.4";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.4 -m 1.0.4 echo 'print "foo master";' > __load__.zeek git commit -am 'new stuff' cd ../bar echo 'print "bar+ loaded";' > __load__.zeek git commit -am 'new stuff' echo 'print "bar 1.0.0 loaded";' > __load__.zeek git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 echo 'print "bar++ loaded";' > __load__.zeek git commit -am 'new stuff' package-manager-2.0.7/testing/tests/metadata-config_files0000644000175000017500000000233213551142424023041 0ustar rhaistrhaist# @TEST-EXEC: bash setup # @TEST-EXEC: zkg install foo bar # @TEST-EXEC: bash %INPUT # @TEST-EXEC: zkg refresh # @TEST-EXEC: zkg upgrade foo # @TEST-EXEC: btest-diff scripts/foo/config.zeek # @TEST-EXEC: btest-diff scripts/bar/config.zeek # @TEST-EXEC: cp state/backups/one/alice/foo/config.zeek* foo_backup.zeek # @TEST-EXEC: btest-diff foo_backup.zeek # @TEST-EXEC: zkg install bar --version=2.0.0 # @TEST-EXEC: cp scripts/bar/config.zeek barconfig2.zeek # @TEST-EXEC: btest-diff barconfig2.zeek # @TEST-EXEC: cp state/backups/one/alice/bar/config.zeek* bar_backup.zeek # @TEST-EXEC: btest-diff bar_backup.zeek @TEST-START-FILE setup cd packages/foo echo 'print "foo orig";' > config.zeek echo 'config_files = config.zeek' >> zkg.meta git add * git commit -am 'new stuff' cd ../bar echo 'print "bar 1.0.0";' > config.zeek echo 'config_files = config.zeek' >> zkg.meta git add * git commit -am 'new stuff' git tag -a 1.0.0 -m 1.0.0 @TEST-END-FILE echo 'print "foo mod";' > scripts/foo/config.zeek echo 'print "bar mod";' > scripts/bar/config.zeek cd packages/foo echo 'print "foo new";' > config.zeek git commit -am 'new stuff' cd ../bar echo 'print "bar 2.0.0";' > config.zeek git commit -am 'new stuff' git tag -a 2.0.0 -m 2.0.0 package-manager-2.0.7/testing/tests/load0000644000175000017500000000054613551142424017560 0ustar rhaistrhaist# @TEST-EXEC: zkg install foo bar # @TEST-EXEC: btest-diff scripts/packages/packages.zeek # @TEST-EXEC: zkg unload foo # @TEST-EXEC: cp scripts/packages/packages.zeek after_unload.out # @TEST-EXEC: btest-diff after_unload.out # @TEST-EXEC: zkg load foo # @TEST-EXEC: cp scripts/packages/packages.zeek after_load.out # @TEST-EXEC: btest-diff after_load.out package-manager-2.0.7/testing/.gitignore0000644000175000017500000000004013551142424017531 0ustar rhaistrhaistdiag.log .btest.failed.dat .tmp package-manager-2.0.7/testing/scripts/0000755000175000017500000000000013551142424017236 5ustar rhaistrhaistpackage-manager-2.0.7/testing/scripts/initializer0000755000175000017500000000107013551142424021505 0ustar rhaistrhaist#! /usr/bin/env bash cp -R $PACKAGES . for p in packages/*; do ( cd $p && git init && git add * && git commit -m 'init' ) done cp -R $SOURCES . find sources -name 'zkg.index' -exec sed -i -e "s#^#$(pwd)/packages/#" {} \; for s in sources/*; do ( cd $s && git init && git add * && git commit -m 'init' ) done echo "\ [sources] one = $(pwd)/sources/one [paths] state_dir = $(pwd)/state script_dir = $(pwd)/scripts plugin_dir = $(pwd)/plugins " >> config type zeek-config > /dev/null 2>&1 && echo "zeek_dist = $(zeek-config --zeek_dist)" >> config || true package-manager-2.0.7/testing/scripts/zkg0000755000175000017500000000055213551142424017761 0ustar rhaistrhaist#! /usr/bin/env bash force="" case "$1" in install) force="--force" ;; upgrade) force="--force" ;; bundle) force="--force" ;; unbundle) force="--force" ;; remove) force="--force" ;; purge) force="--force" ;; esac $TEST_BASE/../zkg --configfile=config "$@" $force package-manager-2.0.7/testing/scripts/diff-remove-abspath0000755000175000017500000000033113551142424023004 0ustar rhaistrhaist#! /usr/bin/env bash # # Replace absolute paths with the basename. if [ `uname` == "Linux" ]; then sed="sed -r" else sed="sed -E" fi $sed 's#/+#/#g' | \ sed 's#/\([^/]\{1,\}/\)\{1,\}\([^/]\{1,\}\)#<...>/\2#g' package-manager-2.0.7/testing/Makefile0000644000175000017500000000060713551142424017212 0ustar rhaistrhaist.PHONY: all all: btest-installation-check @btest -j -f diag.log .PHONY: btest-installation-check btest-installation-check: @type btest > /dev/null 2>&1 || \ { \ echo "btest was not located in PATH."; \ echo "Install it and/or make sure it is in PATH."; \ echo "E.g. you could use the following command to install it:"; \ echo "\tpip install btest"; \ echo ; \ exit 1; \ } package-manager-2.0.7/testing/baselines/0000755000175000017500000000000013551142424017514 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.plugin_tarfile/0000755000175000017500000000000013551142424023661 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.plugin_tarfile/scripts.packages.rot13.__load__.zeek0000644000175000017500000000027313551142424032470 0ustar rhaistrhaist# # This is loaded when a user activates the plugin. Include scripts here that should be # loaded automatically at that point. # event zeek_init() { print "rot13 script is loaded"; } package-manager-2.0.7/testing/baselines/tests.plugin_tarfile/plugins.packages.rot13.__bro_plugin__0000644000175000017500000000001413551142424032737 0ustar rhaistrhaistDemo::Rot13 package-manager-2.0.7/testing/baselines/tests.load/0000755000175000017500000000000013551142424021574 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.load/after_unload.out0000644000175000017500000000013513551142424024767 0ustar rhaistrhaist# WARNING: This file is managed by zkg. # Do not make direct modifications here. @load ./bar package-manager-2.0.7/testing/baselines/tests.load/after_load.out0000644000175000017500000000015113551142424024422 0ustar rhaistrhaist# WARNING: This file is managed by zkg. # Do not make direct modifications here. @load ./bar @load ./foo package-manager-2.0.7/testing/baselines/tests.load/scripts.packages.packages.zeek0000644000175000017500000000015113551142424027472 0ustar rhaistrhaist# WARNING: This file is managed by zkg. # Do not make direct modifications here. @load ./bar @load ./foo package-manager-2.0.7/testing/baselines/tests.list/0000755000175000017500000000000013551142424021630 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.list/unloaded.out0000644000175000017500000000004213551142424024150 0ustar rhaistrhaistone/alice/bar (installed: master) package-manager-2.0.7/testing/baselines/tests.list/installed.out0000644000175000017500000000010413551142424024333 0ustar rhaistrhaistone/alice/bar (installed: master) one/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.list/loaded.out0000644000175000017500000000004213551142424023605 0ustar rhaistrhaistone/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.list/all.out0000644000175000017500000000012513551142424023127 0ustar rhaistrhaistone/alice/bar one/alice/baz one/alice/foo one/alice/qux one/bob/corge one/bob/grault package-manager-2.0.7/testing/baselines/tests.list/after_upgrade.out0000644000175000017500000000000013551142424025157 0ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.list/outdated.out0000644000175000017500000000004213551142424024166 0ustar rhaistrhaistone/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.list/not_installed.out0000644000175000017500000000007113551142424025216 0ustar rhaistrhaistone/alice/baz one/alice/qux one/bob/corge one/bob/grault package-manager-2.0.7/testing/baselines/tests.metadata-script_dir/0000755000175000017500000000000013551142424024575 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.metadata-script_dir/scripts.foo.__load__.zeek0000644000175000017500000000002413551142424031434 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/baselines/tests.purge/0000755000175000017500000000000013551142424021777 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.purge/scripts.packages.packages.zeek0000644000175000017500000000012113551142424027672 0ustar rhaistrhaist# WARNING: This file is managed by zkg. # Do not make direct modifications here. package-manager-2.0.7/testing/baselines/tests.plugin/0000755000175000017500000000000013551142424022153 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.plugin/scripts.packages.rot13.__load__.zeek0000644000175000017500000000027313551142424030762 0ustar rhaistrhaist# # This is loaded when a user activates the plugin. Include scripts here that should be # loaded automatically at that point. # event zeek_init() { print "rot13 script is loaded"; } package-manager-2.0.7/testing/baselines/tests.plugin/__bro_plugin__.after_enabling0000644000175000017500000000001413551142424027764 0ustar rhaistrhaistDemo::Rot13 package-manager-2.0.7/testing/baselines/tests.plugin/plugins.packages.rot13.__bro_plugin__.disabled0000644000175000017500000000001413551142424032777 0ustar rhaistrhaistDemo::Rot13 package-manager-2.0.7/testing/baselines/tests.plugin/plugins.packages.rot13.__bro_plugin__0000644000175000017500000000001413551142424031231 0ustar rhaistrhaistDemo::Rot13 package-manager-2.0.7/testing/baselines/tests.metadata-config_files/0000755000175000017500000000000013551142424025062 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.metadata-config_files/bar_backup.zeek0000644000175000017500000000002113551142424030024 0ustar rhaistrhaistprint "bar mod"; package-manager-2.0.7/testing/baselines/tests.metadata-config_files/foo_backup.zeek0000644000175000017500000000002113551142424030043 0ustar rhaistrhaistprint "foo mod"; package-manager-2.0.7/testing/baselines/tests.metadata-config_files/barconfig2.zeek0000644000175000017500000000002313551142424027751 0ustar rhaistrhaistprint "bar 2.0.0"; package-manager-2.0.7/testing/baselines/tests.metadata-config_files/scripts.foo.config.zeek0000644000175000017500000000002113551142424031450 0ustar rhaistrhaistprint "foo new"; package-manager-2.0.7/testing/baselines/tests.metadata-config_files/scripts.bar.config.zeek0000644000175000017500000000002113551142424031431 0ustar rhaistrhaistprint "bar mod"; package-manager-2.0.7/testing/baselines/tests.pin/0000755000175000017500000000000013551142424021443 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.pin/bar_after_unpin.out0000644000175000017500000000002613551142424025330 0ustar rhaistrhaistprint "bar++ loaded"; package-manager-2.0.7/testing/baselines/tests.pin/scripts.packages.bar.__load__.zeek0000644000175000017500000000005213551142424030041 0ustar rhaistrhaistevent zeek_init() { print "bar loaded"; } package-manager-2.0.7/testing/baselines/tests.pin/scripts.packages.foo.__load__.zeek0000644000175000017500000000002313551142424030056 0ustar rhaistrhaistprint "foo 1.0.2"; package-manager-2.0.7/testing/baselines/tests.pin/foo_after_unpin.out0000644000175000017500000000002313551142424025344 0ustar rhaistrhaistprint "foo 1.0.4"; package-manager-2.0.7/testing/baselines/tests.user_vars/0000755000175000017500000000000013551142424022666 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.user_vars/state.logs.foo-build.log0000644000175000017500000000010013551142424027322 0ustar rhaistrhaist=== STDERR === === STDOUT === /home/jon/sandbox /usr/local /usr package-manager-2.0.7/testing/baselines/tests.refresh/0000755000175000017500000000000013551142424022313 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.refresh/list_after_agg.out0000644000175000017500000000024513551142424026017 0ustar rhaistrhaistone/alice/bar one/alice/baz one/alice/foo (installed: master) one/alice/new_pkg - This is the new_pkg package description one/alice/qux one/bob/corge one/bob/grault package-manager-2.0.7/testing/baselines/tests.refresh/list.out0000644000175000017500000000017313551142424024020 0ustar rhaistrhaistone/alice/bar one/alice/baz one/alice/foo (installed: master) one/alice/new_pkg one/alice/qux one/bob/corge one/bob/grault package-manager-2.0.7/testing/baselines/tests.refresh/search.out0000644000175000017500000000020413551142424024305 0ustar rhaistrhaistone/alice/foo (installed: master) - This is the foo package description one/alice/new_pkg - This is the new_pkg package description package-manager-2.0.7/testing/baselines/tests.refresh/outdated.out0000644000175000017500000000004213551142424024651 0ustar rhaistrhaistone/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.upgrade/0000755000175000017500000000000013551142424022304 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.upgrade/scripts.packages.bar.__load__.zeek0000644000175000017500000000002613551142424030703 0ustar rhaistrhaistprint "bar++ loaded"; package-manager-2.0.7/testing/baselines/tests.upgrade/scripts.packages.foo.__load__.zeek0000644000175000017500000000002313551142424030717 0ustar rhaistrhaistprint "foo 1.0.4"; package-manager-2.0.7/testing/baselines/tests.package-submodule/0000755000175000017500000000000013551142424024245 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.package-submodule/foo.install0000644000175000017500000000005213551142424026415 0ustar rhaistrhaistevent zeek_init() { print "bar loaded"; } package-manager-2.0.7/testing/baselines/tests.package-submodule/foo.upgrade0000644000175000017500000000006513551142424026402 0ustar rhaistrhaistevent zeek_init() { print "bar upgrade is loaded"; } package-manager-2.0.7/testing/baselines/tests.metadata-suggests/0000755000175000017500000000000013551142424024277 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.metadata-suggests/upgrade_suggests.out0000644000175000017500000000025013551142424030400 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-suggests/install_suggests.out0000644000175000017500000000025013551142424030417 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-suggests/bundle_suggests.out0000644000175000017500000000025013551142424030222 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-suggests/no_suggests.out0000644000175000017500000000004213551142424027364 0ustar rhaistrhaistone/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-aliases/0000755000175000017500000000000013551142424024054 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.metadata-aliases/scripts.foo3.__load__.zeek0000644000175000017500000000002413551142424030776 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/baselines/tests.metadata-aliases/scripts.foo.__load__.zeek0000644000175000017500000000002413551142424030713 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/baselines/tests.metadata-aliases/scripts.foo2.__load__.zeek0000644000175000017500000000002413551142424030775 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/baselines/tests.bundle/0000755000175000017500000000000013551142424022126 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.bundle/manifest.out0000644000175000017500000000014413551142424024464 0ustar rhaistrhaistone/alice/bar (installed: 1.0.1) one/alice/baz (installed: master) one/alice/foo (installed: 1.0.0) package-manager-2.0.7/testing/baselines/tests.bundle/snapshot.out0000644000175000017500000000014413551142424024515 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: master) one/alice/foo (installed: 1.0.2) package-manager-2.0.7/testing/baselines/tests.bundle/args.out0000644000175000017500000000014413551142424023612 0ustar rhaistrhaistone/alice/bar (installed: 1.0.2) one/alice/baz (installed: master) one/alice/foo (installed: 1.0.2) package-manager-2.0.7/testing/baselines/tests.info/0000755000175000017500000000000013551142424021610 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.info/bar.info0000644000175000017500000000056113551142424023233 0ustar rhaistrhaist"one/alice/bar" info: versions: ['1.0.0'] install status: current_version = 1.0.0 is_loaded = True is_outdated = False is_pinned = False tracking_method = version metadata file: /Users/jon/pro/zeek/package-manager/testing/.tmp/tests.info/state/clones/package/bar/zkg.meta metadata (from version "1.0.0"): description = bar bar bar tags = sega sunset package-manager-2.0.7/testing/baselines/tests.info/installed.info0000644000175000017500000000055213551142424024446 0ustar rhaistrhaist"one/alice/foo" info: versions: ['1.0.0', '1.0.1', '1.0.2'] install status: current_version = master is_loaded = True is_outdated = True is_pinned = False tracking_method = branch metadata file: /Users/jon/pro/zeek/package-manager/testing/.tmp/tests.info/state/clones/package/foo/zkg.meta metadata (from version "master"): package-manager-2.0.7/testing/baselines/tests.info/foo.info0000644000175000017500000000055213551142424023252 0ustar rhaistrhaist"one/alice/foo" info: versions: ['1.0.0', '1.0.1', '1.0.2'] install status: current_version = master is_loaded = True is_outdated = True is_pinned = False tracking_method = branch metadata file: /Users/jon/pro/zeek/package-manager/testing/.tmp/tests.info/state/clones/package/foo/zkg.meta metadata (from version "master"): package-manager-2.0.7/testing/baselines/tests.install/0000755000175000017500000000000013551142424022323 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.install/scripts.packages.grault.__load__.zeek0000644000175000017500000000002613551142424031454 0ustar rhaistrhaistprint "grault 1.0.2"; package-manager-2.0.7/testing/baselines/tests.install/grault.info0000644000175000017500000000003313551142424024472 0ustar rhaistrhaist tracking_method = commit package-manager-2.0.7/testing/baselines/tests.install/grault.detached.head0000644000175000017500000000002613551142424026202 0ustar rhaistrhaistprint "grault 1.0.0"; package-manager-2.0.7/testing/baselines/tests.install/scripts.packages.corge.__load__.zeek0000644000175000017500000000002613551142424031255 0ustar rhaistrhaistprint "corge loaded"; package-manager-2.0.7/testing/baselines/tests.install/corge.master0000644000175000017500000000004513551142424024636 0ustar rhaistrhaistprint "corge loaded"; print "hello"; package-manager-2.0.7/testing/baselines/tests.install/scripts.packages.bar.__load__.zeek0000644000175000017500000000005213551142424030721 0ustar rhaistrhaistevent zeek_init() { print "bar loaded"; } package-manager-2.0.7/testing/baselines/tests.install/scripts.packages.qux.__load__.zeek0000644000175000017500000000002413551142424030771 0ustar rhaistrhaistprint "qux loaded"; package-manager-2.0.7/testing/baselines/tests.install/grault.1.0.10000644000175000017500000000002613551142424024176 0ustar rhaistrhaistprint "grault 1.0.1"; package-manager-2.0.7/testing/baselines/tests.install/scripts.packages.foo.__load__.zeek0000644000175000017500000000002413551142424030737 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/baselines/tests.install/grault.detached0000644000175000017500000000002613551142424025302 0ustar rhaistrhaistprint "grault 1.0.2"; package-manager-2.0.7/testing/baselines/tests.install/scripts.packages.baz.__load__.zeek0000644000175000017500000000002413551142424030730 0ustar rhaistrhaistprint "baz loaded"; package-manager-2.0.7/testing/baselines/tests.metadata-depends/0000755000175000017500000000000013551142424024055 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.metadata-depends/bundle_depends.out0000644000175000017500000000025013551142424027556 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-depends/upgrade_depends.out0000644000175000017500000000025013551142424027734 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-depends/install_depends.out0000644000175000017500000000025013551142424027753 0ustar rhaistrhaistone/alice/bar (installed: 1.0.0) one/alice/baz (installed: 2.0.0) one/alice/foo (installed: master) one/bob/corge (installed: 1.0.1) one/bob/grault (installed: master) package-manager-2.0.7/testing/baselines/tests.metadata-depends/no_depends.out0000644000175000017500000000004213551142424026720 0ustar rhaistrhaistone/alice/foo (installed: master) package-manager-2.0.7/testing/baselines/tests.remove/0000755000175000017500000000000013551142424022152 5ustar rhaistrhaistpackage-manager-2.0.7/testing/baselines/tests.remove/scripts.packages.packages.zeek0000644000175000017500000000015113551142424030050 0ustar rhaistrhaist# WARNING: This file is managed by zkg. # Do not make direct modifications here. @load ./bar @load ./baz package-manager-2.0.7/testing/packages/0000755000175000017500000000000013551142424017325 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/foo/0000755000175000017500000000000013551142424020110 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/foo/zkg.meta0000644000175000017500000000001213551142424021544 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/foo/__load__.zeek0000644000175000017500000000002413551142424022477 0ustar rhaistrhaistprint "foo loaded"; package-manager-2.0.7/testing/packages/rot13/0000755000175000017500000000000013551142424020275 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/README0000644000175000017500000000012413551142424021152 0ustar rhaistrhaist Demo::Rot13 ================================= package-manager-2.0.7/testing/packages/rot13/COPYING.edit-me0000644000175000017500000000325313551142424022656 0ustar rhaistrhaist### ### This is a BSD-style license. If you're happy with it, just edit ### the XXX parts below and remove this comment. Otherwise, put in ### your own license instead. ### Copyright (c) 2015 by Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. (3) Neither the name of , nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package-manager-2.0.7/testing/packages/rot13/src/0000755000175000017500000000000013551142424021064 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/src/rot13.bif0000644000175000017500000000062513551142424022521 0ustar rhaistrhaistmodule Demo; function rot13%(s: string%) : string %{ char* rot13 = copy_string(s->CheckString()); for ( char* p = rot13; *p; p++ ) { char b = islower(*p) ? 'a' : 'A'; *p = (*p - b + 13) % 26 + b; } BroString* bs = new BroString(1, reinterpret_cast(rot13), strlen(rot13)); return new StringVal(bs); %} package-manager-2.0.7/testing/packages/rot13/src/Plugin.h0000644000175000017500000000046213551142424022475 0ustar rhaistrhaist #ifndef ZEEK_PLUGIN_DEMO_ROT13 #define ZEEK_PLUGIN_DEMO_ROT13 #include namespace plugin { namespace Demo_Rot13 { class Plugin : public ::plugin::Plugin { protected: // Overridden from plugin::Plugin. virtual plugin::Configuration Configure(); }; extern Plugin plugin; } } #endif package-manager-2.0.7/testing/packages/rot13/src/Plugin.cc0000644000175000017500000000053213551142424022631 0ustar rhaistrhaist #include "Plugin.h" namespace plugin { namespace Demo_Rot13 { Plugin plugin; } } using namespace plugin::Demo_Rot13; plugin::Configuration Plugin::Configure() { plugin::Configuration config; config.name = "Demo::Rot13"; config.description = ""; config.version.major = 0; config.version.minor = 1; return config; } package-manager-2.0.7/testing/packages/rot13/CHANGES0000644000175000017500000000000013551142424021256 0ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/scripts/0000755000175000017500000000000013551142424021764 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/scripts/Demo/0000755000175000017500000000000013551142424022650 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/scripts/Demo/Rot13/0000755000175000017500000000000013551142424023560 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/scripts/Demo/Rot13/__load__.zeek0000644000175000017500000000027313551142424026155 0ustar rhaistrhaist# # This is loaded when a user activates the plugin. Include scripts here that should be # loaded automatically at that point. # event zeek_init() { print "rot13 script is loaded"; } package-manager-2.0.7/testing/packages/rot13/scripts/types.zeek0000644000175000017500000000000113551142424023777 0ustar rhaistrhaist package-manager-2.0.7/testing/packages/rot13/scripts/init.zeek0000644000175000017500000000000013551142424023575 0ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/scripts/__load__.zeek0000644000175000017500000000054313551142424024361 0ustar rhaistrhaist# # This is loaded unconditionally at Zeek startup. Include scripts here that should # always be loaded. # # Normally, that will be only code that initializes built-in elements. Load # your standard scripts in # scripts///__load__.zeek instead. # @load ./init.zeek event zeek_init() { print "rot13 plugin is loaded"; } package-manager-2.0.7/testing/packages/rot13/scripts/__preload__.zeek0000644000175000017500000000056013551142424025067 0ustar rhaistrhaist# # This is loaded unconditionally at Zeek startup before any of the BiFs that the # plugin defines become available. # # This is primarily for defining types that BiFs already depend on. If you need # to do any other unconditional initialization (usually that's just for other BiF # elemets), that should go into __load__.zeek instead. # @load ./types.zeek package-manager-2.0.7/testing/packages/rot13/VERSION0000644000175000017500000000000413551142424021337 0ustar rhaistrhaist0.1 package-manager-2.0.7/testing/packages/rot13/CMakeLists.txt0000644000175000017500000000036013551142424023034 0ustar rhaistrhaist cmake_minimum_required(VERSION 2.8) project(Plugin) include(ZeekPlugin) zeek_plugin_begin(Demo Rot13) zeek_plugin_cc(src/Plugin.cc) zeek_plugin_bif(src/rot13.bif) zeek_plugin_dist_files(README CHANGES COPYING VERSION) zeek_plugin_end() package-manager-2.0.7/testing/packages/rot13/zkg.meta0000644000175000017500000000017213551142424021740 0ustar rhaistrhaist[package] script_dir = scripts/Demo/Rot13 build_command = ./configure && make test_command = cd testing && btest -d tests package-manager-2.0.7/testing/packages/rot13/Makefile0000644000175000017500000000130013551142424021727 0ustar rhaistrhaist# # Convenience Makefile providing a few common top-level targets. # cmake_build_dir=build arch=`uname -s | tr A-Z a-z`-`uname -m` all: build-it build-it: @test -e $(cmake_build_dir)/config.status || ./configure -@test -e $(cmake_build_dir)/CMakeCache.txt && \ test $(cmake_build_dir)/CMakeCache.txt -ot `cat $(cmake_build_dir)/CMakeCache.txt | grep ZEEK_DIST | cut -d '=' -f 2`/build/CMakeCache.txt && \ echo Updating stale CMake cache && \ touch $(cmake_build_dir)/CMakeCache.txt ( cd $(cmake_build_dir) && make ) install: ( cd $(cmake_build_dir) && make install ) clean: ( cd $(cmake_build_dir) && make clean ) distclean: rm -rf $(cmake_build_dir) test: make -C tests package-manager-2.0.7/testing/packages/rot13/testing/0000755000175000017500000000000013551142424021752 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/testing/Baseline/0000755000175000017500000000000013551142424023474 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/testing/Baseline/tests.main/0000755000175000017500000000000013551142424025561 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/testing/Baseline/tests.main/output0000644000175000017500000000005613551142424027045 0ustar rhaistrhaistrot13 plugin is loaded rot13 script is loaded package-manager-2.0.7/testing/packages/rot13/testing/Baseline/tests.rot13/0000755000175000017500000000000013551142424025605 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/testing/Baseline/tests.rot13/output0000644000175000017500000000003513551142424027066 0ustar rhaistrhaistUryyb rot13 plugin is loaded package-manager-2.0.7/testing/packages/rot13/testing/tests/0000755000175000017500000000000013551142424023114 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/testing/tests/main0000644000175000017500000000014213551142424023760 0ustar rhaistrhaist# @TEST-EXEC: zeek rot13 > output # @TEST-EXEC: TEST_DIFF_CANONIFIER="sort -s" btest-diff output package-manager-2.0.7/testing/packages/rot13/testing/tests/rot130000644000175000017500000000014013551142424024002 0ustar rhaistrhaist# @TEST-EXEC: zeek %INPUT > output # @TEST-EXEC: btest-diff output print Demo::rot13("Hello"); package-manager-2.0.7/testing/packages/rot13/testing/btest.cfg0000644000175000017500000000000013551142424023542 0ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/rot13/configure.plugin0000644000175000017500000000057413551142424023504 0ustar rhaistrhaist#!/bin/sh # # Hooks to add custom options to the configure script. # plugin_usage() { : # Do nothing # cat < /dev/null 2>&1 || { echo "\ This package requires CMake, please install it first, then you may use this configure script to access CMake equivalent functionality.\ " >&2; exit 1; } usage() { cat 1>&2 </dev/null 2>&1; then plugin_usage 1>&2 fi echo exit 1 } # Function to append a CMake cache entry definition to the # CMakeCacheEntries variable # $1 is the cache entry variable name # $2 is the cache entry variable type # $3 is the cache entry variable value append_cache_entry () { CMakeCacheEntries="$CMakeCacheEntries -D $1:$2=$3" } # set defaults builddir=build zeekdist="" installroot="default" CMakeCacheEntries="" while [ $# -ne 0 ]; do case "$1" in -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) optarg= ;; esac case "$1" in --help|-h) usage ;; --zeek-dist=*) zeekdist=`cd $optarg && pwd` ;; --bro-dist=*) # Legacy option for backwards compability zeekdist=`cd $optarg && pwd` ;; --install-root=*) installroot=$optarg ;; --with-binpac=*) append_cache_entry BinPAC_ROOT_DIR PATH $optarg binpac_root=$optarg ;; --with-broker=*) append_cache_entry BROKER_ROOT_DIR PATH $optarg broker_root=$optarg ;; --with-caf=*) append_cache_entry CAF_ROOT_DIR PATH $optarg caf_root=$optarg ;; --with-bifcl=*) append_cache_entry BifCl_EXE PATH $optarg ;; --enable-debug) append_cache_entry BRO_PLUGIN_ENABLE_DEBUG BOOL true ;; *) if type plugin_option >/dev/null 2>&1; then plugin_option $1 && shift && continue; fi echo "Invalid option '$1'. Try $0 --help to see available options." exit 1 ;; esac shift done if [ -z "$zeekdist" ]; then if type zeek-config >/dev/null 2>&1; then zeek_config="zeek-config" elif type bro-config >/dev/null 2>&1; then zeek_config="bro-config" fi if [ -n "${zeek_config}" ]; then if ${zeek_config} --cmake_dir >/dev/null 2>&1; then # Have a newer version of zeek-config that has needed flags append_cache_entry BRO_CONFIG_PREFIX PATH `${zeek_config} --prefix` append_cache_entry BRO_CONFIG_INCLUDE_DIR PATH `${zeek_config} --include_dir` append_cache_entry BRO_CONFIG_PLUGIN_DIR PATH `${zeek_config} --plugin_dir` append_cache_entry BRO_CONFIG_CMAKE_DIR PATH `${zeek_config} --cmake_dir` append_cache_entry CMAKE_MODULE_PATH PATH `${zeek_config} --cmake_dir` build_type=`${zeek_config} --build_type` if [ "$build_type" = "debug" ]; then append_cache_entry BRO_PLUGIN_ENABLE_DEBUG BOOL true fi if [ -z "$binpac_root" ]; then append_cache_entry BinPAC_ROOT_DIR PATH `${zeek_config} --binpac_root` fi if [ -z "$broker_root" ]; then append_cache_entry BROKER_ROOT_DIR PATH `${zeek_config} --broker_root` fi if [ -z "$caf_root" ]; then append_cache_entry CAF_ROOT_DIR PATH `${zeek_config} --caf_root` fi else # Using legacy bro-config, so we must use the "--bro_dist" option. zeekdist=`${zeek_config} --bro_dist 2> /dev/null` if [ ! -e "$zeekdist/zeek-path-dev.in" ]; then echo "$zeekdist does not appear to be a valid Zeek source tree." exit 1 fi # BRO_DIST is needed to support legacy Bro plugins append_cache_entry BRO_DIST PATH $zeekdist append_cache_entry ZEEK_DIST PATH $zeekdist append_cache_entry CMAKE_MODULE_PATH PATH $zeekdist/cmake fi else echo "Either 'zeek-config' must be in PATH or '--zeek-dist=' used" exit 1 fi else if [ ! -e "$zeekdist/zeek-path-dev.in" -a ! -e "$zeekdist/bro-path-dev.in" ]; then echo "$zeekdist does not appear to be a valid Zeek source tree." exit 1 fi # BRO_DIST is needed to support legacy Bro plugins append_cache_entry BRO_DIST PATH $zeekdist append_cache_entry ZEEK_DIST PATH $zeekdist append_cache_entry CMAKE_MODULE_PATH PATH $zeekdist/cmake fi if [ "$installroot" != "default" ]; then mkdir -p $installroot append_cache_entry BRO_PLUGIN_INSTALL_ROOT PATH $installroot fi echo "Build Directory : $builddir" echo "Zeek Source Directory : $zeekdist" mkdir -p $builddir cd $builddir cmake $CMakeCacheEntries .. echo "# This is the command used to configure this build" > config.status echo $command >> config.status chmod u+x config.status package-manager-2.0.7/testing/packages/baz/0000755000175000017500000000000013551142424020101 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/baz/zkg.meta0000644000175000017500000000001213551142424021535 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/baz/__load__.zeek0000644000175000017500000000002413551142424022470 0ustar rhaistrhaistprint "baz loaded"; package-manager-2.0.7/testing/packages/corge/0000755000175000017500000000000013551142424020424 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/corge/zkg.meta0000644000175000017500000000001213551142424022060 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/corge/__load__.zeek0000644000175000017500000000002613551142424023015 0ustar rhaistrhaistprint "corge loaded"; package-manager-2.0.7/testing/packages/grault/0000755000175000017500000000000013551142424020623 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/grault/zkg.meta0000644000175000017500000000001213551142424022257 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/grault/__load__.zeek0000644000175000017500000000002713551142424023215 0ustar rhaistrhaistprint "grault loaded"; package-manager-2.0.7/testing/packages/bar/0000755000175000017500000000000013551142424020071 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/bar/zkg.meta0000644000175000017500000000001213551142424021525 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/bar/__load__.zeek0000644000175000017500000000005213551142424022461 0ustar rhaistrhaistevent zeek_init() { print "bar loaded"; } package-manager-2.0.7/testing/packages/qux/0000755000175000017500000000000013551142424020142 5ustar rhaistrhaistpackage-manager-2.0.7/testing/packages/qux/zkg.meta0000644000175000017500000000001213551142424021576 0ustar rhaistrhaist[package] package-manager-2.0.7/testing/packages/qux/__load__.zeek0000644000175000017500000000002413551142424022531 0ustar rhaistrhaistprint "qux loaded"; package-manager-2.0.7/testing/sources/0000755000175000017500000000000013551142424017232 5ustar rhaistrhaistpackage-manager-2.0.7/testing/sources/one/0000755000175000017500000000000013551142424020013 5ustar rhaistrhaistpackage-manager-2.0.7/testing/sources/one/alice/0000755000175000017500000000000013551142424021070 5ustar rhaistrhaistpackage-manager-2.0.7/testing/sources/one/alice/zkg.index0000644000175000017500000000002013551142424022704 0ustar rhaistrhaistfoo bar baz qux package-manager-2.0.7/testing/sources/one/bob/0000755000175000017500000000000013551142424020555 5ustar rhaistrhaistpackage-manager-2.0.7/testing/sources/one/bob/zkg.index0000644000175000017500000000001513551142424022375 0ustar rhaistrhaistcorge grault package-manager-2.0.7/testing/btest.cfg0000644000175000017500000000054613551142424017356 0ustar rhaistrhaist[btest] TestDirs = tests TmpDir = %(testbase)s/.tmp IgnoreDirs = .svn CVS .tmp .git IgnoreFiles = *.tmp *.swp .DS_Store .gitignore BaselineDir = baselines Initializer = %(testbase)s/scripts/initializer [environment] PATH=%(testbase)s/scripts:%(default_path)s SCRIPTS=%(testbase)s/scripts SOURCES=%(testbase)s/sources PACKAGES=%(testbase)s/packages package-manager-2.0.7/requirements.txt0000644000175000017500000000026613551142424017362 0ustar rhaistrhaist# Requirements for general zkg usage GitPython semantic_version configparser btest # Requirements for development (e.g. building docs) Sphinx sphinxcontrib-napoleon sphinx_rtd_theme