spl-core 7.14.0rc4.dev3__tar.gz → 7.15.0rc1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/PKG-INFO +2 -1
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/pyproject.toml +3 -2
- spl_core-7.15.0rc1/src/spl_core/__init__.py +1 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/common.cmake +23 -1
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/artifacts_archiver.py +183 -22
- spl_core-7.15.0rc1/src/spl_core/test_utils/junit_merger.py +122 -0
- spl_core-7.14.0rc4.dev3/src/spl_core/__init__.py +0 -1
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/LICENSE +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/README.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/__run.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/common/__init__.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/common/path.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/conan.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/config/KConfig +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/gcov_maid/__init__.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/gcov_maid/gcov_maid.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kconfig/__init__.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kconfig/kconfig.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kconfig.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/__init__.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/create.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/.vscode/cmake-variants.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/KConfig +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/CMakeLists.txt +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/doc/_images/screenshot.png +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/doc/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/src/greeter.c +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/src/greeter.h +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/greeter/test/test_greeter.cc +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/main/CMakeLists.txt +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/main/doc/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/src/main/src/main.c +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/test/EnglishVariant/test__EnglishVariant.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/test/German/test__GermanVariant.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/variants/EnglishVariant/config.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/variants/EnglishVariant/parts.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/variants/GermanVariant/config.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/variants/GermanVariant/config.txt +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/variants/GermanVariant/parts.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.gitignore +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.vscode/cmake-kits.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.vscode/extensions.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.vscode/launch.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.vscode/settings.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.vscode/tasks.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/CMakeLists.txt +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/README.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/bootstrap.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/build.bat +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/build.ps1 +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/conf.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/Doxyfile.in +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/common/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/components/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/doxygen-awesome/LICENSE +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/doxygen-awesome/doxygen-awesome.css +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/software_architecture/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/software_requirements/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/doc/test_report_template.txt +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/index.md +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/pypeline.yaml +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/pyproject.toml +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/pytest.ini +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/scoopfile.json +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/tools/toolchains/clang/toolchain.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/tools/toolchains/gcc/toolchain.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/main.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/spl.cmake +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/steps/collect_pr_changes.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/archive_artifacts_collection.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/base_variant_test_runner.py +0 -0
- {spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/spl_build.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spl-core
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.15.0rc1
|
|
4
4
|
Summary: Software Product Line Support for CMake
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -20,6 +20,7 @@ Requires-Dist: cookiecutter (==2.6.0)
|
|
|
20
20
|
Requires-Dist: doxysphinx (>=3.3,<4.0)
|
|
21
21
|
Requires-Dist: gcovr (>=8.3,<9.0)
|
|
22
22
|
Requires-Dist: hammocking (>=0.8,<0.10)
|
|
23
|
+
Requires-Dist: junitparser
|
|
23
24
|
Requires-Dist: kconfiglib (>=14.1,<15.0)
|
|
24
25
|
Requires-Dist: mlx-traceability (>=10,<12)
|
|
25
26
|
Requires-Dist: myst-parser (>=0.16)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "spl-core"
|
|
3
|
-
version = "7.
|
|
3
|
+
version = "7.15.0-rc.1"
|
|
4
4
|
description = "Software Product Line Support for CMake"
|
|
5
5
|
authors = ["Avengineers <karsten.guenther@kamg.de>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -18,6 +18,7 @@ packages = [{ include = "spl_core", from = "src" }]
|
|
|
18
18
|
|
|
19
19
|
[tool.poetry.scripts]
|
|
20
20
|
please = "spl_core.main:main"
|
|
21
|
+
junit_merger = "spl_core.test_utils.junit_merger:main"
|
|
21
22
|
|
|
22
23
|
[tool.poetry.urls]
|
|
23
24
|
"Bug Tracker" = "https://github.com/avengineers/spl-core/issues"
|
|
@@ -49,6 +50,7 @@ sphinx-design = ">=0.5,<0.7"
|
|
|
49
50
|
pypeline-semantic-release = ">=0.4.1,<=0.5.0"
|
|
50
51
|
pypeline-runner = ">=1,<=2"
|
|
51
52
|
py7zr = "^1.0.0"
|
|
53
|
+
junitparser = "*"
|
|
52
54
|
|
|
53
55
|
[tool.poetry.group.dev.dependencies]
|
|
54
56
|
pytest = ">=7,<9"
|
|
@@ -57,7 +59,6 @@ pre-commit = "^3.1.1"
|
|
|
57
59
|
ruff = ">=0.5,<0.13"
|
|
58
60
|
jinja2 = "*"
|
|
59
61
|
testfixtures = "*"
|
|
60
|
-
junitparser = "*"
|
|
61
62
|
mashumaro = "*"
|
|
62
63
|
loguru = "*"
|
|
63
64
|
flake8 = "*"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "7.15.0-rc.1"
|
|
@@ -606,15 +606,36 @@ set(COV_OUT_JSON coverage.json)
|
|
|
606
606
|
function(_spl_coverage_create_overall_report)
|
|
607
607
|
if(_SPL_COVERAGE_CREATE_OVERALL_REPORT_IS_NECESSARY)
|
|
608
608
|
set(COV_OUT_VARIANT_HTML reports/coverage/index.html)
|
|
609
|
+
set(COV_OUT_VARIANT_JSON variant-coverage.json)
|
|
610
|
+
set(JUNIT_OUT_VARIANT_XML variant-junit.xml)
|
|
611
|
+
|
|
612
|
+
# Generate variant-level merged coverage JSON
|
|
613
|
+
add_custom_command(
|
|
614
|
+
OUTPUT ${COV_OUT_VARIANT_JSON}
|
|
615
|
+
COMMAND gcovr --root ${CMAKE_SOURCE_DIR} --add-tracefile \"${CMAKE_CURRENT_BINARY_DIR}/**/${COV_OUT_JSON}\" --json --output ${COV_OUT_VARIANT_JSON}
|
|
616
|
+
DEPENDS ${GLOBAL_COMPONENTS_COVERAGE_JSON_LIST}
|
|
617
|
+
COMMENT "Generating variant-level merged coverage JSON ${COV_OUT_VARIANT_JSON} ..."
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
# Generate variant-level merged JUnit XML
|
|
621
|
+
add_custom_command(
|
|
622
|
+
OUTPUT ${JUNIT_OUT_VARIANT_XML}
|
|
623
|
+
COMMAND junit_merger --output ${JUNIT_OUT_VARIANT_XML} --inputs ${GLOBAL_COMPONENTS_JUNIT_XML_LIST}
|
|
624
|
+
DEPENDS ${GLOBAL_COMPONENTS_JUNIT_XML_LIST}
|
|
625
|
+
COMMENT "Generating variant-level merged JUnit XML ${JUNIT_OUT_VARIANT_XML} ..."
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Generate variant-level HTML coverage report
|
|
609
629
|
add_custom_command(
|
|
610
630
|
OUTPUT ${COV_OUT_VARIANT_HTML}
|
|
611
631
|
COMMAND gcovr --root ${CMAKE_SOURCE_DIR} --add-tracefile \"${CMAKE_CURRENT_BINARY_DIR}/**/${COV_OUT_JSON}\" --html --html-details --output ${COV_OUT_VARIANT_HTML}
|
|
612
632
|
DEPENDS ${GLOBAL_COMPONENTS_COVERAGE_JSON_LIST}
|
|
613
633
|
COMMENT "Generating overall code coverage report ${COV_OUT_VARIANT_HTML} ..."
|
|
614
634
|
)
|
|
635
|
+
|
|
615
636
|
add_custom_target(
|
|
616
637
|
unittests
|
|
617
|
-
DEPENDS coverage ${COV_OUT_VARIANT_HTML}
|
|
638
|
+
DEPENDS coverage ${COV_OUT_VARIANT_HTML} ${COV_OUT_VARIANT_JSON} ${JUNIT_OUT_VARIANT_XML}
|
|
618
639
|
)
|
|
619
640
|
add_custom_target(
|
|
620
641
|
coverage_overall_report
|
|
@@ -718,6 +739,7 @@ macro(_spl_add_test_suite COMPONENT_NAME PROD_SRC TEST_SOURCES)
|
|
|
718
739
|
DEPENDS ${exe_name}
|
|
719
740
|
)
|
|
720
741
|
|
|
742
|
+
set(GLOBAL_COMPONENTS_JUNIT_XML_LIST "${GLOBAL_COMPONENTS_JUNIT_XML_LIST};${CMAKE_CURRENT_BINARY_DIR}/${TEST_OUT_JUNIT}" CACHE INTERNAL "List of all ${TEST_OUT_JUNIT} files")
|
|
721
743
|
set(GLOBAL_COMPONENTS_COVERAGE_JSON_LIST "${GLOBAL_COMPONENTS_COVERAGE_JSON_LIST};${CMAKE_CURRENT_BINARY_DIR}/${COV_OUT_JSON}" CACHE INTERNAL "List of all ${COV_OUT_JSON} files")
|
|
722
744
|
|
|
723
745
|
# Create coverage results (coverage.json)
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/artifacts_archiver.py
RENAMED
|
@@ -3,9 +3,45 @@ import os
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from datetime import datetime, timezone
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Dict, List, Optional
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
7
|
|
|
8
8
|
import py7zr
|
|
9
|
+
from py_app_dev.core.logging import logger
|
|
10
|
+
from py_app_dev.core.subprocess import SubprocessExecutor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class BuildMetadata:
|
|
15
|
+
"""
|
|
16
|
+
Contains build metadata extracted from environment variables.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
branch_name: The branch name, PR identifier (e.g., "PR-123"), or tag name
|
|
20
|
+
build_number: The build number or "local_build"
|
|
21
|
+
is_tag: Whether this is a tag build
|
|
22
|
+
pr_number: The PR number (without "PR-" prefix) for pull request builds, None otherwise
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
branch_name: str
|
|
26
|
+
build_number: str
|
|
27
|
+
is_tag: bool
|
|
28
|
+
pr_number: Optional[str]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class GitMetadata:
|
|
33
|
+
"""
|
|
34
|
+
Contains git metadata extracted from environment variables or git commands.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
commit_id: The git commit SHA (full hash)
|
|
38
|
+
commit_message: The git commit message subject line (first line only)
|
|
39
|
+
repository_url: The git repository URL (from remote.origin.url)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
commit_id: Optional[str]
|
|
43
|
+
commit_message: Optional[str]
|
|
44
|
+
repository_url: Optional[str]
|
|
9
45
|
|
|
10
46
|
|
|
11
47
|
class ArtifactsArchive:
|
|
@@ -84,7 +120,7 @@ class ArtifactsArchive:
|
|
|
84
120
|
archive_path.unlink()
|
|
85
121
|
|
|
86
122
|
if not self.archive_artifacts:
|
|
87
|
-
|
|
123
|
+
logger.warning("No artifacts registered for archiving")
|
|
88
124
|
# Create empty 7z file
|
|
89
125
|
with py7zr.SevenZipFile(archive_path, "w") as archive:
|
|
90
126
|
pass
|
|
@@ -94,7 +130,7 @@ class ArtifactsArchive:
|
|
|
94
130
|
with py7zr.SevenZipFile(archive_path, "w") as archive:
|
|
95
131
|
for artifact in self.archive_artifacts:
|
|
96
132
|
if not artifact.absolute_path.exists():
|
|
97
|
-
|
|
133
|
+
logger.warning(f"Artifact {artifact.absolute_path} does not exist, skipping")
|
|
98
134
|
continue
|
|
99
135
|
|
|
100
136
|
try:
|
|
@@ -104,13 +140,13 @@ class ArtifactsArchive:
|
|
|
104
140
|
# py7zr can handle directories directly
|
|
105
141
|
archive.writeall(artifact.absolute_path, arcname=str(artifact.archive_path))
|
|
106
142
|
except Exception as file_error:
|
|
107
|
-
|
|
143
|
+
logger.warning(f"Failed to add {artifact.absolute_path} to archive: {file_error}")
|
|
108
144
|
continue
|
|
109
145
|
|
|
110
|
-
|
|
146
|
+
logger.info(f"7z file created at: {archive_path}")
|
|
111
147
|
return archive_path
|
|
112
148
|
except Exception as e:
|
|
113
|
-
|
|
149
|
+
logger.error(f"Error creating artifacts 7z file: {e}")
|
|
114
150
|
raise e
|
|
115
151
|
|
|
116
152
|
|
|
@@ -120,10 +156,10 @@ class ArtifactsArchiver:
|
|
|
120
156
|
It provides a unified interface for registering artifacts to different archives.
|
|
121
157
|
"""
|
|
122
158
|
|
|
123
|
-
def __init__(self) -> None:
|
|
159
|
+
def __init__(self, artifactory_base_url: Optional[str] = None) -> None:
|
|
124
160
|
self.archives: Dict[str, ArtifactsArchive] = {}
|
|
125
161
|
self._target_repos: Dict[str, str] = {}
|
|
126
|
-
|
|
162
|
+
self.artifactory_base_url = artifactory_base_url
|
|
127
163
|
|
|
128
164
|
def add_archive(self, out_dir: Path, archive_filename: str, target_repo: Optional[str] = None, archive_name: str = "default") -> ArtifactsArchive:
|
|
129
165
|
"""
|
|
@@ -190,7 +226,7 @@ class ArtifactsArchiver:
|
|
|
190
226
|
The full Artifactory URL for the archive, or None if no target repo configured
|
|
191
227
|
|
|
192
228
|
Example:
|
|
193
|
-
"https://artifactory.
|
|
229
|
+
"https://artifactory.example.com/artifactory/my-repo/results/develop/123/result.7z"
|
|
194
230
|
"""
|
|
195
231
|
if archive_name not in self.archives:
|
|
196
232
|
return None
|
|
@@ -198,12 +234,15 @@ class ArtifactsArchiver:
|
|
|
198
234
|
if archive_name not in self._target_repos:
|
|
199
235
|
return None
|
|
200
236
|
|
|
237
|
+
if self.artifactory_base_url is None:
|
|
238
|
+
return None
|
|
239
|
+
|
|
201
240
|
archive = self.archives[archive_name]
|
|
202
241
|
target_repo = self._target_repos[archive_name]
|
|
203
|
-
|
|
242
|
+
metadata = self._get_build_metadata()
|
|
204
243
|
|
|
205
244
|
# Construct the URL following the same pattern as create_rt_upload_json
|
|
206
|
-
archive_url = f"
|
|
245
|
+
archive_url = f"{self.artifactory_base_url}/{target_repo}/{metadata.branch_name}/{metadata.build_number}/{archive.archive_name}"
|
|
207
246
|
|
|
208
247
|
return archive_url
|
|
209
248
|
|
|
@@ -245,7 +284,7 @@ class ArtifactsArchiver:
|
|
|
245
284
|
return 28 # 4 weeks for PRs, feature branches, and other branches
|
|
246
285
|
|
|
247
286
|
@staticmethod
|
|
248
|
-
def _get_build_metadata() ->
|
|
287
|
+
def _get_build_metadata() -> BuildMetadata:
|
|
249
288
|
"""
|
|
250
289
|
Get build metadata from environment variables or defaults.
|
|
251
290
|
|
|
@@ -253,14 +292,16 @@ class ArtifactsArchiver:
|
|
|
253
292
|
to local development defaults.
|
|
254
293
|
|
|
255
294
|
Returns:
|
|
256
|
-
|
|
257
|
-
- branch_name: The branch
|
|
295
|
+
BuildMetadata instance containing:
|
|
296
|
+
- branch_name: The branch name, PR identifier (e.g., "PR-123"), or tag name
|
|
258
297
|
- build_number: The build number or "local_build"
|
|
259
298
|
- is_tag: Whether this is a tag build
|
|
299
|
+
- pr_number: The PR number (without "PR-" prefix) for pull requests, None otherwise
|
|
260
300
|
"""
|
|
261
301
|
branch_name = "local_branch"
|
|
262
302
|
build_number = "local_build"
|
|
263
303
|
is_tag = False
|
|
304
|
+
pr_number = None
|
|
264
305
|
|
|
265
306
|
if os.environ.get("JENKINS_URL"):
|
|
266
307
|
change_id = os.environ.get("CHANGE_ID")
|
|
@@ -271,6 +312,7 @@ class ArtifactsArchiver:
|
|
|
271
312
|
if change_id:
|
|
272
313
|
# Pull request case
|
|
273
314
|
branch_name = f"PR-{change_id}"
|
|
315
|
+
pr_number = change_id
|
|
274
316
|
elif tag_name:
|
|
275
317
|
# Tag build case
|
|
276
318
|
branch_name = tag_name
|
|
@@ -282,7 +324,79 @@ class ArtifactsArchiver:
|
|
|
282
324
|
if jenkins_build_number:
|
|
283
325
|
build_number = jenkins_build_number
|
|
284
326
|
|
|
285
|
-
return
|
|
327
|
+
return BuildMetadata(
|
|
328
|
+
branch_name=branch_name,
|
|
329
|
+
build_number=build_number,
|
|
330
|
+
is_tag=is_tag,
|
|
331
|
+
pr_number=pr_number,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
@staticmethod
|
|
335
|
+
def _get_git_metadata() -> GitMetadata:
|
|
336
|
+
"""
|
|
337
|
+
Get git metadata from environment variables or git commands.
|
|
338
|
+
|
|
339
|
+
Attempts to retrieve git information in the following order:
|
|
340
|
+
1. Environment variables (GIT_COMMIT, GIT_URL) - typically set by Jenkins Git plugin
|
|
341
|
+
2. Git commands as fallback - executed locally using git CLI
|
|
342
|
+
|
|
343
|
+
The commit message captured is only the subject line (first line), not the full message.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
GitMetadata instance containing:
|
|
347
|
+
- commit_id: The git commit SHA, or None if unavailable
|
|
348
|
+
- commit_message: The commit subject line (first line only), or None if unavailable
|
|
349
|
+
- repository_url: The git repository URL, or None if unavailable
|
|
350
|
+
"""
|
|
351
|
+
commit_id = None
|
|
352
|
+
commit_message = None
|
|
353
|
+
repository_url = None
|
|
354
|
+
|
|
355
|
+
# Try environment variables first (Jenkins Git plugin)
|
|
356
|
+
env_commit = os.environ.get("GIT_COMMIT")
|
|
357
|
+
env_url = os.environ.get("GIT_URL")
|
|
358
|
+
|
|
359
|
+
if env_commit:
|
|
360
|
+
commit_id = env_commit if env_commit.strip() else None
|
|
361
|
+
if env_url:
|
|
362
|
+
repository_url = env_url if env_url.strip() else None
|
|
363
|
+
|
|
364
|
+
# Fallback to git commands if environment variables not available
|
|
365
|
+
# Get commit ID
|
|
366
|
+
if not commit_id:
|
|
367
|
+
try:
|
|
368
|
+
result = SubprocessExecutor(["git", "rev-parse", "HEAD"]).execute(handle_errors=False)
|
|
369
|
+
if result and result.returncode == 0:
|
|
370
|
+
value = result.stdout.strip()
|
|
371
|
+
commit_id = value if value else None
|
|
372
|
+
except Exception as e:
|
|
373
|
+
logger.warning(f"Failed to get commit ID from git: {e}")
|
|
374
|
+
|
|
375
|
+
# Get commit message (subject line only)
|
|
376
|
+
if not commit_message:
|
|
377
|
+
try:
|
|
378
|
+
result = SubprocessExecutor(["git", "log", "-1", "--format=%s"]).execute(handle_errors=False)
|
|
379
|
+
if result and result.returncode == 0:
|
|
380
|
+
value = result.stdout.strip()
|
|
381
|
+
commit_message = value if value else None
|
|
382
|
+
except Exception as e:
|
|
383
|
+
logger.warning(f"Failed to get commit message from git: {e}")
|
|
384
|
+
|
|
385
|
+
# Get repository URL
|
|
386
|
+
if not repository_url:
|
|
387
|
+
try:
|
|
388
|
+
result = SubprocessExecutor(["git", "config", "--get", "remote.origin.url"]).execute(handle_errors=False)
|
|
389
|
+
if result and result.returncode == 0:
|
|
390
|
+
value = result.stdout.strip()
|
|
391
|
+
repository_url = value if value else None
|
|
392
|
+
except Exception as e:
|
|
393
|
+
logger.warning(f"Failed to get repository URL from git: {e}")
|
|
394
|
+
|
|
395
|
+
return GitMetadata(
|
|
396
|
+
commit_id=commit_id,
|
|
397
|
+
commit_message=commit_message,
|
|
398
|
+
repository_url=repository_url,
|
|
399
|
+
)
|
|
286
400
|
|
|
287
401
|
def create_rt_upload_json(self, out_dir: Path) -> Path:
|
|
288
402
|
"""
|
|
@@ -299,10 +413,10 @@ class ArtifactsArchiver:
|
|
|
299
413
|
Path to the created rt-upload.json file
|
|
300
414
|
"""
|
|
301
415
|
# Get build metadata from environment or defaults
|
|
302
|
-
|
|
416
|
+
metadata = self._get_build_metadata()
|
|
303
417
|
|
|
304
418
|
# Calculate retention period based on branch/tag
|
|
305
|
-
retention_period = self.calculate_retention_period(branch_name, is_tag)
|
|
419
|
+
retention_period = self.calculate_retention_period(metadata.branch_name, metadata.is_tag)
|
|
306
420
|
|
|
307
421
|
# Create the files array for Artifactory upload format
|
|
308
422
|
files_array = []
|
|
@@ -312,7 +426,7 @@ class ArtifactsArchiver:
|
|
|
312
426
|
target_repo = self._target_repos[archive_name]
|
|
313
427
|
|
|
314
428
|
# Construct the RT target path
|
|
315
|
-
rt_target = f"{target_repo}/{branch_name}/{build_number}/"
|
|
429
|
+
rt_target = f"{target_repo}/{metadata.branch_name}/{metadata.build_number}/"
|
|
316
430
|
|
|
317
431
|
# Add this archive to the files array with retention_period property
|
|
318
432
|
files_array.append(
|
|
@@ -344,6 +458,17 @@ class ArtifactsArchiver:
|
|
|
344
458
|
but no artifacts. Use update_artifacts_json() to add artifact categories.
|
|
345
459
|
It uses Jenkins environment variables when available, otherwise falls back to default values.
|
|
346
460
|
|
|
461
|
+
The JSON file includes conditional keys based on the build type:
|
|
462
|
+
- For pull requests: includes "pull_request" key with the PR number (e.g., "117")
|
|
463
|
+
- For tag builds: includes "tag" key with the tag name (e.g., "v1.2.3")
|
|
464
|
+
- For regular branch builds: includes "branch" key with the branch name (e.g., "develop")
|
|
465
|
+
|
|
466
|
+
Optional fields (included only if available):
|
|
467
|
+
- build_url: Jenkins build URL from BUILD_URL environment variable
|
|
468
|
+
- commit_id: Git commit SHA from GIT_COMMIT env var or git rev-parse HEAD
|
|
469
|
+
- commit_message: Git commit subject line from git log (first line only)
|
|
470
|
+
- repository_url: Git repository URL from GIT_URL env var or git config
|
|
471
|
+
|
|
347
472
|
Args:
|
|
348
473
|
variant: The variant name (e.g., "Disco")
|
|
349
474
|
out_dir: Directory where the artifacts.json file will be created
|
|
@@ -358,11 +483,43 @@ class ArtifactsArchiver:
|
|
|
358
483
|
if not variant or not variant.strip():
|
|
359
484
|
raise ValueError("Variant name cannot be empty or None")
|
|
360
485
|
|
|
361
|
-
# Get
|
|
362
|
-
|
|
486
|
+
# Get metadata from environment or defaults
|
|
487
|
+
build_metadata = self._get_build_metadata()
|
|
488
|
+
git_metadata = self._get_git_metadata()
|
|
489
|
+
|
|
490
|
+
# Create the initial artifacts.json structure with base metadata
|
|
491
|
+
artifacts_data: Dict[str, Any] = {
|
|
492
|
+
"variant": variant,
|
|
493
|
+
"build_timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds") + "Z",
|
|
494
|
+
"build_number": build_metadata.build_number,
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# Add build_url if available
|
|
498
|
+
build_url = os.environ.get("BUILD_URL")
|
|
499
|
+
if build_url:
|
|
500
|
+
artifacts_data["build_url"] = build_url
|
|
501
|
+
|
|
502
|
+
# Add conditional keys based on build type
|
|
503
|
+
if build_metadata.pr_number:
|
|
504
|
+
# Pull request build
|
|
505
|
+
artifacts_data["pull_request"] = build_metadata.pr_number
|
|
506
|
+
elif build_metadata.is_tag:
|
|
507
|
+
# Tag build
|
|
508
|
+
artifacts_data["tag"] = build_metadata.branch_name
|
|
509
|
+
else:
|
|
510
|
+
# Regular branch build (or local build)
|
|
511
|
+
artifacts_data["branch"] = build_metadata.branch_name
|
|
512
|
+
|
|
513
|
+
# Add git metadata if available
|
|
514
|
+
if git_metadata.commit_id:
|
|
515
|
+
artifacts_data["commit_id"] = git_metadata.commit_id
|
|
516
|
+
if git_metadata.commit_message:
|
|
517
|
+
artifacts_data["commit_message"] = git_metadata.commit_message
|
|
518
|
+
if git_metadata.repository_url:
|
|
519
|
+
artifacts_data["repository_url"] = git_metadata.repository_url
|
|
363
520
|
|
|
364
|
-
#
|
|
365
|
-
artifacts_data
|
|
521
|
+
# Add empty artifacts dictionary
|
|
522
|
+
artifacts_data["artifacts"] = {}
|
|
366
523
|
|
|
367
524
|
# Create the artifacts.json file
|
|
368
525
|
json_path = out_dir / "artifacts.json"
|
|
@@ -487,6 +644,10 @@ class ArtifactsArchiver:
|
|
|
487
644
|
# out_dir = Path("./build/output")
|
|
488
645
|
#
|
|
489
646
|
# # Create initial artifacts.json file first, then add categories
|
|
647
|
+
# # The resulting JSON will contain conditional keys based on build type:
|
|
648
|
+
# # - For PRs: {"variant": "Disco", "build_timestamp": "...", "build_number": "123", "pull_request": "117", "artifacts": {}}
|
|
649
|
+
# # - For tags: {"variant": "Disco", "build_timestamp": "...", "build_number": "123", "tag": "v1.2.3", "artifacts": {}}
|
|
650
|
+
# # - For branches: {"variant": "Disco", "build_timestamp": "...", "build_number": "123", "branch": "develop", "artifacts": {}}
|
|
490
651
|
# artifacts_json_path = archiver.create_artifacts_json(variant, out_dir)
|
|
491
652
|
# archiver.update_artifacts_json("test_reports", test_reports, artifacts_json_path)
|
|
492
653
|
# archiver.update_artifacts_json("sca_reports", sca_reports, artifacts_json_path)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JUnit XML Merger
|
|
3
|
+
|
|
4
|
+
Merges multiple JUnit XML files into a single variant-level JUnit XML file.
|
|
5
|
+
This enables CI/CD tooling to consume aggregated test results from all components.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
from junitparser import JUnitXml
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class JUnitMergerError(Exception):
|
|
17
|
+
"""Exception raised when JUnit XML merging fails."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class JUnitMerger:
|
|
23
|
+
"""
|
|
24
|
+
Merges multiple JUnit XML files into a single variant-level JUnit XML file.
|
|
25
|
+
|
|
26
|
+
This class provides functionality to aggregate test results from multiple
|
|
27
|
+
components into a unified JUnit XML file for CI/CD tooling.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, input_files: List[str], output_file: str):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the JUnit merger.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
input_files: List of paths to input JUnit XML files
|
|
36
|
+
output_file: Path to output merged JUnit XML file
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
ValueError: If input_files list is empty
|
|
40
|
+
"""
|
|
41
|
+
if not input_files:
|
|
42
|
+
raise ValueError("No input files provided for merging")
|
|
43
|
+
|
|
44
|
+
self.input_files = input_files
|
|
45
|
+
self.output_file = output_file
|
|
46
|
+
|
|
47
|
+
def merge(self) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Merge all input JUnit XML files into the output file.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
FileNotFoundError: If any input file doesn't exist
|
|
53
|
+
Exception: If any input file contains malformed XML or validation fails
|
|
54
|
+
"""
|
|
55
|
+
# Create merged JUnit XML container
|
|
56
|
+
merged_xml = JUnitXml()
|
|
57
|
+
|
|
58
|
+
# Process each input file
|
|
59
|
+
for input_path in self.input_files:
|
|
60
|
+
input_file = Path(input_path)
|
|
61
|
+
|
|
62
|
+
if not input_file.exists():
|
|
63
|
+
raise FileNotFoundError(f"Input file not found: {input_path}")
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
# Parse the input JUnit XML file
|
|
67
|
+
xml = JUnitXml.fromfile(str(input_file))
|
|
68
|
+
|
|
69
|
+
# Add all test suites from this file to the merged result
|
|
70
|
+
for suite in xml:
|
|
71
|
+
merged_xml.add_testsuite(suite)
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
raise JUnitMergerError(f"Failed to parse JUnit XML file '{input_path}': {e}") from e
|
|
75
|
+
|
|
76
|
+
# Write merged result to output file
|
|
77
|
+
output_path = Path(self.output_file)
|
|
78
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
merged_xml.write(str(output_path))
|
|
80
|
+
|
|
81
|
+
# Validate the output
|
|
82
|
+
self._validate_output()
|
|
83
|
+
|
|
84
|
+
def _validate_output(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Validate that the merged output file is valid JUnit XML.
|
|
87
|
+
|
|
88
|
+
Raises:
|
|
89
|
+
Exception: If the output file cannot be parsed as valid JUnit XML
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
JUnitXml.fromfile(str(self.output_file))
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise JUnitMergerError(f"Validation failed: merged output is not valid JUnit XML: {e}") from e
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def main() -> int:
|
|
98
|
+
"""
|
|
99
|
+
Main entry point for command-line interface.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Exit code (0 for success, non-zero for failure)
|
|
103
|
+
"""
|
|
104
|
+
parser = argparse.ArgumentParser(description="Merge multiple JUnit XML files into a single variant-level file")
|
|
105
|
+
parser.add_argument("--output", required=True, help="Path to output merged JUnit XML file")
|
|
106
|
+
parser.add_argument("--inputs", nargs="+", required=True, help="Paths to input JUnit XML files to merge")
|
|
107
|
+
|
|
108
|
+
args = parser.parse_args()
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
merger = JUnitMerger(args.inputs, args.output)
|
|
112
|
+
merger.merge()
|
|
113
|
+
print(f"Successfully merged {len(args.inputs)} JUnit XML file(s) into {args.output}")
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
print(f"ERROR: Failed to merge JUnit XML files: {e}", file=sys.stderr)
|
|
118
|
+
return 1
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
sys.exit(main())
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "7.14.0-rc4.dev.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/application/KConfig
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/build.bat
RENAMED
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/build.ps1
RENAMED
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/conf.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/index.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/kickstart/templates/project/pytest.ini
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spl_core-7.14.0rc4.dev3 → spl_core-7.15.0rc1}/src/spl_core/test_utils/base_variant_test_runner.py
RENAMED
|
File without changes
|
|
File without changes
|