Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ The documentation can be found on [github pages](https://dynatrace-extensions.gi

### Requirements:

* Python 3.10
* Python 3.10 is recommended
* Running on later Python versions is possible, but you have to adjust command lines, and simulate does not work

### Install the SDK

Expand All @@ -42,17 +43,27 @@ dt-sdk create my_first_extension

### Simulate

To simulate extension you need to run it on the same Python version as the framework, so Python 3.10

```bash
cd my_first_extension
dt-sdk run
```

### Build


On Python 3.10

```bash
dt-sdk build
```

On other versions of Python (e.g. 3.2) you need to specifically provide platforms that extension has to support, for example:

```bash
dt-sdk build -o -e win_amd64,manylinux1_x86_64
```


### Upload

Expand Down
9 changes: 5 additions & 4 deletions dynatrace_extension/cli/create/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def replace_placeholders(file: Path, replaces: list[tuple[str, str]]):
f.write(contents)


def copy_templates(source: Path, destination: Path, extension_name: str):
def copy_templates(source: Path, destination: Path, extension_name: str, python_version: str):
extension_name_lower = extension_name.lower()
extension_name_capitalize = extension_name.capitalize()
extension_name_dash = extension_name_lower.replace("_", "-")
Expand All @@ -34,6 +34,7 @@ def copy_templates(source: Path, destination: Path, extension_name: str):
("%extension_name%", extension_name_lower),
("%extension-name%", extension_name_dash),
("%extension-prefix%", extension_prefix),
("%python_version%", python_version),
]
for file in source.iterdir():
if file.is_dir() and file.name != "__pycache__":
Expand All @@ -43,7 +44,7 @@ def copy_templates(source: Path, destination: Path, extension_name: str):
dir_name = extension_name
dest_dir = Path(destination, dir_name)
dest_dir.mkdir(mode=output_mode_folder, exist_ok=True)
copy_templates(file, dest_dir, extension_name)
copy_templates(file, dest_dir, extension_name, python_version)

elif file.is_file() and file.name.endswith(".template"):
# If it is a file, copy and process it
Expand All @@ -66,10 +67,10 @@ def is_pep8_compliant(extension_name: str) -> bool:
return True


def generate_extension(extension_name: str, output: Path) -> Path:
def generate_extension(extension_name: str, output: Path, python_version: str) -> Path:
output.mkdir(mode=output_mode_folder, exist_ok=True, parents=True)
try:
copy_templates(template_base_folder / "extension_template", output, extension_name)
copy_templates(template_base_folder / "extension_template", output, extension_name, python_version)
return output.resolve()
except Exception:
shutil.rmtree(output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ setup(
description="%Extension_Name% python EF2 extension",
author="Dynatrace",
packages=find_packages(),
python_requires=">=3.10",
python_requires="%python_version%",
include_package_data=True,
install_requires=["dt-extensions-sdk"],
extras_require={"dev": ["dt-extensions-sdk[cli]"]},
Expand Down
66 changes: 54 additions & 12 deletions dynatrace_extension/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
app = typer.Typer(pretty_exceptions_show_locals=False, pretty_exceptions_enable=False)
console = Console()

# if we are not python 3.10.X, exit with an error
if sys.version_info < (3, 10) or sys.version_info >= (3, 11):
console.print(f"Python 3.10.X is required to build extensions, you are using {sys.version_info}", style="bold red")
sys.exit(1)

CERT_DIR_ENVIRONMENT_VAR = "DT_CERTIFICATES_FOLDER"
CERTIFICATE_DEFAULT_PATH = Path.home() / ".dynatrace" / "certificates"

DEFAULT_TARGET_PYTHON_VERSION = "310"
USABLE_TARGET_PYTHON_VERSIONS = ["310"]


def python_version_in_usable_versions(versions: str):
for local_version in versions.split(","):
if local_version not in USABLE_TARGET_PYTHON_VERSIONS:
return False
return True


# show version
@app.command()
Expand Down Expand Up @@ -57,6 +62,12 @@ def run(
:param print_metrics: If true, print metrics to the console
"""

if sys.version_info < (3, 10) or sys.version_info >= (3, 11):
console.print(
f"Extensions are run on {DEFAULT_TARGET_PYTHON_VERSION}, you are using {sys.version_info}", style="bold red"
)
sys.exit(1)

# This parses the yaml, which validates it before running
extension_yaml = ExtensionYaml(extension_dir / "extension/extension.yaml")
try:
Expand Down Expand Up @@ -106,6 +117,12 @@ def build(
"-o",
help="Only build for the extra platforms, useful when building from arm64 (mac)",
),
python_version: str = typer.Option(
DEFAULT_TARGET_PYTHON_VERSION,
"--python-version",
"-p",
help="Python version to use to downloads wheels, e.g. 310",
),
):
"""
Builds and signs an extension using the developer fused key-certificate
Expand All @@ -119,25 +136,30 @@ def build(
:param extra_index_url: Extra index url to use when downloading dependencies
:param find_links: Extra index url to use when downloading dependencies
:param only_extra_platforms: If true, only build for the extra platforms, useful when building from arm64
:param python_version: List of comma separated python tags to use to downloads wheels, e.g. 310,313. Used only when extra_platforms is specified
"""
if not python_version_in_usable_versions(python_version):
console.print(f"Currently you can only build extensions for Python versions {USABLE_TARGET_PYTHON_VERSIONS}")
sys.exit(1)

console.print(f"Building and signing extension from {extension_dir} to {target_directory}", style="cyan")
if target_directory is None:
target_directory = extension_dir / "dist"
if not target_directory.exists():
target_directory.mkdir()

console.print("Stage 1 - Download and build dependencies", style="bold blue")
wheel(extension_dir, extra_platforms, extra_index_url, find_links, only_extra_platforms)
wheel(extension_dir, extra_platforms, extra_index_url, find_links, only_extra_platforms, python_version)

console.print("Stage 2 - Create the extension zip file", style="bold blue")
console.print("Stage 4 - Create the extension zip file", style="bold blue")
built_zip = assemble(extension_dir, target_directory)

console.print("Stage 3 - Sign the extension", style="bold blue")
console.print("Stage 5 - Sign the extension", style="bold blue")
extension_yaml = ExtensionYaml(Path(extension_dir) / "extension" / "extension.yaml")
output = target_directory / extension_yaml.zip_file_name()
sign(built_zip, private_key, output)

console.print(f"Stage 4 - Delete {built_zip}", style="bold blue")
console.print(f"Stage 6 - Delete {built_zip}", style="bold blue")
built_zip.unlink()


Expand Down Expand Up @@ -202,6 +224,12 @@ def wheel(
"-o",
help="Only build for the extra platforms, useful when building from arm64 (mac)",
),
python_version: str = typer.Option(
DEFAULT_TARGET_PYTHON_VERSION,
"--python-versions",
"-p",
help="Python version to use to downloads wheels, e.g. 310",
),
):
"""
Builds the extension and it's dependencies into wheel files
Expand All @@ -212,7 +240,12 @@ def wheel(
:param extra_index_url: Extra index url to use when downloading dependencies
:param find_links: Extra index url to use when downloading dependencies
:param only_extra_platforms: If true, only build for the extra platforms, useful when building from arm64
:param python_version: List of comma separated python tags to use to downloads wheels, e.g. 310,313. Used only when extra_platforms is specified
"""
if not python_version_in_usable_versions(python_version):
console.print(f"Currently you can only build extensions for Python {USABLE_TARGET_PYTHON_VERSIONS}")
sys.exit(1)

relative_lib_folder_dir = "extension/lib"
lib_folder: Path = extension_dir / relative_lib_folder_dir
_clean_directory(lib_folder)
Expand All @@ -227,6 +260,7 @@ def wheel(
command.extend(["--find-links", find_links])
if only_extra_platforms:
command.append("--no-deps")
command.append("--ignore-requires-python")
command.append(".")
run_process(command, cwd=extension_dir)

Expand All @@ -243,6 +277,8 @@ def wheel(
"--only-binary=:all:",
"--platform",
extra_platform,
"--python-version",
f"{python_version}",
]
if extra_index_url:
command.extend(["--extra-index-url", extra_index_url])
Expand Down Expand Up @@ -405,12 +441,17 @@ def gencerts(


@app.command(help="Creates a new python extension")
def create(extension_name: str, output: Path = typer.Option(None, "--output", "-o")):
def create(
extension_name: str,
output: Path = typer.Option(None, "--output", "-o"),
python_version: str = typer.Option(f"{DEFAULT_TARGET_PYTHON_VERSION}", "--python-version", "-p"),
):
"""
Creates a new python extension

:param extension_name: The name of the extension
:param output: The path to the output directory, if not specified, we will use the extension name
:param python_version: version of python to build extension for, in format of 3XX (i.e. 310)
"""

if not is_pep8_compliant(extension_name):
Expand All @@ -421,7 +462,8 @@ def create(extension_name: str, output: Path = typer.Option(None, "--output", "-
output = Path.cwd() / extension_name
else:
output = output / extension_name
extension_path = generate_extension(extension_name, output)
effective_python_version = f"=={python_version[0]}.{python_version[1:]}"
extension_path = generate_extension(extension_name, output, effective_python_version)
console.print(f"Extension created at {extension_path}", style="bold green")


Expand Down Expand Up @@ -481,7 +523,7 @@ def run_process(command: list[str], cwd: Path | None = None, env: dict | None =
console.print(print_message, style="cyan")
else:
console.print(f"Running: {friendly_command}", style="cyan")
return subprocess.run(command, cwd=cwd, env=env, check=True) # noqa: S603
return subprocess.run(command, cwd=cwd, env=env, check=True, capture_output=True) # noqa: S603


def _clean_directory(directory: Path):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "dt-extensions-sdk"
dynamic = ["version"]
description = ''
readme = "README.md"
requires-python = ">=3.10,<3.11"
requires-python = ">=3.10"
license = "MIT"
keywords = []
authors = [
Expand Down