-
Notifications
You must be signed in to change notification settings - Fork 29
feat: add python as a supported build tool #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e4fab89
09926fc
de62f78
6f7d867
b52061f
9527cd5
f9555d7
9173323
343612e
2be2612
da5cc6a
67fc25a
5b27054
8165079
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
# Copyright (c) 2022 - 2022, Oracle and/or its affiliates. All rights reserved. | ||
# Copyright (c) 2022 - 2023, Oracle and/or its affiliates. All rights reserved. | ||
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/. | ||
|
||
"""The build_tool package contains the supported build tools for Macaron.""" | ||
|
||
from .base_build_tool import BaseBuildTool | ||
from .gradle import Gradle | ||
from .maven import Maven | ||
from .pip import Pip | ||
from .poetry import Poetry | ||
|
||
# The list of supported build tools. The order of the list determine the order | ||
# in which each build tool is checked against the target repository. | ||
BUILD_TOOLS: list[BaseBuildTool] = [Gradle(), Maven()] | ||
BUILD_TOOLS: list[BaseBuildTool] = [Gradle(), Maven(), Poetry(), Pip()] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Copyright (c) 2023 - 2023, Oracle and/or its affiliates. All rights reserved. | ||
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/. | ||
|
||
"""This module contains the Pip class which inherits BaseBuildTool. | ||
|
||
This module is used to work with repositories that use pip for dependency management. | ||
""" | ||
|
||
import logging | ||
|
||
from macaron.config.defaults import defaults | ||
from macaron.dependency_analyzer import DependencyAnalyzer, NoneDependencyAnalyzer | ||
from macaron.slsa_analyzer.build_tool.base_build_tool import BaseBuildTool, file_exists | ||
|
||
logger: logging.Logger = logging.getLogger(__name__) | ||
|
||
|
||
class Pip(BaseBuildTool): | ||
"""This class contains the information of the pip build tool.""" | ||
|
||
def __init__(self) -> None: | ||
"""Initialize instance.""" | ||
super().__init__(name="pip") | ||
|
||
def load_defaults(self) -> None: | ||
"""Load the default values from defaults.ini.""" | ||
if "builder.pip" in defaults: | ||
for item in defaults["builder.pip"]: | ||
if hasattr(self, item): | ||
setattr(self, item, defaults.get_list("builder.pip", item)) | ||
|
||
if "builder.pip.ci.deploy" in defaults: | ||
for item in defaults["builder.pip.ci.deploy"]: | ||
if item in self.ci_deploy_kws: | ||
self.ci_deploy_kws[item] = defaults.get_list("builder.pip.ci.deploy", item) | ||
|
||
def is_detected(self, repo_path: str) -> bool: | ||
"""Return True if this build tool is used in the target repo. | ||
|
||
Parameters | ||
---------- | ||
repo_path : str | ||
The path to the target repo. | ||
|
||
Returns | ||
------- | ||
bool | ||
True if this build tool is detected, else False. | ||
""" | ||
for file in self.build_configs: | ||
if file_exists(repo_path, file): | ||
return True | ||
return False | ||
|
||
def prepare_config_files(self, wrapper_path: str, build_dir: str) -> bool: | ||
"""Prepare the necessary wrapper files for running the build. | ||
|
||
This method returns False on errors. Pip doesn't require any preparation, therefore this method always | ||
returns True. | ||
|
||
Parameters | ||
---------- | ||
wrapper_path : str | ||
The path where all necessary wrapper files are located. | ||
build_dir : str | ||
The path of the build dir. This is where all files are copied to. | ||
|
||
Returns | ||
------- | ||
bool | ||
True if succeed else False. | ||
""" | ||
return True | ||
|
||
def get_dep_analyzer(self, repo_path: str) -> DependencyAnalyzer: | ||
"""Create a DependencyAnalyzer for the build tool. | ||
|
||
Parameters | ||
---------- | ||
repo_path: str | ||
The path to the target repo. | ||
|
||
Returns | ||
------- | ||
DependencyAnalyzer | ||
The DependencyAnalyzer object. | ||
""" | ||
# TODO: Implement this method. | ||
return NoneDependencyAnalyzer() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a TODO to implement it later? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 9c167f2. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Copyright (c) 2023 - 2023, Oracle and/or its affiliates. All rights reserved. | ||
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/. | ||
|
||
"""This module contains the Poetry class which inherits BaseBuildTool. | ||
|
||
This module is used to work with repositories that use Poetry for dependency management. | ||
""" | ||
|
||
import glob | ||
import logging | ||
import os | ||
import tomllib | ||
from pathlib import Path | ||
|
||
from macaron.config.defaults import defaults | ||
from macaron.dependency_analyzer import DependencyAnalyzer, NoneDependencyAnalyzer | ||
from macaron.slsa_analyzer.build_tool.base_build_tool import BaseBuildTool, file_exists | ||
|
||
logger: logging.Logger = logging.getLogger(__name__) | ||
|
||
|
||
class Poetry(BaseBuildTool): | ||
"""This class contains the information of the poetry build tool.""" | ||
|
||
def __init__(self) -> None: | ||
"""Initialize instance.""" | ||
super().__init__(name="poetry") | ||
|
||
def load_defaults(self) -> None: | ||
"""Load the default values from defaults.ini.""" | ||
if "builder.poetry" in defaults: | ||
for item in defaults["builder.poetry"]: | ||
if hasattr(self, item): | ||
setattr(self, item, defaults.get_list("builder.poetry", item)) | ||
|
||
if "builder.pip.ci.deploy" in defaults: | ||
for item in defaults["builder.pip.ci.deploy"]: | ||
if item in self.ci_deploy_kws: | ||
self.ci_deploy_kws[item] = defaults.get_list("builder.pip.ci.deploy", item) | ||
|
||
def is_detected(self, repo_path: str) -> bool: | ||
"""Return True if this build tool is used in the target repo. | ||
|
||
Parameters | ||
---------- | ||
repo_path : str | ||
The path to the target repo. | ||
|
||
Returns | ||
------- | ||
bool | ||
True if this build tool is detected, else False. | ||
""" | ||
package_lock_exists = "" | ||
for file in self.package_lock: | ||
if file_exists(repo_path, file): | ||
package_lock_exists = file | ||
break | ||
|
||
for conf in self.build_configs: | ||
# Find the paths of all pyproject.toml files. | ||
pattern = os.path.join(repo_path, "**", conf) | ||
files_detected = glob.glob(pattern, recursive=True) | ||
|
||
if files_detected: | ||
# If a package_lock file exists, and a config file is present, Poetry build tool is detected. | ||
if package_lock_exists: | ||
return True | ||
# TODO: this implementation assumes one build type, so when multiple build types are supported, this | ||
# needs to be updated. | ||
# Take the highest level file, if there are two at the same level, take the first in the list. | ||
file_path = min(files_detected, key=lambda x: len(Path(x).parts)) | ||
try: | ||
# Parse the .toml file | ||
with open(file_path, "rb") as toml_file: | ||
behnazh-w marked this conversation as resolved.
Show resolved
Hide resolved
|
||
try: | ||
data = tomllib.load(toml_file) | ||
# Check for the existence of a [tool.poetry] section. | ||
if ("tool" in data) and ("poetry" in data["tool"]): | ||
return True | ||
except tomllib.TOMLDecodeError: | ||
logger.error("Failed to read the %s file: invalid toml file.", conf) | ||
return False | ||
return False | ||
behnazh-w marked this conversation as resolved.
Show resolved
Hide resolved
|
||
except FileNotFoundError: | ||
logger.error("Failed to read the %s file.", conf) | ||
return False | ||
|
||
return False | ||
|
||
def prepare_config_files(self, wrapper_path: str, build_dir: str) -> bool: | ||
"""Prepare the necessary wrapper files for running the build. | ||
|
||
This method returns False on errors. Poetry doesn't require any preparation, therefore this method always | ||
returns True. | ||
|
||
Parameters | ||
---------- | ||
wrapper_path : str | ||
The path where all necessary wrapper files are located. | ||
build_dir : str | ||
The path of the build dir. This is where all files are copied to. | ||
|
||
Returns | ||
------- | ||
bool | ||
True if succeeds else False. | ||
""" | ||
return True | ||
|
||
def get_dep_analyzer(self, repo_path: str) -> DependencyAnalyzer: | ||
"""Create a DependencyAnalyzer for the build tool. | ||
|
||
Parameters | ||
---------- | ||
repo_path: str | ||
The path to the target repo. | ||
|
||
Returns | ||
------- | ||
DependencyAnalyzer | ||
The DependencyAnalyzer object. | ||
""" | ||
# TODO: Implement this method. | ||
return NoneDependencyAnalyzer() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as pip. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 9c167f2. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a comment that
pip
does not require any preparation?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 9c167f2.