From 6294646827dae3a7bf3f9db408ae5604dd18c636 Mon Sep 17 00:00:00 2001 From: "John L. Singleton" Date: Wed, 14 Feb 2024 16:24:09 -0500 Subject: [PATCH 1/3] fix windows compatability; add flag for skipping compliation. --- codeql_bundle/cli.py | 4 ++++ codeql_bundle/helpers/bundle.py | 34 +++++++++++++++++++++++++-------- codeql_bundle/helpers/codeql.py | 22 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/codeql_bundle/cli.py b/codeql_bundle/cli.py index 301403d..c5dc21a 100644 --- a/codeql_bundle/cli.py +++ b/codeql_bundle/cli.py @@ -40,6 +40,7 @@ type=click.Path(exists=True, path_type=Path), default=Path.cwd(), ) +@click.option('--no-precompile', '-nc', is_flag=True, help="Do not pre-compile the bundle.") @click.option( "-l", "--log", @@ -56,6 +57,7 @@ def main( bundle_path: Path, output: Path, workspace: Path, + no_precompile: bool, loglevel: str, platform: List[str], code_scanning_config: Optional[Path], @@ -82,6 +84,8 @@ def main( try: bundle = CustomBundle(bundle_path, workspace) + # options for custom bundle + bundle.disable_precompilation = no_precompile unsupported_platforms = list(filter(lambda p: not bundle.supports_platform(BundlePlatform.from_string(p)), platform)) if len(unsupported_platforms) > 0: diff --git a/codeql_bundle/helpers/bundle.py b/codeql_bundle/helpers/bundle.py index e0ca889..51521eb 100644 --- a/codeql_bundle/helpers/bundle.py +++ b/codeql_bundle/helpers/bundle.py @@ -144,6 +144,8 @@ def __str__(self): class Bundle: def __init__(self, bundle_path: Path) -> None: self.tmp_dir = TemporaryDirectory() + self.disable_precompilation = False + if bundle_path.is_dir(): self.bundle_path = Path(self.tmp_dir.name) / bundle_path.name shutil.copytree( @@ -190,7 +192,8 @@ def supports_windows() -> set[BundlePlatform]: elif current_system == "Windows" and BundlePlatform.WINDOWS not in self.platforms: raise BundleException("Bundle doesn't support Windows!") - self.codeql = CodeQL(self.bundle_path / "codeql") + self.codeql = CodeQL(self.bundle_codeql_exe) + try: logging.info(f"Validating the CodeQL CLI version part of the bundle.") unpacked_location = self.codeql.unpacked_location() @@ -215,12 +218,28 @@ def __del__(self) -> None: f"Removing temporary directory {self.tmp_dir.name} used to build custom bundle." ) self.tmp_dir.cleanup() - + def get_bundle_packs(self) -> List[ResolvedCodeQLPack]: return self.bundle_packs def supports_platform(self, platform: BundlePlatform) -> bool: return platform in self.platforms + + @property + def bundle_codeql_exe(self): + if platform.system() == "Windows": + return self.bundle_path / "codeql.exe" + + return self.bundle_path / "codeql" + + @property + def disable_precompilation(self): + return self._disable_precompilation + + @disable_precompilation.setter + def disable_precompilation(self, value: bool): + self._disable_precompilation = value + class CustomBundle(Bundle): def __init__(self, bundle_path: Path, workspace_path: Path = Path.cwd()) -> None: @@ -341,7 +360,7 @@ def bundle_customization_pack(customization_pack: ResolvedCodeQLPack): f"Bundling the customization pack {customization_pack_copy.config.name} at {customization_pack_copy.path}" ) self.codeql.pack_bundle( - customization_pack_copy, self.bundle_path / "qlpacks" + customization_pack_copy, self.bundle_path / "qlpacks", disable_precompilation=self.disable_precompilation ) def copy_pack(pack: ResolvedCodeQLPack) -> ResolvedCodeQLPack: @@ -470,13 +489,14 @@ def bundle_stdlib_pack(pack: ResolvedCodeQLPack): logging.debug( f"Bundling the standard library pack {pack_copy.config.name} at {pack_copy.path}" ) - self.codeql.pack_bundle(pack_copy, self.bundle_path / "qlpacks") + self.codeql.pack_bundle(pack_copy, self.bundle_path / "qlpacks", disable_precompilation=self.disable_precompilation) def bundle_library_pack(library_pack: ResolvedCodeQLPack): logging.info(f"Bundling the library pack {library_pack.config.name}.") self.codeql.pack_bundle( library_pack, self.bundle_path / "qlpacks", + disable_precompilation=self.disable_precompilation ) def bundle_query_pack(pack: ResolvedCodeQLPack): @@ -520,8 +540,7 @@ def bundle_query_pack(pack: ResolvedCodeQLPack): self.codeql.pack_create( pack_copy, self.bundle_path / "qlpacks", - self.bundle_path, - ) + self.bundle_path) else: logging.info(f"Bundling the query pack {pack.config.name}.") pack_copy = copy_pack(pack) @@ -537,8 +556,7 @@ def bundle_query_pack(pack: ResolvedCodeQLPack): self.codeql.pack_create( pack_copy, - self.bundle_path / "qlpacks", - ) + self.bundle_path / "qlpacks") sorted_packs = list(pack_sorter.static_order()) logger.debug(f"Sorted packs: {' -> '.join(map(lambda p: p.config.name, sorted_packs))}") diff --git a/codeql_bundle/helpers/codeql.py b/codeql_bundle/helpers/codeql.py index 085d2a5..f5a9462 100644 --- a/codeql_bundle/helpers/codeql.py +++ b/codeql_bundle/helpers/codeql.py @@ -65,6 +65,14 @@ def __init__(self, codeql_path: Path): self.codeql_path = codeql_path self._version = None + @property + def disable_precompilation(self): + return self._disable_precompilation + + @disable_precompilation.setter + def disable_precompilation(self, value: bool): + self._disable_precompilation = value + def _exec(self, command: str, *args: str) -> subprocess.CompletedProcess[str]: logger.debug( f"Running CodeQL command: {command} with arguments: {' '.join(args)}" @@ -124,11 +132,18 @@ def pack_bundle( pack: CodeQLPack, output_path: Path, *additional_packs: Path, + disable_precompilation = False ): if not pack.config.library: raise CodeQLException(f"Cannot bundle non-library pack {pack.config.name}!") args = ["bundle", "--format=json", f"--pack-path={output_path}"] + if disable_precompilation: + args.append("--no-precompile") + logging.warn( + f"NOTE: Precompilation is disabled for {pack.config.name}! This may result in slower query execution." + ) + if len(additional_packs) > 0: args.append(f"--additional-packs={':'.join(map(str,additional_packs))}") cp = self._exec( @@ -146,11 +161,18 @@ def pack_create( pack: CodeQLPack, output_path: Path, *additional_packs: Path, + disable_precompilation = False ): if pack.config.library: raise CodeQLException(f"Cannot bundle non-query pack {pack.config.name}!") args = ["create", "--format=json", f"--output={output_path}", "--threads=0", "--no-default-compilation-cache"] + if disable_precompilation: + args.append("--no-precompile") + logging.warn( + f"NOTE: Precompilation is disabled for {pack.config.name}! This may result in slower query execution." + ) + if self.supports_qlx(): args.append("--qlx") if len(additional_packs) > 0: From 99ff57336c8cc872966d05000a63899660d6fbf0 Mon Sep 17 00:00:00 2001 From: Nicolas Kraiouchkine Date: Wed, 8 May 2024 13:26:12 +0200 Subject: [PATCH 2/3] Add support for additional files and certificates --- codeql_bundle/cli.py | 48 +++- codeql_bundle/helpers/bundle.py | 453 +++++++++++++++++++++++++------- poetry.lock | 235 ++++++++++++++--- pyproject.toml | 1 + 4 files changed, 604 insertions(+), 133 deletions(-) diff --git a/codeql_bundle/cli.py b/codeql_bundle/cli.py index c5dc21a..b1f254c 100644 --- a/codeql_bundle/cli.py +++ b/codeql_bundle/cli.py @@ -14,6 +14,7 @@ from typing import List, Optional import sys import logging +import os logger = logging.getLogger(__name__) @@ -40,7 +41,9 @@ type=click.Path(exists=True, path_type=Path), default=Path.cwd(), ) -@click.option('--no-precompile', '-nc', is_flag=True, help="Do not pre-compile the bundle.") +@click.option( + "--no-precompile", "-nc", is_flag=True, help="Do not pre-compile the bundle." +) @click.option( "-l", "--log", @@ -50,8 +53,25 @@ ), default="WARNING", ) -@click.option("-p", "--platform", multiple=True, type=click.Choice(["linux64", "osx64", "win64"], case_sensitive=False), help="Target platform for the bundle") -@click.option("-c", "--code-scanning-config", type=click.Path(exists=True, path_type=Path), help="Path to a Code Scanning configuration file that will be the default for the bundle") +@click.option( + "-p", + "--platform", + multiple=True, + type=click.Choice(["linux64", "osx64", "win64"], case_sensitive=False), + help="Target platform for the bundle", +) +@click.option( + "-c", + "--code-scanning-config", + type=click.Path(exists=True, path_type=Path), + help="Path to a Code Scanning configuration file that will be the default for the bundle", +) +@click.option( + "-a", + "--additional-data-config", + type=click.Path(exists=True, path_type=Path), + help="Path to a JSON file specifying additional data to install into the bundle", +) @click.argument("packs", nargs=-1, required=True) def main( bundle_path: Path, @@ -61,6 +81,7 @@ def main( loglevel: str, platform: List[str], code_scanning_config: Optional[Path], + additional_data_config: Optional[Path], packs: List[str], ) -> None: @@ -75,6 +96,8 @@ def main( level=getattr(logging, loglevel.upper()), ) + workspace = Path(os.path.abspath(workspace)) + if workspace.name == "codeql-workspace.yml": workspace = workspace.parent @@ -84,10 +107,15 @@ def main( try: bundle = CustomBundle(bundle_path, workspace) - # options for custom bundle + # options for custom bundle bundle.disable_precompilation = no_precompile - unsupported_platforms = list(filter(lambda p: not bundle.supports_platform(BundlePlatform.from_string(p)), platform)) + unsupported_platforms = list( + filter( + lambda p: not bundle.supports_platform(BundlePlatform.from_string(p)), + platform, + ) + ) if len(unsupported_platforms) > 0: logger.fatal( f"The provided bundle supports the platform(s) {', '.join(map(str, bundle.platforms))}, but doesn't support the following platform(s): {', '.join(unsupported_platforms)}" @@ -113,7 +141,6 @@ def main( else: selected_packs = packs_in_workspace - missing_packs = set(packs) - {pack.config.name for pack in selected_packs} if len(missing_packs) > 0: logger.fatal( @@ -125,8 +152,15 @@ def main( f"Adding the pack(s) {','.join(map(lambda p: p.config.name, selected_packs))} and its workspace dependencies to the custom bundle." ) bundle.add_packs(*selected_packs) + if additional_data_config: + logger.info( + f"Installing additions specified in the config file {additional_data_config} to the custom bundle." + ) + bundle.add_files_and_certs(additional_data_config, workspace) if code_scanning_config: - logger.info(f"Adding the Code Scanning configuration file {code_scanning_config} to the custom bundle.") + logger.info( + f"Adding the Code Scanning configuration file {code_scanning_config} to the custom bundle." + ) bundle.add_code_scanning_config(code_scanning_config) logger.info(f"Bundling custom bundle(s) at {output}") platforms = set(map(BundlePlatform.from_string, platform)) diff --git a/codeql_bundle/helpers/bundle.py b/codeql_bundle/helpers/bundle.py index 51521eb..242b0ce 100644 --- a/codeql_bundle/helpers/bundle.py +++ b/codeql_bundle/helpers/bundle.py @@ -1,8 +1,4 @@ -from .codeql import ( - CodeQL, - CodeQLException, - CodeQLPack -) +from .codeql import CodeQL, CodeQLException, CodeQLPack from pathlib import Path from tempfile import TemporaryDirectory import tarfile @@ -12,6 +8,10 @@ import yaml import dataclasses import logging +import json +import os +import subprocess +from jsonschema import validate, ValidationError from enum import Enum, verify, UNIQUE from dataclasses import dataclass from graphlib import TopologicalSorter @@ -20,12 +20,14 @@ logger = logging.getLogger(__name__) + @verify(UNIQUE) class CodeQLPackKind(Enum): QUERY_PACK = 1 LIBRARY_PACK = 2 CUSTOMIZATION_PACK = 3 + @dataclass(kw_only=True, frozen=True, eq=True) class ResolvedCodeQLPack(CodeQLPack): kind: CodeQLPackKind @@ -55,17 +57,24 @@ def get_cache_path(self) -> Path: def is_stdlib_module(self) -> bool: return self.config.get_scope() == "codeql" + class BundleException(Exception): pass + class PackResolverException(Exception): pass -def build_pack_resolver(packs: List[CodeQLPack], already_resolved_packs: List[ResolvedCodeQLPack] = []) -> Callable[[CodeQLPack], ResolvedCodeQLPack]: - def builder() -> Callable[[CodeQLPack], ResolvedCodeQLPack]: - resolved_packs: dict[CodeQLPack, ResolvedCodeQLPack] = {pack: pack for pack in already_resolved_packs} - candidates : dict[str, List[CodeQLPack]] = defaultdict(list) +def build_pack_resolver( + packs: List[CodeQLPack], already_resolved_packs: List[ResolvedCodeQLPack] = [] +) -> Callable[[CodeQLPack], ResolvedCodeQLPack]: + def builder() -> Callable[[CodeQLPack], ResolvedCodeQLPack]: + resolved_packs: dict[CodeQLPack, ResolvedCodeQLPack] = { + pack: pack for pack in already_resolved_packs + } + + candidates: dict[str, List[CodeQLPack]] = defaultdict(list) for pack in packs + already_resolved_packs: candidates[pack.config.name].append(pack) @@ -84,36 +93,58 @@ def get_pack_kind(pack: CodeQLPack) -> CodeQLPackKind: def resolve(pack: CodeQLPack) -> ResolvedCodeQLPack: def inner(pack_to_be_resolved: CodeQLPack) -> ResolvedCodeQLPack: - logger.debug(f"Resolving pack {pack_to_be_resolved.config.name}@{pack_to_be_resolved.config.version}") + logger.debug( + f"Resolving pack {pack_to_be_resolved.config.name}@{pack_to_be_resolved.config.version}" + ) if pack_to_be_resolved in resolved_packs: - logger.debug(f"Resolved pack {pack_to_be_resolved.config.name}@{pack_to_be_resolved.config.version}, already resolved.") + logger.debug( + f"Resolved pack {pack_to_be_resolved.config.name}@{pack_to_be_resolved.config.version}, already resolved." + ) return resolved_packs[pack_to_be_resolved] else: resolved_deps: List[ResolvedCodeQLPack] = [] - for dep_name, dep_version in pack_to_be_resolved.config.dependencies.items(): + for ( + dep_name, + dep_version, + ) in pack_to_be_resolved.config.dependencies.items(): logger.debug(f"Resolving dependency {dep_name}:{dep_version}.") resolved_dep = None for candidate_pack in candidates[dep_name]: - logger.debug(f"Considering candidate pack {candidate_pack.config.name}@{candidate_pack.config.version}.") + logger.debug( + f"Considering candidate pack {candidate_pack.config.name}@{candidate_pack.config.version}." + ) if candidate_pack == pack: - raise PackResolverException(f"Pack {pack.config.name}@{str(pack.config.version)} (transitively) depends on itself via {pack_to_be_resolved.config.name}@{str(pack_to_be_resolved.config.version)}!") + raise PackResolverException( + f"Pack {pack.config.name}@{str(pack.config.version)} (transitively) depends on itself via {pack_to_be_resolved.config.name}@{str(pack_to_be_resolved.config.version)}!" + ) if dep_version.match(candidate_pack.config.version): - logger.debug(f"Found candidate pack {candidate_pack.config.name}@{candidate_pack.config.version}.") + logger.debug( + f"Found candidate pack {candidate_pack.config.name}@{candidate_pack.config.version}." + ) resolved_dep = inner(candidate_pack) if not resolved_dep: - raise PackResolverException(f"Could not resolve dependency {dep_name}@{dep_version} for pack {pack_to_be_resolved.config.name}@{str(pack_to_be_resolved.config.version)}!") + raise PackResolverException( + f"Could not resolve dependency {dep_name}@{dep_version} for pack {pack_to_be_resolved.config.name}@{str(pack_to_be_resolved.config.version)}!" + ) resolved_deps.append(resolved_dep) - - resolved_pack = ResolvedCodeQLPack(path=pack_to_be_resolved.path, config=pack_to_be_resolved.config, kind=get_pack_kind(pack_to_be_resolved), dependencies=resolved_deps) + resolved_pack = ResolvedCodeQLPack( + path=pack_to_be_resolved.path, + config=pack_to_be_resolved.config, + kind=get_pack_kind(pack_to_be_resolved), + dependencies=resolved_deps, + ) resolved_packs[pack_to_be_resolved] = resolved_pack return resolved_pack + return inner(pack) + return resolve return builder() + @verify(UNIQUE) class BundlePlatform(Enum): LINUX = 1 @@ -141,10 +172,11 @@ def __str__(self): else: raise BundleException(f"Invalid platform {self}") + class Bundle: def __init__(self, bundle_path: Path) -> None: self.tmp_dir = TemporaryDirectory() - self.disable_precompilation = False + self.disable_precompilation = False if bundle_path.is_dir(): self.bundle_path = Path(self.tmp_dir.name) / bundle_path.name @@ -180,7 +212,9 @@ def supports_windows() -> set[BundlePlatform]: else: return set() - self.platforms: set[BundlePlatform] = supports_linux() | supports_macos() | supports_windows() + self.platforms: set[BundlePlatform] = ( + supports_linux() | supports_macos() | supports_windows() + ) current_system = platform.system() if not current_system in ["Linux", "Darwin", "Windows"]: @@ -189,7 +223,9 @@ def supports_windows() -> set[BundlePlatform]: raise BundleException("Bundle doesn't support Linux!") elif current_system == "Darwin" and BundlePlatform.OSX not in self.platforms: raise BundleException("Bundle doesn't support OSX!") - elif current_system == "Windows" and BundlePlatform.WINDOWS not in self.platforms: + elif ( + current_system == "Windows" and BundlePlatform.WINDOWS not in self.platforms + ): raise BundleException("Bundle doesn't support Windows!") self.codeql = CodeQL(self.bundle_codeql_exe) @@ -205,7 +241,9 @@ def supports_windows() -> set[BundlePlatform]: packs: List[CodeQLPack] = self.codeql.pack_ls(self.bundle_path) resolve = build_pack_resolver(packs) - self.bundle_packs: list[ResolvedCodeQLPack] = [resolve(pack) for pack in packs] + self.bundle_packs: list[ResolvedCodeQLPack] = [ + resolve(pack) for pack in packs + ] self.languages = self.codeql.resolve_languages() @@ -218,24 +256,24 @@ def __del__(self) -> None: f"Removing temporary directory {self.tmp_dir.name} used to build custom bundle." ) self.tmp_dir.cleanup() - + def get_bundle_packs(self) -> List[ResolvedCodeQLPack]: return self.bundle_packs def supports_platform(self, platform: BundlePlatform) -> bool: return platform in self.platforms - + @property def bundle_codeql_exe(self): if platform.system() == "Windows": return self.bundle_path / "codeql.exe" - + return self.bundle_path / "codeql" - + @property def disable_precompilation(self): return self._disable_precompilation - + @disable_precompilation.setter def disable_precompilation(self, value: bool): self._disable_precompilation = value @@ -255,7 +293,9 @@ def __init__(self, bundle_path: Path, workspace_path: Path = Path.cwd()) -> None resolve = build_pack_resolver(packs, self.bundle_packs) try: - self.workspace_packs: list[ResolvedCodeQLPack] = [resolve(pack) for pack in packs] + self.workspace_packs: list[ResolvedCodeQLPack] = [ + resolve(pack) for pack in packs + ] except PackResolverException as e: raise BundleException(e) @@ -292,16 +332,26 @@ def add_packs(self, *packs: ResolvedCodeQLPack): and import as the first module in the language module (eg., `cpp.qll` will import `Customizations.qll` as the first module). """ # Keep a map of standard library packs to their customization packs so we know which need to be modified. - std_lib_deps : dict[ResolvedCodeQLPack, List[ResolvedCodeQLPack]] = defaultdict(list) - pack_sorter : TopologicalSorter[ResolvedCodeQLPack] = TopologicalSorter() - - def add_to_graph(pack: ResolvedCodeQLPack, processed_packs: set[ResolvedCodeQLPack], std_lib_deps: dict[ResolvedCodeQLPack, List[ResolvedCodeQLPack]]): + std_lib_deps: dict[ResolvedCodeQLPack, List[ResolvedCodeQLPack]] = defaultdict( + list + ) + pack_sorter: TopologicalSorter[ResolvedCodeQLPack] = TopologicalSorter() + + def add_to_graph( + pack: ResolvedCodeQLPack, + processed_packs: set[ResolvedCodeQLPack], + std_lib_deps: dict[ResolvedCodeQLPack, List[ResolvedCodeQLPack]], + ): # Only process workspace packs in this function if not pack in self.workspace_packs: - logger.debug(f"Skipping adding pack {pack.config.name}@{str(pack.config.version)} to dependency graph") + logger.debug( + f"Skipping adding pack {pack.config.name}@{str(pack.config.version)} to dependency graph" + ) return if pack.kind == CodeQLPackKind.CUSTOMIZATION_PACK: - logger.debug(f"Adding customization pack {pack.config.name}@{str(pack.config.version)} to dependency graph") + logger.debug( + f"Adding customization pack {pack.config.name}@{str(pack.config.version)} to dependency graph" + ) pack_sorter.add(pack) std_lib_deps[pack.dependencies[0]].append(pack) else: @@ -309,12 +359,20 @@ def add_to_graph(pack: ResolvedCodeQLPack, processed_packs: set[ResolvedCodeQLPa # the customization pack to query pack because the customization pack will no longer have that # dependency in the bundle. if pack.kind == CodeQLPackKind.QUERY_PACK: - for customization_pack in [dep for dep in pack.dependencies if dep.kind == CodeQLPackKind.CUSTOMIZATION_PACK]: + for customization_pack in [ + dep + for dep in pack.dependencies + if dep.kind == CodeQLPackKind.CUSTOMIZATION_PACK + ]: std_lib_dep = customization_pack.dependencies[0] if not std_lib_dep in pack.dependencies: - logger.debug(f"Adding stdlib dependency {std_lib_dep.config.name}@{str(std_lib_dep.config.version)} to {pack.config.name}@{str(pack.config.version)}") + logger.debug( + f"Adding stdlib dependency {std_lib_dep.config.name}@{str(std_lib_dep.config.version)} to {pack.config.name}@{str(pack.config.version)}" + ) pack.dependencies.append(std_lib_dep) - logger.debug(f"Adding pack {pack.config.name}@{str(pack.config.version)} to dependency graph") + logger.debug( + f"Adding pack {pack.config.name}@{str(pack.config.version)} to dependency graph" + ) # We include standard library packs in the dependency graph to ensure they dictate the correct order # when we need to customize packs. # This does mean we will repack them, but that is only small price to pay for simplicity. @@ -324,24 +382,39 @@ def add_to_graph(pack: ResolvedCodeQLPack, processed_packs: set[ResolvedCodeQLPa add_to_graph(dep, processed_packs, std_lib_deps) processed_packs.add(pack) - processed_packs : set[ResolvedCodeQLPack] = set() + processed_packs: set[ResolvedCodeQLPack] = set() for pack in packs: if not pack in processed_packs: add_to_graph(pack, processed_packs, std_lib_deps) - def is_dependent_on(pack: ResolvedCodeQLPack, other: ResolvedCodeQLPack) -> bool: - return other in pack.dependencies or any(map(lambda p: is_dependent_on(p, other), pack.dependencies)) + def is_dependent_on( + pack: ResolvedCodeQLPack, other: ResolvedCodeQLPack + ) -> bool: + return other in pack.dependencies or any( + map(lambda p: is_dependent_on(p, other), pack.dependencies) + ) + # Add the stdlib and its dependencies to properly sort the customization packs before the other packs. for pack, deps in std_lib_deps.items(): - logger.debug(f"Adding standard library pack {pack.config.name}@{str(pack.config.version)} to dependency graph") + logger.debug( + f"Adding standard library pack {pack.config.name}@{str(pack.config.version)} to dependency graph" + ) pack_sorter.add(pack, *deps) # Add the standard query packs that rely transitively on the stdlib. - for query_pack in [p for p in self.bundle_packs if p.kind == CodeQLPackKind.QUERY_PACK and is_dependent_on(p, pack)]: - logger.debug(f"Adding standard query pack {query_pack.config.name}@{str(query_pack.config.version)} to dependency graph") + for query_pack in [ + p + for p in self.bundle_packs + if p.kind == CodeQLPackKind.QUERY_PACK and is_dependent_on(p, pack) + ]: + logger.debug( + f"Adding standard query pack {query_pack.config.name}@{str(query_pack.config.version)} to dependency graph" + ) pack_sorter.add(query_pack, pack) def bundle_customization_pack(customization_pack: ResolvedCodeQLPack): - logging.info(f"Bundling the customization pack {customization_pack.config.name}.") + logging.info( + f"Bundling the customization pack {customization_pack.config.name}." + ) customization_pack_copy = copy_pack(customization_pack) # Remove the target dependency to prevent a circular dependency in the target. @@ -360,16 +433,18 @@ def bundle_customization_pack(customization_pack: ResolvedCodeQLPack): f"Bundling the customization pack {customization_pack_copy.config.name} at {customization_pack_copy.path}" ) self.codeql.pack_bundle( - customization_pack_copy, self.bundle_path / "qlpacks", disable_precompilation=self.disable_precompilation + customization_pack_copy, + self.bundle_path / "qlpacks", + disable_precompilation=self.disable_precompilation, ) def copy_pack(pack: ResolvedCodeQLPack) -> ResolvedCodeQLPack: pack_copy_dir = ( - Path(self.tmp_dir.name) - / "temp" # Add a temp path segment because the standard library packs have scope 'codeql' that collides with the 'codeql' directory in the bundle that is extracted to the temporary directory. - / cast(str, pack.config.get_scope()) - / pack.config.get_pack_name() - / str(pack.config.version) + Path(self.tmp_dir.name) + / "temp" # Add a temp path segment because the standard library packs have scope 'codeql' that collides with the 'codeql' directory in the bundle that is extracted to the temporary directory. + / cast(str, pack.config.get_scope()) + / pack.config.get_pack_name() + / str(pack.config.version) ) logging.debug( @@ -379,9 +454,7 @@ def copy_pack(pack: ResolvedCodeQLPack) -> ResolvedCodeQLPack: pack.path.parent, pack_copy_dir, ) - pack_copy_path = ( - pack_copy_dir / pack.path.name - ) + pack_copy_path = pack_copy_dir / pack.path.name return dataclasses.replace(pack, path=pack_copy_path) def add_customization_support(pack: ResolvedCodeQLPack): @@ -392,13 +465,11 @@ def add_customization_support(pack: ResolvedCodeQLPack): return logging.debug( - f"Standard library CodeQL pack {pack.config.name} does not have a 'Customizations' library, attempting to add one." - ) + f"Standard library CodeQL pack {pack.config.name} does not have a 'Customizations' library, attempting to add one." + ) # Assume the CodeQL library pack has name `-all`. target_language = pack.config.get_pack_name().removesuffix("-all") - target_language_library_path = ( - pack.path.parent / f"{target_language}.qll" - ) + target_language_library_path = pack.path.parent / f"{target_language}.qll" logging.debug( f"Looking for standard library language module {target_language_library_path.name}" ) @@ -434,9 +505,7 @@ def add_customization_support(pack: ResolvedCodeQLPack): f"Writing modified language library to {target_language_library_path}" ) - target_customization_library_path = ( - pack.path.parent / "Customizations.qll" - ) + target_customization_library_path = pack.path.parent / "Customizations.qll" logging.debug( f"Creating Customizations library with import of language {target_language}" ) @@ -489,14 +558,18 @@ def bundle_stdlib_pack(pack: ResolvedCodeQLPack): logging.debug( f"Bundling the standard library pack {pack_copy.config.name} at {pack_copy.path}" ) - self.codeql.pack_bundle(pack_copy, self.bundle_path / "qlpacks", disable_precompilation=self.disable_precompilation) + self.codeql.pack_bundle( + pack_copy, + self.bundle_path / "qlpacks", + disable_precompilation=self.disable_precompilation, + ) def bundle_library_pack(library_pack: ResolvedCodeQLPack): logging.info(f"Bundling the library pack {library_pack.config.name}.") self.codeql.pack_bundle( library_pack, self.bundle_path / "qlpacks", - disable_precompilation=self.disable_precompilation + disable_precompilation=self.disable_precompilation, ) def bundle_query_pack(pack: ResolvedCodeQLPack): @@ -538,9 +611,8 @@ def bundle_query_pack(pack: ResolvedCodeQLPack): ) # Recompile the query pack with the assumption that all its dependencies are now in the bundle. self.codeql.pack_create( - pack_copy, - self.bundle_path / "qlpacks", - self.bundle_path) + pack_copy, self.bundle_path / "qlpacks", self.bundle_path + ) else: logging.info(f"Bundling the query pack {pack.config.name}.") pack_copy = copy_pack(pack) @@ -549,17 +621,20 @@ def bundle_query_pack(pack: ResolvedCodeQLPack): qlpack_spec = yaml.safe_load(fd) # Assume there is only one dependency and it is the standard library. - qlpack_spec["dependencies"] = {pack.config.name: str(pack.config.version) for pack in pack_copy.dependencies} + qlpack_spec["dependencies"] = { + pack.config.name: str(pack.config.version) + for pack in pack_copy.dependencies + } logging.debug(f"Rewriting dependencies for {pack.config.name}.") with pack_copy.path.open("w") as fd: yaml.dump(qlpack_spec, fd) - self.codeql.pack_create( - pack_copy, - self.bundle_path / "qlpacks") + self.codeql.pack_create(pack_copy, self.bundle_path / "qlpacks") sorted_packs = list(pack_sorter.static_order()) - logger.debug(f"Sorted packs: {' -> '.join(map(lambda p: p.config.name, sorted_packs))}") + logger.debug( + f"Sorted packs: {' -> '.join(map(lambda p: p.config.name, sorted_packs))}" + ) for pack in sorted_packs: if pack.kind == CodeQLPackKind.CUSTOMIZATION_PACK: bundle_customization_pack(pack) @@ -571,6 +646,150 @@ def bundle_query_pack(pack: ResolvedCodeQLPack): elif pack.kind == CodeQLPackKind.QUERY_PACK: bundle_query_pack(pack) + def add_files_and_certs(self, config_path: Path, workspace_path: Path): + schema = { + "type": "object", + "properties": { + "CodeQLBundleAdditionalFiles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "Source": {"type": "string"}, + "Destination": {"type": "string"}, + }, + "required": ["Source", "Destination"], + "additionalProperties": False, + }, + "minItems": 1, + }, + "CodeQLBundleAdditionalCertificates": { + "type": "array", + "items": { + "type": "object", + "properties": {"Source": {"type": "string"}}, + "required": ["Source"], + "additionalProperties": False, + }, + "minItems": 1, + }, + }, + "additionalProperties": True, + } + + def validate_config(config) -> bool: + try: + validate(instance=config, schema=schema) + except ValidationError as e: + print("JSON validation error:", e) + return False + return True + + def load_config(file_path): + with open(file_path, "r") as file: + data = json.load(file) + if validate_config(data): + return data + else: + raise BundleException( + f"Installation config {file_path} is not valid." + ) + + def is_unsafe_path(basedir: Path, path: Path) -> bool: + matchpath = os.path.realpath(path) + basedir = os.path.realpath(basedir) + return os.path.commonpath([basedir, matchpath]) != basedir + + if not config_path.exists(): + raise BundleException(f"Installation config {config_path} does not exist.") + if not config_path.is_file(): + raise BundleException(f"Installation config {config_path} is not a file.") + + config = load_config(config_path) + + if platform.system() == "Windows": + keytool = "tools/win64/java/bin/keytool.exe" + elif platform.system() == "Linux": + keytool = "tools/linux64/java/bin/keytool" + elif platform.system() == "Darwin": + keytool = "tools/osx64/java/bin/keytool" + else: + raise BundleException(f"Unsupported platform {platform.system()}") + + keytool = self.bundle_path / keytool + if not keytool.exists(): + raise BundleException(f"Keytool {keytool} does not exist.") + + keystores: list[str] = [ + "tools/win64/java/lib/security/cacerts", + "tools/linux64/java/lib/security/cacerts", + "tools/osx64/java/lib/security/cacerts", + "tools/osx64/java-aarch64/lib/security/cacerts", + ] + + # Add the certificates to the Java keystores + if "CodeQLBundleAdditionalCertificates" in config: + for cert in config["CodeQLBundleAdditionalCertificates"]: + src = workspace_path / Path(cert["Source"]) + src = src.resolve() + if is_unsafe_path(workspace_path, src): + raise BundleException( + f"Certificate file {src} is not in the workspace path." + ) + if not src.exists(): + raise BundleException(f"Certificate file {src} does not exist.") + + for keystore in keystores: + keystore = self.bundle_path / keystore + if not keystore.exists(): + raise BundleException(f"Keystore {keystore} does not exist.") + logging.info(f"Adding certificate {src} to keystore {keystore}") + subprocess.run( + [ + str(keytool), + "-import", + "-trustcacerts", + "-alias", + "root", + "-file", + str(src), + "-keystore", + str(keystore), + "-storepass", + "changeit", + "-noprompt", + ], + check=True, + cwd=self.bundle_path, + ) + + # Add additional files to the bundle + if "CodeQLBundleAdditionalFiles" in config: + for file in config["CodeQLBundleAdditionalFiles"]: + src = (workspace_path / Path(file["Source"])).resolve() + dst = (self.bundle_path / Path(file["Destination"])).resolve() + + if not src.exists(): + raise BundleException(f"Source file {src} does not exist.") + + if is_unsafe_path(workspace_path, src): + raise BundleException( + f"Source file {src} is not in the workspace path." + ) + if is_unsafe_path(self.bundle_path, dst): + print(self.bundle_path) + raise BundleException( + f"Destination path {dst} is not in the bundle path." + ) + + if src.is_dir(): + logging.info(f"Copying directory {src} to {dst}") + shutil.copytree(src, dst, dirs_exist_ok=True) + else: + logging.info(f"Copying file {src} to {dst}") + dst.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(src, dst) + def add_code_scanning_config(self, default_config: Path): if not default_config.exists(): raise BundleException(f"Default config {default_config} does not exist.") @@ -578,7 +797,12 @@ def add_code_scanning_config(self, default_config: Path): raise BundleException(f"Default config {default_config} is not a file.") shutil.copy(default_config, self.bundle_path / "default-codeql-config.yml") - def bundle(self, output_path: Path, platforms: set[BundlePlatform] = set(), default_config : Optional[Path] = None): + def bundle( + self, + output_path: Path, + platforms: set[BundlePlatform] = set(), + default_config: Optional[Path] = None, + ): if len(platforms) == 0: if output_path.is_dir(): output_path = output_path / "codeql-bundle.tar.gz" @@ -598,34 +822,56 @@ def bundle(self, output_path: Path, platforms: set[BundlePlatform] = set(), defa f"Unsupported platform(s) {', '.join(map(str,unsupported_platforms))} specified. Use the platform agnostic bundle to bundle for different platforms." ) - def create_bundle_for_platform(bundle_output_path:Path, platform: BundlePlatform) -> None: + def create_bundle_for_platform( + bundle_output_path: Path, platform: BundlePlatform + ) -> None: """Create a bundle for a single platform.""" - def filter_for_platform(platform: BundlePlatform) -> Callable[[tarfile.TarInfo], Optional[tarfile.TarInfo]]: + + def filter_for_platform( + platform: BundlePlatform, + ) -> Callable[[tarfile.TarInfo], Optional[tarfile.TarInfo]]: """Create a filter function that will only include files for the specified platform.""" - relative_tools_paths = [Path(lang) / "tools" for lang in self.languages] + [Path("tools")] + relative_tools_paths = [ + Path(lang) / "tools" for lang in self.languages + ] + [Path("tools")] - def get_nonplatform_tool_paths(platform: BundlePlatform) -> List[Path]: + def get_nonplatform_tool_paths( + platform: BundlePlatform, + ) -> List[Path]: """Get a list of paths to tools that are not for the specified platform relative to the root of a bundle.""" - specialize_path : Optional[Callable[[Path], List[Path]]] = None + specialize_path: Optional[Callable[[Path], List[Path]]] = None linux64_subpaths = [Path("linux64"), Path("linux")] osx64_subpaths = [Path("osx64"), Path("macos")] win64_subpaths = [Path("win64"), Path("windows")] if platform == BundlePlatform.LINUX: - specialize_path = lambda p: [p / subpath for subpath in osx64_subpaths + win64_subpaths] + specialize_path = lambda p: [ + p / subpath + for subpath in osx64_subpaths + win64_subpaths + ] elif platform == BundlePlatform.WINDOWS: - specialize_path = lambda p: [p / subpath for subpath in osx64_subpaths + linux64_subpaths] + specialize_path = lambda p: [ + p / subpath + for subpath in osx64_subpaths + linux64_subpaths + ] elif platform == BundlePlatform.OSX: - specialize_path = lambda p: [p / subpath for subpath in linux64_subpaths + win64_subpaths] + specialize_path = lambda p: [ + p / subpath + for subpath in linux64_subpaths + win64_subpaths + ] else: raise BundleException(f"Unsupported platform {platform}.") - return [candidate for candidates in map(specialize_path, relative_tools_paths) for candidate in candidates] + return [ + candidate + for candidates in map(specialize_path, relative_tools_paths) + for candidate in candidates + ] def filter(tarinfo: tarfile.TarInfo) -> Optional[tarfile.TarInfo]: tarfile_path = Path(tarinfo.name) exclusion_paths = get_nonplatform_tool_paths(platform) - + # Manual exclusions based on diffing the contents of the platform specific bundles and the generated platform specific bundles. if platform != BundlePlatform.WINDOWS: exclusion_paths.append(Path("codeql.exe")) @@ -640,31 +886,48 @@ def filter(tarinfo: tarfile.TarInfo) -> Optional[tarfile.TarInfo]: if platform == BundlePlatform.OSX: exclusion_paths.append(Path("swift/qltest/linux64")) exclusion_paths.append(Path("swift/resource-dir/linux64")) - - - tarfile_path_root = Path(tarfile_path.parts[0]) - exclusion_paths = [tarfile_path_root / path for path in exclusion_paths] - if any(tarfile_path.is_relative_to(path) for path in exclusion_paths): + tarfile_path_root = Path(tarfile_path.parts[0]) + exclusion_paths = [ + tarfile_path_root / path for path in exclusion_paths + ] + + if any( + tarfile_path.is_relative_to(path) + for path in exclusion_paths + ): return None return tarinfo return filter - logging.debug(f"Bundling custom bundle for {platform} to {bundle_output_path}.") + + logging.debug( + f"Bundling custom bundle for {platform} to {bundle_output_path}." + ) with tarfile.open(bundle_output_path, mode="w:gz") as bundle_archive: bundle_archive.add( - self.bundle_path, arcname="codeql", filter=filter_for_platform(platform) + self.bundle_path, + arcname="codeql", + filter=filter_for_platform(platform), ) - with concurrent.futures.ThreadPoolExecutor(max_workers=len(platforms)) as executor: - future_to_platform = {executor.submit(create_bundle_for_platform, output_path / f"codeql-bundle-{platform}.tar.gz", platform): platform for platform in platforms} + with concurrent.futures.ThreadPoolExecutor( + max_workers=len(platforms) + ) as executor: + future_to_platform = { + executor.submit( + create_bundle_for_platform, + output_path / f"codeql-bundle-{platform}.tar.gz", + platform, + ): platform + for platform in platforms + } for future in concurrent.futures.as_completed(future_to_platform): platform = future_to_platform[future] try: future.result() except Exception as exc: - raise BundleException(f"Failed to create bundle for platform {platform} with exception: {exc}.") - - - + raise BundleException( + f"Failed to create bundle for platform {platform} with exception: {exc}." + ) diff --git a/poetry.lock b/poetry.lock index f963af2..63b3d8b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,34 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + [[package]] name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -13,45 +37,55 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "main" +name = "jsonschema" +version = "4.22.0" +description = "An implementation of JSON Schema validation for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.22.0-py3-none-any.whl", hash = "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802"}, + {file = "jsonschema-4.22.0.tar.gz", hash = "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7"}, +] -[[package]] -name = "semantic-version" -version = "2.10.0" -description = "A library implementing the 'SemVer' scheme." -category = "main" -optional = false -python-versions = ">=2.7" +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" [package.extras] -dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] -doc = ["Sphinx", "sphinx-rtd-theme"] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] -[metadata] -lock-version = "1.1" -python-versions = "^3.11" -content-hash = "533dfc8c46bfee4ba2d82fb0b5fa736eac11da15745ce10ed916ce456b8753ad" - -[metadata.files] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, ] -pyyaml = [ + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -93,7 +127,146 @@ pyyaml = [ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] -semantic-version = [ + +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] + +[[package]] +name = "semantic-version" +version = "2.10.0" +description = "A library implementing the 'SemVer' scheme." +optional = false +python-versions = ">=2.7" +files = [ {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, ] + +[package.extras] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1)", "coverage", "flake8", "nose2", "readme-renderer (<25.0)", "tox", "wheel", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "9602b58c5eec044f93ac2563400b8cbeb650c73af658e2ac91733dff25ba5645" diff --git a/pyproject.toml b/pyproject.toml index bc421d6..ba5b197 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ python = "^3.11" click = "^8.1.3" semantic-version = "^2.10.0" pyyaml = "^6.0" +jsonschema = "^4.22.0" [tool.poetry.scripts] codeql-bundle = 'codeql_bundle.cli:main' From 13e3037a6b41a10b79c27b5c294812a346c27f40 Mon Sep 17 00:00:00 2001 From: Nicolas Kraiouchkine Date: Wed, 8 May 2024 13:45:27 +0200 Subject: [PATCH 3/3] Bump version to 0.3.0 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ba5b197..5c386c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "codeql-bundle" -version = "0.2.0" +version = "0.3.0" description = "Tool to create custom CodeQL bundles" authors = ["Remco Vermeulen "] readme = "README.md"