ScriptCollection 3.5.16__py3-none-any.whl → 4.0.78__py3-none-any.whl
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.
- ScriptCollection/AnionBuildPlatform.py +206 -0
- ScriptCollection/{UpdateCertificates.py → CertificateUpdater.py} +69 -46
- ScriptCollection/Executables.py +515 -18
- ScriptCollection/GeneralUtilities.py +1272 -873
- ScriptCollection/ImageUpdater.py +648 -0
- ScriptCollection/ProgramRunnerBase.py +10 -10
- ScriptCollection/ProgramRunnerMock.py +2 -0
- ScriptCollection/ProgramRunnerPopen.py +7 -1
- ScriptCollection/ProgramRunnerSudo.py +108 -0
- ScriptCollection/SCLog.py +115 -0
- ScriptCollection/ScriptCollectionCore.py +942 -266
- ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +95 -0
- ScriptCollection/TFCPS/Docker/__init__.py +0 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py +8 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py +6 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationNoGenerate.py +7 -0
- ScriptCollection/TFCPS/DotNet/TFCPS_CodeUnitSpecific_DotNet.py +485 -0
- ScriptCollection/TFCPS/DotNet/__init__.py +0 -0
- ScriptCollection/TFCPS/Flutter/TFCPS_CodeUnitSpecific_Flutter.py +130 -0
- ScriptCollection/TFCPS/Flutter/__init__.py +0 -0
- ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py +74 -0
- ScriptCollection/TFCPS/Go/__init__.py +0 -0
- ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py +131 -0
- ScriptCollection/TFCPS/NodeJS/__init__.py +0 -0
- ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +227 -0
- ScriptCollection/TFCPS/Python/__init__.py +0 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnitSpecific_Base.py +418 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnit.py +128 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnits.py +136 -0
- ScriptCollection/TFCPS/TFCPS_CreateRelease.py +95 -0
- ScriptCollection/TFCPS/TFCPS_Generic.py +43 -0
- ScriptCollection/TFCPS/TFCPS_MergeToMain.py +122 -0
- ScriptCollection/TFCPS/TFCPS_MergeToStable.py +350 -0
- ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py +47 -0
- ScriptCollection/TFCPS/TFCPS_Tools_General.py +1356 -0
- ScriptCollection/TFCPS/__init__.py +0 -0
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/METADATA +23 -22
- scriptcollection-4.0.78.dist-info/RECORD +43 -0
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/WHEEL +1 -1
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/entry_points.txt +32 -0
- ScriptCollection/ProgramRunnerEpew.py +0 -122
- ScriptCollection/RPStream.py +0 -42
- ScriptCollection/TasksForCommonProjectStructure.py +0 -2625
- ScriptCollection-3.5.16.dist-info/RECORD +0 -16
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/top_level.txt +0 -0
|
@@ -1,2625 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
from graphlib import TopologicalSorter
|
|
3
|
-
import os
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from functools import cmp_to_key
|
|
6
|
-
import shutil
|
|
7
|
-
import math
|
|
8
|
-
import re
|
|
9
|
-
import urllib.request
|
|
10
|
-
import zipfile
|
|
11
|
-
import json
|
|
12
|
-
import configparser
|
|
13
|
-
import tempfile
|
|
14
|
-
import uuid
|
|
15
|
-
import requests
|
|
16
|
-
from packaging import version
|
|
17
|
-
import xmlschema
|
|
18
|
-
from OpenSSL import crypto
|
|
19
|
-
from lxml import etree
|
|
20
|
-
from .GeneralUtilities import GeneralUtilities
|
|
21
|
-
from .ScriptCollectionCore import ScriptCollectionCore
|
|
22
|
-
from .ProgramRunnerEpew import ProgramRunnerEpew
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class CreateReleaseConfiguration():
|
|
26
|
-
projectname: str
|
|
27
|
-
remotename: str
|
|
28
|
-
artifacts_folder: str
|
|
29
|
-
push_artifacts_scripts_folder: str
|
|
30
|
-
verbosity: int
|
|
31
|
-
reference_repository_remote_name: str = None
|
|
32
|
-
reference_repository_branch_name: str = "main"
|
|
33
|
-
build_repository_branch: str = "main"
|
|
34
|
-
public_repository_url: str
|
|
35
|
-
additional_arguments_file: str = None
|
|
36
|
-
repository_folder_name: str = None
|
|
37
|
-
__sc: ScriptCollectionCore = None
|
|
38
|
-
|
|
39
|
-
def __init__(self, projectname: str, remotename: str, build_artifacts_target_folder: str, push_artifacts_scripts_folder: str, verbosity: int, repository_folder: str, additional_arguments_file: str, repository_folder_name: str):
|
|
40
|
-
|
|
41
|
-
self.__sc = ScriptCollectionCore()
|
|
42
|
-
self.projectname = projectname
|
|
43
|
-
self.remotename = remotename
|
|
44
|
-
self.artifacts_folder = build_artifacts_target_folder
|
|
45
|
-
self.push_artifacts_scripts_folder = push_artifacts_scripts_folder
|
|
46
|
-
self.verbosity = verbosity
|
|
47
|
-
if self.remotename is None:
|
|
48
|
-
self.public_repository_url = None
|
|
49
|
-
else:
|
|
50
|
-
self.public_repository_url = self.__sc.git_get_remote_url(repository_folder, remotename)
|
|
51
|
-
self.reference_repository_remote_name = self.remotename
|
|
52
|
-
self.additional_arguments_file = additional_arguments_file
|
|
53
|
-
self.repository_folder_name = repository_folder_name
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class CreateReleaseInformationForProjectInCommonProjectFormat:
|
|
57
|
-
projectname: str
|
|
58
|
-
repository: str
|
|
59
|
-
artifacts_folder: str
|
|
60
|
-
verbosity: int = 1
|
|
61
|
-
reference_repository: str = None
|
|
62
|
-
public_repository_url: str = None
|
|
63
|
-
target_branch_name: str = None
|
|
64
|
-
push_artifacts_scripts_folder: str = None
|
|
65
|
-
target_environmenttype_for_qualitycheck: str = "QualityCheck"
|
|
66
|
-
target_environmenttype_for_productive: str = "Productive"
|
|
67
|
-
additional_arguments_file: str = None
|
|
68
|
-
export_target: str = None
|
|
69
|
-
|
|
70
|
-
def __init__(self, repository: str, artifacts_folder: str, projectname: str, public_repository_url: str, target_branch_name: str, additional_arguments_file: str, export_target: str, push_artifacts_scripts_folder: str):
|
|
71
|
-
self.repository = repository
|
|
72
|
-
self.public_repository_url = public_repository_url
|
|
73
|
-
self.target_branch_name = target_branch_name
|
|
74
|
-
self.artifacts_folder = artifacts_folder
|
|
75
|
-
self.additional_arguments_file = additional_arguments_file
|
|
76
|
-
self.export_target = export_target
|
|
77
|
-
self.push_artifacts_scripts_folder = push_artifacts_scripts_folder
|
|
78
|
-
if projectname is None:
|
|
79
|
-
projectname = os.path.basename(self.repository)
|
|
80
|
-
else:
|
|
81
|
-
self.projectname = projectname
|
|
82
|
-
self.reference_repository = f"{repository}Reference"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class MergeToStableBranchInformationForProjectInCommonProjectFormat:
|
|
86
|
-
repository: str
|
|
87
|
-
sourcebranch: str = "main"
|
|
88
|
-
targetbranch: str = "stable"
|
|
89
|
-
sign_git_tags: bool = True
|
|
90
|
-
target_environmenttype_for_qualitycheck: str = "QualityCheck"
|
|
91
|
-
target_environmenttype_for_productive: str = "Productive"
|
|
92
|
-
additional_arguments_file: str = None
|
|
93
|
-
export_target: str = None
|
|
94
|
-
|
|
95
|
-
push_source_branch: bool = False
|
|
96
|
-
push_source_branch_remote_name: str = None
|
|
97
|
-
push_target_branch: bool = False
|
|
98
|
-
push_target_branch_remote_name: str = None
|
|
99
|
-
|
|
100
|
-
verbosity: int = 1
|
|
101
|
-
|
|
102
|
-
def __init__(self, repository: str, additional_arguments_file: str, export_target: str):
|
|
103
|
-
self.repository = repository
|
|
104
|
-
self.additional_arguments_file = additional_arguments_file
|
|
105
|
-
self.export_target = export_target
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class TasksForCommonProjectStructure:
|
|
109
|
-
__sc: ScriptCollectionCore = None
|
|
110
|
-
reference_latest_version_of_xsd_when_generating_xml: bool = True
|
|
111
|
-
validate_developers_of_repository: bool = True
|
|
112
|
-
dotnet_runsettings_file = "runsettings.xml"
|
|
113
|
-
|
|
114
|
-
@staticmethod
|
|
115
|
-
@GeneralUtilities.check_arguments
|
|
116
|
-
def get_development_environment_name() -> str:
|
|
117
|
-
return "Development"
|
|
118
|
-
|
|
119
|
-
@staticmethod
|
|
120
|
-
@GeneralUtilities.check_arguments
|
|
121
|
-
def get_qualitycheck_environment_name() -> str:
|
|
122
|
-
return "QualityCheck"
|
|
123
|
-
|
|
124
|
-
@staticmethod
|
|
125
|
-
@GeneralUtilities.check_arguments
|
|
126
|
-
def get_productive_environment_name() -> str:
|
|
127
|
-
return "Productive"
|
|
128
|
-
|
|
129
|
-
def __init__(self, sc: ScriptCollectionCore = None):
|
|
130
|
-
if sc is None:
|
|
131
|
-
sc = ScriptCollectionCore()
|
|
132
|
-
self.__sc = sc
|
|
133
|
-
|
|
134
|
-
@GeneralUtilities.check_arguments
|
|
135
|
-
def get_build_folder(self, repository_folder: str, codeunit_name: str) -> str:
|
|
136
|
-
return os.path.join(repository_folder, codeunit_name, "Other", "Build")
|
|
137
|
-
|
|
138
|
-
@GeneralUtilities.check_arguments
|
|
139
|
-
def get_artifacts_folder(self, repository_folder: str, codeunit_name: str) -> str:
|
|
140
|
-
return os.path.join(repository_folder, codeunit_name, "Other", "Artifacts")
|
|
141
|
-
|
|
142
|
-
@GeneralUtilities.check_arguments
|
|
143
|
-
def get_wheel_file(self, repository_folder: str, codeunit_name: str) -> str:
|
|
144
|
-
return self.__sc.find_file_by_extension(os.path.join(self.get_artifacts_folder(repository_folder, codeunit_name), "BuildResult_Wheel"), "whl")
|
|
145
|
-
|
|
146
|
-
@GeneralUtilities.check_arguments
|
|
147
|
-
def get_testcoverage_threshold_from_codeunit_file(self, codeunit_file: str):
|
|
148
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
149
|
-
return float(str(root.xpath('//cps:properties/cps:testsettings/@minimalcodecoverageinpercent', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0]))
|
|
150
|
-
|
|
151
|
-
@GeneralUtilities.check_arguments
|
|
152
|
-
def codeunit_has_testable_sourcecode(self, codeunit_file: str) -> bool:
|
|
153
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
154
|
-
return GeneralUtilities.string_to_boolean(str(root.xpath('//cps:properties/@codeunithastestablesourcecode', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0]))
|
|
155
|
-
|
|
156
|
-
@GeneralUtilities.check_arguments
|
|
157
|
-
def codeunit_throws_exception_if_codeunitfile_is_not_validatable(self, codeunit_file: str) -> bool:
|
|
158
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
159
|
-
return GeneralUtilities.string_to_boolean(str(root.xpath('//cps:properties/@throwexceptionifcodeunitfilecannotbevalidated', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0]))
|
|
160
|
-
|
|
161
|
-
@GeneralUtilities.check_arguments
|
|
162
|
-
def codeunit_has_updatable_dependencies(self, codeunit_file: str) -> bool:
|
|
163
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
164
|
-
return GeneralUtilities.string_to_boolean(str(root.xpath('//cps:properties/@codeunithasupdatabledependencies', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0]))
|
|
165
|
-
|
|
166
|
-
@GeneralUtilities.check_arguments
|
|
167
|
-
def get_codeunit_description(self, codeunit_file: str) -> bool:
|
|
168
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
169
|
-
return str(root.xpath('//cps:properties/@description', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0])
|
|
170
|
-
|
|
171
|
-
@GeneralUtilities.check_arguments
|
|
172
|
-
def check_testcoverage(self, testcoverage_file_in_cobertura_format: str, repository_folder: str, codeunitname: str) -> None:
|
|
173
|
-
GeneralUtilities.write_message_to_stdout("Check testcoverage..")
|
|
174
|
-
root: etree._ElementTree = etree.parse(testcoverage_file_in_cobertura_format)
|
|
175
|
-
if len(root.xpath('//coverage/packages/package')) != 1:
|
|
176
|
-
raise ValueError(f"'{testcoverage_file_in_cobertura_format}' must contain exactly 1 package.")
|
|
177
|
-
if root.xpath('//coverage/packages/package[1]/@name')[0] != codeunitname:
|
|
178
|
-
raise ValueError(f"The package name of the tested package in '{testcoverage_file_in_cobertura_format}' must be '{codeunitname}'.")
|
|
179
|
-
coverage_in_percent = round(float(str(root.xpath('//coverage/packages/package[1]/@line-rate')[0]))*100, 2)
|
|
180
|
-
technicalminimalrequiredtestcoverageinpercent = 0
|
|
181
|
-
if not technicalminimalrequiredtestcoverageinpercent < coverage_in_percent:
|
|
182
|
-
raise ValueError(f"The test-coverage of package '{codeunitname}' must be greater than {technicalminimalrequiredtestcoverageinpercent}%.")
|
|
183
|
-
codeunit_file = os.path.join(repository_folder, codeunitname, f"{codeunitname}.codeunit.xml")
|
|
184
|
-
minimalrequiredtestcoverageinpercent = self.get_testcoverage_threshold_from_codeunit_file(codeunit_file)
|
|
185
|
-
if (coverage_in_percent < minimalrequiredtestcoverageinpercent):
|
|
186
|
-
raise ValueError(f"The testcoverage for codeunit {codeunitname} must be {minimalrequiredtestcoverageinpercent}% or more but is {coverage_in_percent}%.")
|
|
187
|
-
|
|
188
|
-
@GeneralUtilities.check_arguments
|
|
189
|
-
def replace_version_in_python_file(self, file: str, new_version_value: str) -> None:
|
|
190
|
-
GeneralUtilities.write_text_to_file(file, re.sub("version = \"\\d+\\.\\d+\\.\\d+\"", f"version = \"{new_version_value}\"", GeneralUtilities.read_text_from_file(file)))
|
|
191
|
-
|
|
192
|
-
@GeneralUtilities.check_arguments
|
|
193
|
-
def standardized_tasks_run_testcases_for_python_codeunit(self, run_testcases_file: str, generate_badges: bool, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
194
|
-
codeunitname: str = Path(os.path.dirname(run_testcases_file)).parent.parent.name
|
|
195
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
196
|
-
repository_folder: str = str(Path(os.path.dirname(run_testcases_file)).parent.parent.parent.absolute())
|
|
197
|
-
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
198
|
-
self.__sc.run_program("coverage", f"run -m pytest -s ./{codeunitname}Tests", codeunit_folder, verbosity=verbosity)
|
|
199
|
-
self.__sc.run_program("coverage", "xml", codeunit_folder, verbosity=verbosity)
|
|
200
|
-
coveragefolder = os.path.join(repository_folder, codeunitname, "Other/Artifacts/TestCoverage")
|
|
201
|
-
GeneralUtilities.ensure_directory_exists(coveragefolder)
|
|
202
|
-
coveragefile = os.path.join(coveragefolder, "TestCoverage.xml")
|
|
203
|
-
GeneralUtilities.ensure_file_does_not_exist(coveragefile)
|
|
204
|
-
os.rename(os.path.join(repository_folder, codeunitname, "coverage.xml"), coveragefile)
|
|
205
|
-
self.run_testcases_common_post_task(repository_folder, codeunitname, verbosity, generate_badges, targetenvironmenttype, commandline_arguments)
|
|
206
|
-
|
|
207
|
-
@GeneralUtilities.check_arguments
|
|
208
|
-
def copy_source_files_to_output_directory(self, buildscript_file: str) -> None:
|
|
209
|
-
GeneralUtilities.write_message_to_stdout("Copy sourcecode...")
|
|
210
|
-
sc = ScriptCollectionCore()
|
|
211
|
-
folder = os.path.dirname(os.path.realpath(buildscript_file))
|
|
212
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", folder)
|
|
213
|
-
result = sc.run_program_argsasarray("git", ["ls-tree", "-r", "HEAD", "--name-only"], codeunit_folder)
|
|
214
|
-
files = [f for f in result[1].split('\n') if len(f) > 0]
|
|
215
|
-
for file in files:
|
|
216
|
-
full_source_file = os.path.join(codeunit_folder, file)
|
|
217
|
-
if os.path.isfile(full_source_file):
|
|
218
|
-
# Reson of isdir-check:
|
|
219
|
-
# Prevent trying to copy files which are not exist.
|
|
220
|
-
# Otherwise exceptions occurr because uncommitted deletions of files will result in an error here.
|
|
221
|
-
target_file = os.path.join(codeunit_folder, "Other", "Artifacts", "SourceCode", file)
|
|
222
|
-
target_folder = os.path.dirname(target_file)
|
|
223
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
224
|
-
shutil.copyfile(full_source_file, target_file)
|
|
225
|
-
|
|
226
|
-
@GeneralUtilities.check_arguments
|
|
227
|
-
def standardized_tasks_build_for_dart_project_in_common_project_structure(self, build_script_file: str, verbosity: int, targets: list[str], args: list[str], package_name: str):
|
|
228
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../../..", build_script_file)
|
|
229
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
230
|
-
src_folder = GeneralUtilities.resolve_relative_path(package_name, codeunit_folder) # TODO replace packagename
|
|
231
|
-
artifacts_folder = os.path.join(codeunit_folder, "Other", "Artifacts")
|
|
232
|
-
verbosity = self.get_verbosity_from_commandline_arguments(args, verbosity)
|
|
233
|
-
for target in targets:
|
|
234
|
-
GeneralUtilities.write_message_to_stdout(f"Build package {package_name} for target {target}...")
|
|
235
|
-
sc = ScriptCollectionCore()
|
|
236
|
-
self.run_with_epew("flutter", f"build {target}", src_folder, verbosity)
|
|
237
|
-
if target == "web":
|
|
238
|
-
web_relase_folder = os.path.join(src_folder, "build/web")
|
|
239
|
-
web_folder = os.path.join(artifacts_folder, "BuildResult_WebApplication")
|
|
240
|
-
GeneralUtilities.ensure_directory_does_not_exist(web_folder)
|
|
241
|
-
GeneralUtilities.ensure_directory_exists(web_folder)
|
|
242
|
-
GeneralUtilities.copy_content_of_folder(web_relase_folder, web_folder)
|
|
243
|
-
elif target == "windows":
|
|
244
|
-
windows_release_folder = os.path.join(src_folder, "build/windows/x64/runner/Release")
|
|
245
|
-
windows_folder = os.path.join(artifacts_folder, "BuildResult_Windows")
|
|
246
|
-
GeneralUtilities.ensure_directory_does_not_exist(windows_folder)
|
|
247
|
-
GeneralUtilities.ensure_directory_exists(windows_folder)
|
|
248
|
-
GeneralUtilities.copy_content_of_folder(windows_release_folder, windows_folder)
|
|
249
|
-
elif target == "ios":
|
|
250
|
-
raise ValueError("building for ios is not implemented yet")
|
|
251
|
-
elif target == "appbundle":
|
|
252
|
-
aab_folder = os.path.join(artifacts_folder, "BuildResult_AAB")
|
|
253
|
-
GeneralUtilities.ensure_directory_does_not_exist(aab_folder)
|
|
254
|
-
GeneralUtilities.ensure_directory_exists(aab_folder)
|
|
255
|
-
aab_relase_folder = os.path.join(src_folder, "build/app/outputs/bundle/release")
|
|
256
|
-
aab_file_original = self.__sc.find_file_by_extension(aab_relase_folder, "aab")
|
|
257
|
-
aab_file = os.path.join(aab_folder, f"{codeunit_name}.aab")
|
|
258
|
-
shutil.copyfile(aab_file_original, aab_file)
|
|
259
|
-
bundletool = os.path.join(codeunit_folder, "Other/Resources/AndroidAppBundleTool/bundletool.jar")
|
|
260
|
-
apk_folder = os.path.join(artifacts_folder, "BuildResult_APK")
|
|
261
|
-
GeneralUtilities.ensure_directory_does_not_exist(apk_folder)
|
|
262
|
-
GeneralUtilities.ensure_directory_exists(apk_folder)
|
|
263
|
-
apks_file = f"{apk_folder}/{codeunit_name}.apks"
|
|
264
|
-
sc.run_program("java", f"-jar {bundletool} build-apks --bundle={aab_file} --output={apks_file} --mode=universal", aab_relase_folder, verbosity)
|
|
265
|
-
with zipfile.ZipFile(apks_file, "r") as zip_ref:
|
|
266
|
-
zip_ref.extract("universal.apk", apk_folder)
|
|
267
|
-
GeneralUtilities.ensure_file_does_not_exist(apks_file)
|
|
268
|
-
os.rename(f"{apk_folder}/universal.apk", f"{apk_folder}/{codeunit_name}.apk")
|
|
269
|
-
else:
|
|
270
|
-
raise ValueError(f"Not supported target: {target}")
|
|
271
|
-
self.copy_source_files_to_output_directory(build_script_file)
|
|
272
|
-
|
|
273
|
-
@GeneralUtilities.check_arguments
|
|
274
|
-
def standardized_tasks_build_for_python_codeunit(self, buildscript_file: str, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
275
|
-
codeunitname: str = Path(os.path.dirname(buildscript_file)).parent.parent.name
|
|
276
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
277
|
-
codeunit_folder = str(Path(os.path.dirname(buildscript_file)).parent.parent.absolute())
|
|
278
|
-
repository_folder: str = str(Path(os.path.dirname(buildscript_file)).parent.parent.parent.absolute())
|
|
279
|
-
target_directory = GeneralUtilities.resolve_relative_path(
|
|
280
|
-
"../Artifacts/BuildResult_Wheel", os.path.join(self.get_artifacts_folder(repository_folder, codeunitname)))
|
|
281
|
-
GeneralUtilities.ensure_directory_exists(target_directory)
|
|
282
|
-
self.__sc.run_program("python", f"-m build --wheel --outdir {target_directory}", codeunit_folder, verbosity=verbosity)
|
|
283
|
-
self.generate_bom_for_python_project(verbosity, codeunit_folder, codeunitname, commandline_arguments)
|
|
284
|
-
self.copy_source_files_to_output_directory(buildscript_file)
|
|
285
|
-
|
|
286
|
-
@GeneralUtilities.check_arguments
|
|
287
|
-
def generate_bom_for_python_project(self, verbosity: int, codeunit_folder: str, codeunitname: str, commandline_arguments: list[str]) -> None:
|
|
288
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
289
|
-
codeunitversion = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
290
|
-
bom_folder = "Other/Artifacts/BOM"
|
|
291
|
-
bom_folder_full = os.path.join(codeunit_folder, bom_folder)
|
|
292
|
-
GeneralUtilities.ensure_directory_exists(bom_folder_full)
|
|
293
|
-
if not os.path.isfile(os.path.join(codeunit_folder, "requirements.txt")):
|
|
294
|
-
raise ValueError(f"Codeunit {codeunitname} does not have a 'requirements.txt'-file.")
|
|
295
|
-
# TODO check that all values from setup.cfg are contained in requirements.txt
|
|
296
|
-
result = self.__sc.run_program("cyclonedx-py", "requirements", codeunit_folder, verbosity=verbosity)
|
|
297
|
-
bom_file = os.path.join(codeunit_folder, f"{bom_folder}/{codeunitname}.{codeunitversion}.bom.json")
|
|
298
|
-
GeneralUtilities.ensure_file_exists(bom_file)
|
|
299
|
-
GeneralUtilities.write_text_to_file(bom_file, result[1])
|
|
300
|
-
|
|
301
|
-
@GeneralUtilities.check_arguments
|
|
302
|
-
def standardized_tasks_push_wheel_file_to_registry(self, wheel_file: str, api_key: str, repository: str, gpg_identity: str, verbosity: int) -> None:
|
|
303
|
-
# repository-value when PyPi should be used: "pypi"
|
|
304
|
-
# gpg_identity-value when wheel-file should not be signed: None
|
|
305
|
-
folder = os.path.dirname(wheel_file)
|
|
306
|
-
filename = os.path.basename(wheel_file)
|
|
307
|
-
|
|
308
|
-
if gpg_identity is None:
|
|
309
|
-
gpg_identity_argument = ""
|
|
310
|
-
else:
|
|
311
|
-
gpg_identity_argument = "" # f" --sign --identity {gpg_identity}"
|
|
312
|
-
# disabled due to https://blog.pypi.org/posts/2023-05-23-removing-pgp/
|
|
313
|
-
|
|
314
|
-
if verbosity > 2:
|
|
315
|
-
verbose_argument = " --verbose"
|
|
316
|
-
else:
|
|
317
|
-
verbose_argument = ""
|
|
318
|
-
|
|
319
|
-
twine_argument = f"upload{gpg_identity_argument} --repository {repository} --non-interactive {filename} --disable-progress-bar"
|
|
320
|
-
twine_argument = f"{twine_argument} --username __token__ --password {api_key}{verbose_argument}"
|
|
321
|
-
self.__sc.run_program("twine", twine_argument, folder, verbosity=verbosity, throw_exception_if_exitcode_is_not_zero=True)
|
|
322
|
-
|
|
323
|
-
@GeneralUtilities.check_arguments
|
|
324
|
-
def push_wheel_build_artifact(self, push_build_artifacts_file, product_name, codeunitname, repository: str, apikey: str, gpg_identity: str, verbosity: int, commandline_arguments: list[str], repository_folder_name: str) -> None:
|
|
325
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
326
|
-
folder_of_this_file = os.path.dirname(push_build_artifacts_file)
|
|
327
|
-
repository_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}../Submodules{os.path.sep}{repository_folder_name}", folder_of_this_file)
|
|
328
|
-
wheel_file = self.get_wheel_file(repository_folder, codeunitname)
|
|
329
|
-
self.standardized_tasks_push_wheel_file_to_registry(wheel_file, apikey, repository, gpg_identity, verbosity)
|
|
330
|
-
|
|
331
|
-
@GeneralUtilities.check_arguments
|
|
332
|
-
def get_version_of_codeunit_file_content(self, codeunit_file_content: str) -> str:
|
|
333
|
-
root: etree._ElementTree = etree.fromstring(codeunit_file_content.encode("utf-8"))
|
|
334
|
-
result = str(root.xpath('//cps:version/text()',
|
|
335
|
-
namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0])
|
|
336
|
-
return result
|
|
337
|
-
|
|
338
|
-
@GeneralUtilities.check_arguments
|
|
339
|
-
def get_version_of_codeunit(self, codeunit_file: str) -> None:
|
|
340
|
-
return self.get_version_of_codeunit_file_content(GeneralUtilities.read_text_from_file(codeunit_file))
|
|
341
|
-
|
|
342
|
-
@GeneralUtilities.check_arguments
|
|
343
|
-
def get_version_of_codeunit_folder(self, codeunit_folder: str) -> None:
|
|
344
|
-
codeunit_file = os.path.join(codeunit_folder, f"{os.path.basename(codeunit_folder)}.codeunit.xml")
|
|
345
|
-
return self.get_version_of_codeunit(codeunit_file)
|
|
346
|
-
|
|
347
|
-
@staticmethod
|
|
348
|
-
@GeneralUtilities.check_arguments
|
|
349
|
-
def get_string_value_from_commandline_arguments(commandline_arguments: list[str], property_name: str, default_value: str) -> str:
|
|
350
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, property_name)
|
|
351
|
-
if result is None:
|
|
352
|
-
return default_value
|
|
353
|
-
else:
|
|
354
|
-
return result
|
|
355
|
-
|
|
356
|
-
@staticmethod
|
|
357
|
-
@GeneralUtilities.check_arguments
|
|
358
|
-
def get_is_pre_merge_value_from_commandline_arguments(commandline_arguments: list[str], default_value: bool) -> bool:
|
|
359
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "is_pre_merge")
|
|
360
|
-
if result is None:
|
|
361
|
-
return default_value
|
|
362
|
-
else:
|
|
363
|
-
return GeneralUtilities.string_to_boolean(result)
|
|
364
|
-
|
|
365
|
-
@staticmethod
|
|
366
|
-
@GeneralUtilities.check_arguments
|
|
367
|
-
def get_assume_dependent_codeunits_are_already_built_from_commandline_arguments(commandline_arguments: list[str], default_value: bool) -> bool:
|
|
368
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "assume_dependent_codeunits_are_already_built")
|
|
369
|
-
if result is None:
|
|
370
|
-
return default_value
|
|
371
|
-
else:
|
|
372
|
-
return GeneralUtilities.string_to_boolean(result)
|
|
373
|
-
|
|
374
|
-
@staticmethod
|
|
375
|
-
@GeneralUtilities.check_arguments
|
|
376
|
-
def get_verbosity_from_commandline_arguments(commandline_arguments: list[str], default_value: int) -> int:
|
|
377
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "verbosity")
|
|
378
|
-
if result is None:
|
|
379
|
-
return default_value
|
|
380
|
-
else:
|
|
381
|
-
return int(result)
|
|
382
|
-
|
|
383
|
-
@staticmethod
|
|
384
|
-
@GeneralUtilities.check_arguments
|
|
385
|
-
def get_targetenvironmenttype_from_commandline_arguments(commandline_arguments: list[str], default_value: str) -> str:
|
|
386
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "targetenvironmenttype")
|
|
387
|
-
if result is None:
|
|
388
|
-
return default_value
|
|
389
|
-
else:
|
|
390
|
-
return result
|
|
391
|
-
|
|
392
|
-
@staticmethod
|
|
393
|
-
@GeneralUtilities.check_arguments
|
|
394
|
-
def get_additionalargumentsfile_from_commandline_arguments(commandline_arguments: list[str], default_value: str) -> str:
|
|
395
|
-
result = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "additionalargumentsfile")
|
|
396
|
-
if result is None:
|
|
397
|
-
return default_value
|
|
398
|
-
else:
|
|
399
|
-
return result
|
|
400
|
-
|
|
401
|
-
@staticmethod
|
|
402
|
-
@GeneralUtilities.check_arguments
|
|
403
|
-
def get_filestosign_from_commandline_arguments(commandline_arguments: list[str], default_value: dict[str, str]) -> dict[str, str]:
|
|
404
|
-
result_plain = TasksForCommonProjectStructure.get_property_from_commandline_arguments(commandline_arguments, "sign")
|
|
405
|
-
if result_plain is None:
|
|
406
|
-
return default_value
|
|
407
|
-
else:
|
|
408
|
-
result: dict[str, str] = dict[str, str]()
|
|
409
|
-
files_tuples = GeneralUtilities.to_list(result_plain, ";")
|
|
410
|
-
for files_tuple in files_tuples:
|
|
411
|
-
splitted = files_tuple.split("=")
|
|
412
|
-
result[splitted[0]] = splitted[1]
|
|
413
|
-
return result
|
|
414
|
-
|
|
415
|
-
@staticmethod
|
|
416
|
-
@GeneralUtilities.check_arguments
|
|
417
|
-
def get_property_from_commandline_arguments(commandline_arguments: list[str], property_name: str) -> str:
|
|
418
|
-
result: str = None
|
|
419
|
-
count = len(commandline_arguments)
|
|
420
|
-
loop_index = -1
|
|
421
|
-
for commandline_argument in commandline_arguments:
|
|
422
|
-
loop_index = loop_index+1
|
|
423
|
-
if loop_index < count-1:
|
|
424
|
-
prefix = f"--overwrite_{property_name}"
|
|
425
|
-
if commandline_argument == prefix:
|
|
426
|
-
result = commandline_arguments[loop_index+1]
|
|
427
|
-
return result
|
|
428
|
-
return result
|
|
429
|
-
|
|
430
|
-
@GeneralUtilities.check_arguments
|
|
431
|
-
def update_version_of_codeunit(self, common_tasks_file: str, current_version: str) -> None:
|
|
432
|
-
codeunit_name: str = os.path.basename(GeneralUtilities.resolve_relative_path("..", os.path.dirname(common_tasks_file)))
|
|
433
|
-
codeunit_file: str = os.path.join(GeneralUtilities.resolve_relative_path("..", os.path.dirname(common_tasks_file)), f"{codeunit_name}.codeunit.xml")
|
|
434
|
-
self.write_version_to_codeunit_file(codeunit_file, current_version)
|
|
435
|
-
|
|
436
|
-
@GeneralUtilities.check_arguments
|
|
437
|
-
def t4_transform(self, commontasks_script_file_of_current_file: str, verbosity: int):
|
|
438
|
-
sc = ScriptCollectionCore()
|
|
439
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", commontasks_script_file_of_current_file)
|
|
440
|
-
self.__ensure_grylibrary_is_available(codeunit_folder)
|
|
441
|
-
repository_folder: str = os.path.dirname(codeunit_folder)
|
|
442
|
-
codeunitname: str = os.path.basename(codeunit_folder)
|
|
443
|
-
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
444
|
-
for search_result in Path(codeunit_folder).glob('**/*.tt'):
|
|
445
|
-
tt_file = str(search_result)
|
|
446
|
-
relative_path_to_tt_file = str(Path(tt_file).relative_to(codeunit_folder))
|
|
447
|
-
argument = f"--parameter=repositoryFolder={repository_folder} --parameter=codeUnitName={codeunitname} {relative_path_to_tt_file}"
|
|
448
|
-
sc.run_program("t4", argument, codeunit_folder, verbosity=verbosity)
|
|
449
|
-
|
|
450
|
-
@GeneralUtilities.check_arguments
|
|
451
|
-
def standardized_tasks_generate_reference_by_docfx(self, generate_reference_script_file: str, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
452
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
453
|
-
folder_of_current_file = os.path.dirname(generate_reference_script_file)
|
|
454
|
-
generated_reference_folder = GeneralUtilities.resolve_relative_path("../Artifacts/Reference", folder_of_current_file)
|
|
455
|
-
GeneralUtilities.ensure_directory_does_not_exist(generated_reference_folder)
|
|
456
|
-
GeneralUtilities.ensure_directory_exists(generated_reference_folder)
|
|
457
|
-
obj_folder = os.path.join(folder_of_current_file, "obj")
|
|
458
|
-
GeneralUtilities.ensure_directory_does_not_exist(obj_folder)
|
|
459
|
-
GeneralUtilities.ensure_directory_exists(obj_folder)
|
|
460
|
-
self.__sc.run_program("docfx", "docfx.json", folder_of_current_file, verbosity=verbosity)
|
|
461
|
-
GeneralUtilities.ensure_directory_does_not_exist(obj_folder)
|
|
462
|
-
|
|
463
|
-
def standardized_task_verify_standard_format_csproj_files(self, codeunit_folder: str) -> bool:
|
|
464
|
-
repository_folder = os.path.dirname(codeunit_folder)
|
|
465
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
466
|
-
codeunit_folder = os.path.join(repository_folder, codeunit_name)
|
|
467
|
-
codeunit_version = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
468
|
-
message = " does not match the standardized .csproj-file-format which is defined by the regex "
|
|
469
|
-
|
|
470
|
-
csproj_project_name = codeunit_name
|
|
471
|
-
csproj_file = os.path.join(codeunit_folder, csproj_project_name, csproj_project_name+".csproj")
|
|
472
|
-
result1: tuple[bool, str] = self.__standardized_task_verify_standard_format_for_project_csproj_file(csproj_file, codeunit_folder, codeunit_name, codeunit_version)
|
|
473
|
-
if not result1[0]:
|
|
474
|
-
raise ValueError(csproj_file+message+f'"{result1[1]}".')
|
|
475
|
-
|
|
476
|
-
test_csproj_project_name = csproj_project_name+"Tests"
|
|
477
|
-
test_csproj_file = os.path.join(codeunit_folder, test_csproj_project_name, test_csproj_project_name+".csproj")
|
|
478
|
-
result2: tuple[bool, str] = self.__standardized_task_verify_standard_format_for_test_csproj_file(test_csproj_file, codeunit_name, codeunit_version)
|
|
479
|
-
if not result2[0]:
|
|
480
|
-
raise ValueError(test_csproj_file+message+f'"{result2[1]}".')
|
|
481
|
-
|
|
482
|
-
def __standardized_task_verify_standard_format_for_project_csproj_file(self, csproj_file: str, codeunit_folder: str, codeunit_name: str, codeunit_version: str) -> tuple[bool, str]:
|
|
483
|
-
codeunit_name_regex = re.escape(codeunit_name)
|
|
484
|
-
codeunit_file = os.path.join(codeunit_folder, f"{codeunit_name}.codeunit.xml")
|
|
485
|
-
codeunit_description = self.get_codeunit_description(codeunit_file)
|
|
486
|
-
codeunit_version_regex = re.escape(codeunit_version)
|
|
487
|
-
codeunit_description_regex = re.escape(codeunit_description)
|
|
488
|
-
regex = f"""^<Project Sdk=\\"Microsoft\\.NET\\.Sdk\\">
|
|
489
|
-
<PropertyGroup>
|
|
490
|
-
<TargetFramework>([^<]+)<\\/TargetFramework>
|
|
491
|
-
<Authors>([^<]+)<\\/Authors>
|
|
492
|
-
<Version>{codeunit_version_regex}<\\/Version>
|
|
493
|
-
<AssemblyVersion>{codeunit_version_regex}<\\/AssemblyVersion>
|
|
494
|
-
<FileVersion>{codeunit_version_regex}<\\/FileVersion>
|
|
495
|
-
<SelfContained>false<\\/SelfContained>
|
|
496
|
-
<IsPackable>false<\\/IsPackable>
|
|
497
|
-
<PreserveCompilationContext>false<\\/PreserveCompilationContext>
|
|
498
|
-
<GenerateRuntimeConfigurationFiles>true<\\/GenerateRuntimeConfigurationFiles>
|
|
499
|
-
<Copyright>([^<]+)<\\/Copyright>
|
|
500
|
-
<Description>{codeunit_description_regex}<\\/Description>
|
|
501
|
-
<PackageProjectUrl>https:\\/\\/([^<]+)<\\/PackageProjectUrl>
|
|
502
|
-
<RepositoryUrl>https:\\/\\/([^<]+)\\.git<\\/RepositoryUrl>
|
|
503
|
-
<RootNamespace>([^<]+)\\.Core<\\/RootNamespace>
|
|
504
|
-
<ProduceReferenceAssembly>false<\\/ProduceReferenceAssembly>
|
|
505
|
-
<Nullable>(disable|enable|warnings|annotations)<\\/Nullable>
|
|
506
|
-
<Configurations>Development;QualityCheck;Productive<\\/Configurations>
|
|
507
|
-
<IsTestProject>false<\\/IsTestProject>
|
|
508
|
-
<LangVersion>([^<]+)<\\/LangVersion>
|
|
509
|
-
<PackageRequireLicenseAcceptance>true<\\/PackageRequireLicenseAcceptance>
|
|
510
|
-
<GenerateSerializationAssemblies>Off<\\/GenerateSerializationAssemblies>
|
|
511
|
-
<AppendTargetFrameworkToOutputPath>false<\\/AppendTargetFrameworkToOutputPath>
|
|
512
|
-
<OutputPath>\\.\\.\\\\Other\\\\Artifacts\\\\BuildResult_DotNet_win-x64<\\/OutputPath>
|
|
513
|
-
<PlatformTarget>([^<]+)<\\/PlatformTarget>
|
|
514
|
-
<WarningLevel>\\d<\\/WarningLevel>
|
|
515
|
-
<Prefer32Bit>false<\\/Prefer32Bit>
|
|
516
|
-
<SignAssembly>True<\\/SignAssembly>
|
|
517
|
-
<AssemblyOriginatorKeyFile>\\.\\.\\\\\\.\\.\\\\Other\\\\Resources\\\\PublicKeys\\\\StronglyNamedKey\\\\([^<]+)PublicKey\\.snk<\\/AssemblyOriginatorKeyFile>
|
|
518
|
-
<DelaySign>True<\\/DelaySign>
|
|
519
|
-
<NoWarn>([^<]+)<\\/NoWarn>
|
|
520
|
-
<WarningsAsErrors>([^<]+)<\\/WarningsAsErrors>
|
|
521
|
-
<ErrorLog>\\.\\.\\\\Other\\\\Resources\\\\CodeAnalysisResult\\\\{codeunit_name_regex}\\.sarif<\\/ErrorLog>
|
|
522
|
-
<OutputType>([^<]+)<\\/OutputType>
|
|
523
|
-
<DocumentationFile>\\.\\.\\\\Other\\\\Artifacts\\\\MetaInformation\\\\{codeunit_name_regex}\\.xml<\\/DocumentationFile>(\\n|.)*
|
|
524
|
-
<\\/PropertyGroup>
|
|
525
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='Development'\\\">
|
|
526
|
-
<DebugType>full<\\/DebugType>
|
|
527
|
-
<DebugSymbols>true<\\/DebugSymbols>
|
|
528
|
-
<Optimize>false<\\/Optimize>
|
|
529
|
-
<DefineConstants>TRACE;DEBUG;Development<\\/DefineConstants>
|
|
530
|
-
<ErrorReport>prompt<\\/ErrorReport>
|
|
531
|
-
<\\/PropertyGroup>
|
|
532
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='QualityCheck'\\\">
|
|
533
|
-
<DebugType>portable<\\/DebugType>
|
|
534
|
-
<DebugSymbols>true<\\/DebugSymbols>
|
|
535
|
-
<Optimize>false<\\/Optimize>
|
|
536
|
-
<DefineConstants>TRACE;QualityCheck<\\/DefineConstants>
|
|
537
|
-
<ErrorReport>none<\\/ErrorReport>
|
|
538
|
-
<\\/PropertyGroup>
|
|
539
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='Productive'\\\">
|
|
540
|
-
<DebugType>none<\\/DebugType>
|
|
541
|
-
<DebugSymbols>false<\\/DebugSymbols>
|
|
542
|
-
<Optimize>true<\\/Optimize>
|
|
543
|
-
<DefineConstants>Productive<\\/DefineConstants>
|
|
544
|
-
<ErrorReport>none<\\/ErrorReport>
|
|
545
|
-
<\\/PropertyGroup>(\\n|.)*
|
|
546
|
-
<\\/Project>$"""
|
|
547
|
-
return (self.__standardized_task_verify_standard_format_for_csproj_files(regex, csproj_file), regex)
|
|
548
|
-
|
|
549
|
-
def __standardized_task_verify_standard_format_for_test_csproj_file(self, csproj_file: str, codeunit_name: str, codeunit_version: str) -> tuple[bool, str]:
|
|
550
|
-
codeunit_name_regex = re.escape(codeunit_name)
|
|
551
|
-
codeunit_version_regex = re.escape(codeunit_version)
|
|
552
|
-
regex = f"""^<Project Sdk=\\"Microsoft\\.NET\\.Sdk\\">
|
|
553
|
-
<PropertyGroup>
|
|
554
|
-
<TargetFramework>([^<]+)<\\/TargetFramework>
|
|
555
|
-
<Authors>([^<]+)<\\/Authors>
|
|
556
|
-
<Version>{codeunit_version_regex}<\\/Version>
|
|
557
|
-
<AssemblyVersion>{codeunit_version_regex}<\\/AssemblyVersion>
|
|
558
|
-
<FileVersion>{codeunit_version_regex}<\\/FileVersion>
|
|
559
|
-
<SelfContained>false<\\/SelfContained>
|
|
560
|
-
<IsPackable>false<\\/IsPackable>
|
|
561
|
-
<PreserveCompilationContext>false<\\/PreserveCompilationContext>
|
|
562
|
-
<GenerateRuntimeConfigurationFiles>true<\\/GenerateRuntimeConfigurationFiles>
|
|
563
|
-
<Copyright>([^<]+)<\\/Copyright>
|
|
564
|
-
<Description>{codeunit_name_regex}Tests is the test-project for {codeunit_name_regex}\\.<\\/Description>
|
|
565
|
-
<PackageProjectUrl>https:\\/\\/([^<]+)<\\/PackageProjectUrl>
|
|
566
|
-
<RepositoryUrl>https:\\/\\/([^<]+)\\.git</RepositoryUrl>
|
|
567
|
-
<RootNamespace>([^<]+)\\.Tests<\\/RootNamespace>
|
|
568
|
-
<ProduceReferenceAssembly>false<\\/ProduceReferenceAssembly>
|
|
569
|
-
<Nullable>(disable|enable|warnings|annotations)<\\/Nullable>
|
|
570
|
-
<Configurations>Development;QualityCheck;Productive<\\/Configurations>
|
|
571
|
-
<IsTestProject>true<\\/IsTestProject>
|
|
572
|
-
<LangVersion>([^<]+)<\\/LangVersion>
|
|
573
|
-
<PackageRequireLicenseAcceptance>true<\\/PackageRequireLicenseAcceptance>
|
|
574
|
-
<GenerateSerializationAssemblies>Off<\\/GenerateSerializationAssemblies>
|
|
575
|
-
<AppendTargetFrameworkToOutputPath>false<\\/AppendTargetFrameworkToOutputPath>
|
|
576
|
-
<OutputPath>\\.\\.\\\\Other\\\\Artifacts\\\\BuildResultTests_DotNet_win-x64<\\/OutputPath>
|
|
577
|
-
<PlatformTarget>([^<]+)<\\/PlatformTarget>
|
|
578
|
-
<WarningLevel>\\d<\\/WarningLevel>
|
|
579
|
-
<Prefer32Bit>false<\\/Prefer32Bit>
|
|
580
|
-
<SignAssembly>True<\\/SignAssembly>
|
|
581
|
-
<AssemblyOriginatorKeyFile>\\.\\.\\\\\\.\\.\\\\Other\\\\Resources\\\\PublicKeys\\\\StronglyNamedKey\\\\([^<]+)PublicKey\\.snk<\\/AssemblyOriginatorKeyFile>
|
|
582
|
-
<DelaySign>True<\\/DelaySign>
|
|
583
|
-
<NoWarn>([^<]+)<\\/NoWarn>
|
|
584
|
-
<WarningsAsErrors>([^<]+)<\\/WarningsAsErrors>
|
|
585
|
-
<ErrorLog>\\.\\.\\\\Other\\\\Resources\\\\CodeAnalysisResult\\\\{codeunit_name_regex}Tests\\.sarif<\\/ErrorLog>
|
|
586
|
-
<OutputType>Library<\\/OutputType>(\\n|.)*
|
|
587
|
-
<\\/PropertyGroup>
|
|
588
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='Development'\\\">
|
|
589
|
-
<DebugType>full<\\/DebugType>
|
|
590
|
-
<DebugSymbols>true<\\/DebugSymbols>
|
|
591
|
-
<Optimize>false<\\/Optimize>
|
|
592
|
-
<DefineConstants>TRACE;DEBUG;Development<\\/DefineConstants>
|
|
593
|
-
<ErrorReport>prompt<\\/ErrorReport>
|
|
594
|
-
<\\/PropertyGroup>
|
|
595
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='QualityCheck'\\\">
|
|
596
|
-
<DebugType>portable<\\/DebugType>
|
|
597
|
-
<DebugSymbols>true<\\/DebugSymbols>
|
|
598
|
-
<Optimize>false<\\/Optimize>
|
|
599
|
-
<DefineConstants>TRACE;QualityCheck<\\/DefineConstants>
|
|
600
|
-
<ErrorReport>none<\\/ErrorReport>
|
|
601
|
-
<\\/PropertyGroup>
|
|
602
|
-
<PropertyGroup Condition=\\\"'\\$\\(Configuration\\)'=='Productive'\\\">
|
|
603
|
-
<DebugType>none<\\/DebugType>
|
|
604
|
-
<DebugSymbols>false<\\/DebugSymbols>
|
|
605
|
-
<Optimize>true<\\/Optimize>
|
|
606
|
-
<DefineConstants>Productive<\\/DefineConstants>
|
|
607
|
-
<ErrorReport>none<\\/ErrorReport>
|
|
608
|
-
<\\/PropertyGroup>(\\n|.)*
|
|
609
|
-
<\\/Project>$"""
|
|
610
|
-
return (self.__standardized_task_verify_standard_format_for_csproj_files(regex, csproj_file), regex)
|
|
611
|
-
|
|
612
|
-
def __standardized_task_verify_standard_format_for_csproj_files(self, regex: str, csproj_file: str) -> bool:
|
|
613
|
-
filename = os.path.basename(csproj_file)
|
|
614
|
-
GeneralUtilities.write_message_to_stdout(f"Check {filename}...")
|
|
615
|
-
file_content = GeneralUtilities.read_text_from_file(csproj_file)
|
|
616
|
-
regex = regex.replace("\r", "").replace("\n", "\\n")
|
|
617
|
-
file_content = file_content.replace("\r", "")
|
|
618
|
-
match = re.match(regex, file_content)
|
|
619
|
-
return match is not None
|
|
620
|
-
|
|
621
|
-
@GeneralUtilities.check_arguments
|
|
622
|
-
def __standardized_tasks_build_for_dotnet_build(self, csproj_file: str, originaloutputfolder: str, files_to_sign: dict[str, str], commitid: str, verbosity: int, runtimes: list[str], target_environmenttype: str, target_environmenttype_mapping: dict[str, str], copy_license_file_to_target_folder: bool, repository_folder: str, codeunit_name: str, commandline_arguments: list[str]) -> None:
|
|
623
|
-
csproj_filename = os.path.basename(csproj_file)
|
|
624
|
-
GeneralUtilities.write_message_to_stdout(f"Build {csproj_filename}...")
|
|
625
|
-
dotnet_build_configuration: str = target_environmenttype_mapping[target_environmenttype]
|
|
626
|
-
verbosity = self.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
627
|
-
codeunit_folder = os.path.join(repository_folder, codeunit_name)
|
|
628
|
-
csproj_file_folder = os.path.dirname(csproj_file)
|
|
629
|
-
csproj_file_name = os.path.basename(csproj_file)
|
|
630
|
-
csproj_file_name_without_extension = csproj_file_name.split(".")[0]
|
|
631
|
-
sarif_folder = os.path.join(codeunit_folder, "Other", "Resources", "CodeAnalysisResult")
|
|
632
|
-
GeneralUtilities.ensure_directory_exists(sarif_folder)
|
|
633
|
-
gitkeep_file = os.path.join(sarif_folder, ".gitkeep")
|
|
634
|
-
GeneralUtilities.ensure_file_exists(gitkeep_file)
|
|
635
|
-
for runtime in runtimes:
|
|
636
|
-
outputfolder = originaloutputfolder+runtime
|
|
637
|
-
GeneralUtilities.ensure_directory_does_not_exist(os.path.join(csproj_file_folder, "obj"))
|
|
638
|
-
GeneralUtilities.ensure_directory_does_not_exist(outputfolder)
|
|
639
|
-
self.__sc.run_program("dotnet", "clean", csproj_file_folder, verbosity=verbosity)
|
|
640
|
-
GeneralUtilities.ensure_directory_exists(outputfolder)
|
|
641
|
-
self.__sc.run_program("dotnet", "restore", codeunit_folder, verbosity=verbosity)
|
|
642
|
-
self.__sc.run_program("dotnet", f"build {csproj_file_name} -c {dotnet_build_configuration} -o {outputfolder} --runtime {runtime}", csproj_file_folder, verbosity=verbosity)
|
|
643
|
-
if copy_license_file_to_target_folder:
|
|
644
|
-
license_file = os.path.join(repository_folder, "License.txt")
|
|
645
|
-
target = os.path.join(outputfolder, f"{codeunit_name}.License.txt")
|
|
646
|
-
shutil.copyfile(license_file, target)
|
|
647
|
-
if 0 < len(files_to_sign):
|
|
648
|
-
for key, value in files_to_sign.items():
|
|
649
|
-
dll_file = key
|
|
650
|
-
snk_file = value
|
|
651
|
-
dll_file_full = os.path.join(outputfolder, dll_file)
|
|
652
|
-
if os.path.isfile(dll_file_full):
|
|
653
|
-
GeneralUtilities.assert_condition(self.__sc.run_program("sn", f"-vf {dll_file}", outputfolder, throw_exception_if_exitcode_is_not_zero=False)[0] == 1, f"Pre-verifying of {dll_file} failed.")
|
|
654
|
-
self.__sc.run_program("sn", f"-R {dll_file} {snk_file}", outputfolder)
|
|
655
|
-
GeneralUtilities.assert_condition(self.__sc.run_program("sn", f"-vf {dll_file}", outputfolder, throw_exception_if_exitcode_is_not_zero=False)[0] == 0, f"Verifying of {dll_file} failed.")
|
|
656
|
-
sarif_filename = f"{csproj_file_name_without_extension}.sarif"
|
|
657
|
-
sarif_source_file = os.path.join(sarif_folder, sarif_filename)
|
|
658
|
-
if os.path.exists(sarif_source_file):
|
|
659
|
-
sarif_folder_target = os.path.join(codeunit_folder, "Other", "Artifacts", "CodeAnalysisResult")
|
|
660
|
-
GeneralUtilities.ensure_directory_exists(sarif_folder_target)
|
|
661
|
-
sarif_target_file = os.path.join(sarif_folder_target, sarif_filename)
|
|
662
|
-
GeneralUtilities.ensure_file_does_not_exist(sarif_target_file)
|
|
663
|
-
shutil.copyfile(sarif_source_file, sarif_target_file)
|
|
664
|
-
|
|
665
|
-
@GeneralUtilities.check_arguments
|
|
666
|
-
def standardized_tasks_build_for_dotnet_project(self, buildscript_file: str, default_target_environmenttype: str, target_environmenttype_mapping: dict[str, str], runtimes: list[str], verbosity: int, commandline_arguments: list[str]) -> None:
|
|
667
|
-
# hint: arguments can be overwritten by commandline_arguments
|
|
668
|
-
# this function builds an exe
|
|
669
|
-
target_environmenttype = self.get_targetenvironmenttype_from_commandline_arguments(commandline_arguments, default_target_environmenttype)
|
|
670
|
-
self.__standardized_tasks_build_for_dotnet_project(
|
|
671
|
-
buildscript_file, target_environmenttype_mapping, default_target_environmenttype, verbosity, target_environmenttype,
|
|
672
|
-
runtimes, True, commandline_arguments)
|
|
673
|
-
|
|
674
|
-
@GeneralUtilities.check_arguments
|
|
675
|
-
def standardized_tasks_build_for_dotnet_library_project(self, buildscript_file: str, default_target_environmenttype: str, target_environmenttype_mapping: dict[str, str], runtimes: list[str], verbosity: int, commandline_arguments: list[str]) -> None:
|
|
676
|
-
# hint: arguments can be overwritten by commandline_arguments
|
|
677
|
-
# this function builds a dll and converts it to a nupkg-file
|
|
678
|
-
|
|
679
|
-
target_environmenttype = self.get_targetenvironmenttype_from_commandline_arguments(commandline_arguments, default_target_environmenttype)
|
|
680
|
-
self.__standardized_tasks_build_for_dotnet_project(buildscript_file, target_environmenttype_mapping, default_target_environmenttype, verbosity, target_environmenttype, runtimes, True, commandline_arguments)
|
|
681
|
-
self.__standardized_tasks_build_nupkg_for_dotnet_create_package(buildscript_file, verbosity, commandline_arguments)
|
|
682
|
-
|
|
683
|
-
@GeneralUtilities.check_arguments
|
|
684
|
-
def get_default_target_environmenttype_mapping(self) -> dict[str, str]:
|
|
685
|
-
return {
|
|
686
|
-
TasksForCommonProjectStructure.get_development_environment_name(): TasksForCommonProjectStructure.get_development_environment_name(),
|
|
687
|
-
TasksForCommonProjectStructure.get_qualitycheck_environment_name(): TasksForCommonProjectStructure.get_qualitycheck_environment_name(),
|
|
688
|
-
TasksForCommonProjectStructure.get_productive_environment_name(): TasksForCommonProjectStructure.get_productive_environment_name()
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
@GeneralUtilities.check_arguments
|
|
692
|
-
def __standardized_tasks_build_for_dotnet_project(self, buildscript_file: str, target_environmenttype_mapping: dict[str, str], default_target_environment_type: str, verbosity: int, target_environment_type: str, runtimes: list[str], copy_license_file_to_target_folder: bool, commandline_arguments: list[str]) -> None:
|
|
693
|
-
codeunitname: str = os.path.basename(str(Path(os.path.dirname(buildscript_file)).parent.parent.absolute()))
|
|
694
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
695
|
-
files_to_sign: dict[str, str] = TasksForCommonProjectStructure.get_filestosign_from_commandline_arguments(commandline_arguments, dict())
|
|
696
|
-
repository_folder: str = str(Path(os.path.dirname(buildscript_file)).parent.parent.parent.absolute())
|
|
697
|
-
commitid = self.__sc.git_get_commit_id(repository_folder)
|
|
698
|
-
outputfolder = GeneralUtilities.resolve_relative_path("../Artifacts", os.path.dirname(buildscript_file))
|
|
699
|
-
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
700
|
-
csproj_file = os.path.join(codeunit_folder, codeunitname, codeunitname + ".csproj")
|
|
701
|
-
csproj_test_file = os.path.join(codeunit_folder, codeunitname+"Tests", codeunitname+"Tests.csproj")
|
|
702
|
-
self.__standardized_tasks_build_for_dotnet_build(csproj_file, os.path.join(outputfolder, "BuildResult_DotNet_"), files_to_sign, commitid, verbosity, runtimes, target_environment_type, target_environmenttype_mapping, copy_license_file_to_target_folder, repository_folder, codeunitname, commandline_arguments)
|
|
703
|
-
self.__standardized_tasks_build_for_dotnet_build(csproj_test_file, os.path.join(outputfolder, "BuildResultTests_DotNet_"), files_to_sign, commitid, verbosity, runtimes, target_environment_type, target_environmenttype_mapping, copy_license_file_to_target_folder, repository_folder, codeunitname, commandline_arguments)
|
|
704
|
-
self.generate_sbom_for_dotnet_project(codeunit_folder, verbosity, commandline_arguments)
|
|
705
|
-
self.copy_source_files_to_output_directory(buildscript_file)
|
|
706
|
-
|
|
707
|
-
@GeneralUtilities.check_arguments
|
|
708
|
-
def __standardized_tasks_build_nupkg_for_dotnet_create_package(self, buildscript_file: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
709
|
-
codeunitname: str = os.path.basename(str(Path(os.path.dirname(buildscript_file)).parent.parent.absolute()))
|
|
710
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
711
|
-
repository_folder: str = str(Path(os.path.dirname(buildscript_file)).parent.parent.parent.absolute())
|
|
712
|
-
build_folder = os.path.join(repository_folder, codeunitname, "Other", "Build")
|
|
713
|
-
outputfolder = GeneralUtilities.resolve_relative_path("../Artifacts/BuildResult_NuGet", os.path.dirname(buildscript_file))
|
|
714
|
-
root: etree._ElementTree = etree.parse(os.path.join(build_folder, f"{codeunitname}.nuspec"))
|
|
715
|
-
current_version = root.xpath("//*[name() = 'package']/*[name() = 'metadata']/*[name() = 'version']/text()")[0]
|
|
716
|
-
nupkg_filename = f"{codeunitname}.{current_version}.nupkg"
|
|
717
|
-
nupkg_file = f"{build_folder}/{nupkg_filename}"
|
|
718
|
-
GeneralUtilities.ensure_file_does_not_exist(nupkg_file)
|
|
719
|
-
commit_id = self.__sc.git_get_commit_id(repository_folder)
|
|
720
|
-
self.__sc.run_program("nuget", f"pack {codeunitname}.nuspec -Properties \"commitid={commit_id}\"", build_folder, verbosity=verbosity)
|
|
721
|
-
GeneralUtilities.ensure_directory_does_not_exist(outputfolder)
|
|
722
|
-
GeneralUtilities.ensure_directory_exists(outputfolder)
|
|
723
|
-
os.rename(nupkg_file, f"{outputfolder}/{nupkg_filename}")
|
|
724
|
-
|
|
725
|
-
@GeneralUtilities.check_arguments
|
|
726
|
-
def generate_sbom_for_dotnet_project(self, codeunit_folder: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
727
|
-
GeneralUtilities.write_message_to_stdout("Generate SBOM...")
|
|
728
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
729
|
-
sc = ScriptCollectionCore()
|
|
730
|
-
bomfile_folder = "Other\\Artifacts\\BOM"
|
|
731
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
732
|
-
sc.run_program("dotnet", f"CycloneDX {codeunit_name}\\{codeunit_name}.csproj -o {bomfile_folder} --disable-github-licenses", codeunit_folder, verbosity=verbosity)
|
|
733
|
-
codeunitversion = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunit_name}.codeunit.xml"))
|
|
734
|
-
target = f"{codeunit_folder}\\{bomfile_folder}\\{codeunit_name}.{codeunitversion}.sbom.xml"
|
|
735
|
-
GeneralUtilities.ensure_file_does_not_exist(target)
|
|
736
|
-
os.rename(f"{codeunit_folder}\\{bomfile_folder}\\bom.xml", target)
|
|
737
|
-
|
|
738
|
-
@GeneralUtilities.check_arguments
|
|
739
|
-
def standardized_tasks_run_linting_for_flutter_project_in_common_project_structure(self, script_file: str, default_verbosity: int, args: list[str]):
|
|
740
|
-
pass # TODO
|
|
741
|
-
|
|
742
|
-
@GeneralUtilities.check_arguments
|
|
743
|
-
def standardized_tasks_linting_for_python_codeunit(self, linting_script_file: str, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
744
|
-
codeunitname: str = Path(os.path.dirname(linting_script_file)).parent.parent.name
|
|
745
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
746
|
-
repository_folder: str = str(Path(os.path.dirname(linting_script_file)).parent.parent.parent.absolute())
|
|
747
|
-
errors_found = False
|
|
748
|
-
GeneralUtilities.write_message_to_stdout(f"Check for linting-issues in codeunit {codeunitname}.")
|
|
749
|
-
src_folder = os.path.join(repository_folder, codeunitname, codeunitname)
|
|
750
|
-
tests_folder = src_folder+"Tests"
|
|
751
|
-
# TODO check if there are errors in sarif-file
|
|
752
|
-
for file in GeneralUtilities.get_all_files_of_folder(src_folder)+GeneralUtilities.get_all_files_of_folder(tests_folder):
|
|
753
|
-
relative_file_path_in_repository = os.path.relpath(file, repository_folder)
|
|
754
|
-
if file.endswith(".py") and os.path.getsize(file) > 0 and not self.__sc.file_is_git_ignored(relative_file_path_in_repository, repository_folder):
|
|
755
|
-
GeneralUtilities.write_message_to_stdout(f"Check for linting-issues in {os.path.relpath(file,os.path.join(repository_folder,codeunitname))}.")
|
|
756
|
-
linting_result = self.__sc.python_file_has_errors(file, repository_folder)
|
|
757
|
-
if (linting_result[0]):
|
|
758
|
-
errors_found = True
|
|
759
|
-
for error in linting_result[1]:
|
|
760
|
-
GeneralUtilities.write_message_to_stderr(error)
|
|
761
|
-
if errors_found:
|
|
762
|
-
raise ValueError("Linting-issues occurred.")
|
|
763
|
-
else:
|
|
764
|
-
GeneralUtilities.write_message_to_stdout("No linting-issues found.")
|
|
765
|
-
|
|
766
|
-
@GeneralUtilities.check_arguments
|
|
767
|
-
def standardized_tasks_generate_coverage_report(self, repository_folder: str, codeunitname: str, verbosity: int, generate_badges: bool, targetenvironmenttype: str, commandline_arguments: list[str], add_testcoverage_history_entry: bool = None) -> None:
|
|
768
|
-
"""This function expects that the file '<repositorybasefolder>/<codeunitname>/Other/Artifacts/TestCoverage/TestCoverage.xml'
|
|
769
|
-
which contains a test-coverage-report in the cobertura-format exists.
|
|
770
|
-
This script expectes that the testcoverage-reportfolder is '<repositorybasefolder>/<codeunitname>/Other/Artifacts/TestCoverageReport'.
|
|
771
|
-
This script expectes that a test-coverage-badges should be added to '<repositorybasefolder>/<codeunitname>/Other/Resources/Badges'."""
|
|
772
|
-
GeneralUtilities.write_message_to_stdout("Generate testcoverage report..")
|
|
773
|
-
codeunit_version = self.get_version_of_codeunit(os.path.join(repository_folder, codeunitname, f"{codeunitname}.codeunit.xml"))
|
|
774
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
775
|
-
if verbosity == 0:
|
|
776
|
-
verbose_argument_for_reportgenerator = "Off"
|
|
777
|
-
elif verbosity == 1:
|
|
778
|
-
verbose_argument_for_reportgenerator = "Error"
|
|
779
|
-
elif verbosity == 2:
|
|
780
|
-
verbose_argument_for_reportgenerator = "Info"
|
|
781
|
-
elif verbosity == 3:
|
|
782
|
-
verbose_argument_for_reportgenerator = "Verbose"
|
|
783
|
-
else:
|
|
784
|
-
raise ValueError(f"Unknown value for verbosity: {GeneralUtilities.str_none_safe(verbosity)}")
|
|
785
|
-
|
|
786
|
-
# Generating report
|
|
787
|
-
GeneralUtilities.ensure_directory_does_not_exist(os.path.join(repository_folder, codeunitname, f"{codeunitname}/Other/Artifacts/TestCoverageReport"))
|
|
788
|
-
GeneralUtilities.ensure_directory_exists(os.path.join(repository_folder, codeunitname, "Other/Artifacts/TestCoverageReport"))
|
|
789
|
-
|
|
790
|
-
if add_testcoverage_history_entry is None:
|
|
791
|
-
add_testcoverage_history_entry = self.get_is_pre_merge_value_from_commandline_arguments(commandline_arguments, add_testcoverage_history_entry)
|
|
792
|
-
|
|
793
|
-
history_folder = f"{codeunitname}/Other/Resources/TestCoverageHistory"
|
|
794
|
-
history_folder_full = os.path.join(repository_folder, history_folder)
|
|
795
|
-
GeneralUtilities.ensure_directory_exists(history_folder_full)
|
|
796
|
-
history_argument = f" -historydir:{history_folder}"
|
|
797
|
-
argument = f"-reports:{codeunitname}/Other/Artifacts/TestCoverage/TestCoverage.xml -targetdir:{codeunitname}/Other/Artifacts/TestCoverageReport --verbosity:{verbose_argument_for_reportgenerator}{history_argument} -title:{codeunitname} -tag:v{codeunit_version}"
|
|
798
|
-
self.__sc.run_program("reportgenerator", argument, repository_folder, verbosity=verbosity)
|
|
799
|
-
if not add_testcoverage_history_entry:
|
|
800
|
-
os.remove(GeneralUtilities.get_direct_files_of_folder(history_folder_full)[-1])
|
|
801
|
-
|
|
802
|
-
# Generating badges
|
|
803
|
-
if generate_badges:
|
|
804
|
-
testcoverageubfolger = "Other/Resources/TestCoverageBadges"
|
|
805
|
-
fulltestcoverageubfolger = os.path.join(repository_folder, codeunitname, testcoverageubfolger)
|
|
806
|
-
GeneralUtilities.ensure_directory_does_not_exist(fulltestcoverageubfolger)
|
|
807
|
-
GeneralUtilities.ensure_directory_exists(fulltestcoverageubfolger)
|
|
808
|
-
self.__sc.run_program("reportgenerator", f"-reports:Other/Artifacts/TestCoverage/TestCoverage.xml -targetdir:{testcoverageubfolger} -reporttypes:Badges --verbosity:{verbose_argument_for_reportgenerator}", os.path.join(repository_folder, codeunitname), verbosity=verbosity)
|
|
809
|
-
|
|
810
|
-
@GeneralUtilities.check_arguments
|
|
811
|
-
def standardized_tasks_run_testcases_for_dotnet_project(self, runtestcases_file: str, targetenvironmenttype: str, verbosity: int, generate_badges: bool, target_environmenttype_mapping: dict[str, str], commandline_arguments: list[str]) -> None:
|
|
812
|
-
GeneralUtilities.write_message_to_stdout("Run testcases...")
|
|
813
|
-
dotnet_build_configuration: str = target_environmenttype_mapping[targetenvironmenttype]
|
|
814
|
-
codeunit_name: str = os.path.basename(str(Path(os.path.dirname(runtestcases_file)).parent.parent.absolute()))
|
|
815
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
816
|
-
repository_folder: str = str(Path(os.path.dirname(runtestcases_file)).parent.parent.parent.absolute()).replace("\\", "/")
|
|
817
|
-
coverage_file_folder = os.path.join(repository_folder, codeunit_name, "Other/Artifacts/TestCoverage")
|
|
818
|
-
temp_folder = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
|
|
819
|
-
GeneralUtilities.ensure_directory_exists(temp_folder)
|
|
820
|
-
runsettings_file = self.dotnet_runsettings_file
|
|
821
|
-
codeunit_folder = f"{repository_folder}/{codeunit_name}"
|
|
822
|
-
arg = f"test . -c {dotnet_build_configuration} -o {temp_folder}"
|
|
823
|
-
if os.path.isfile(os.path.join(codeunit_folder, runsettings_file)):
|
|
824
|
-
arg = f"{arg} --settings {runsettings_file}"
|
|
825
|
-
arg = f"{arg} /p:CollectCoverage=true /p:CoverletOutput=../Other/Artifacts/TestCoverage/Testcoverage /p:CoverletOutputFormat=cobertura"
|
|
826
|
-
self.__sc.run_program("dotnet", arg, codeunit_folder, verbosity=verbosity)
|
|
827
|
-
target_file = os.path.join(coverage_file_folder, "TestCoverage.xml")
|
|
828
|
-
GeneralUtilities.ensure_file_does_not_exist(target_file)
|
|
829
|
-
os.rename(os.path.join(coverage_file_folder, "Testcoverage.cobertura.xml"), target_file)
|
|
830
|
-
self.__remove_unrelated_package_from_testcoverage_file(target_file, codeunit_name)
|
|
831
|
-
root: etree._ElementTree = etree.parse(target_file)
|
|
832
|
-
source_base_path_in_coverage_file: str = root.xpath("//coverage/sources/source/text()")[0].replace("\\", "/")
|
|
833
|
-
content = GeneralUtilities.read_text_from_file(target_file)
|
|
834
|
-
GeneralUtilities.assert_condition(source_base_path_in_coverage_file.startswith(repository_folder) or repository_folder.startswith(source_base_path_in_coverage_file), f"Unexpected path for coverage. Sourcepath: \"{source_base_path_in_coverage_file}\"; repository: \"{repository_folder}\"")
|
|
835
|
-
content = re.sub('\\\\', '/', content)
|
|
836
|
-
content = re.sub("filename=\"([^\"]+)\"", lambda match: self.__standardized_tasks_run_testcases_for_dotnet_project_helper(source_base_path_in_coverage_file, codeunit_folder, match), content)
|
|
837
|
-
GeneralUtilities.write_text_to_file(target_file, content)
|
|
838
|
-
self.run_testcases_common_post_task(repository_folder, codeunit_name, verbosity, generate_badges, targetenvironmenttype, commandline_arguments)
|
|
839
|
-
|
|
840
|
-
@GeneralUtilities.check_arguments
|
|
841
|
-
def run_testcases_common_post_task(self, repository_folder: str, codeunit_name: str, verbosity: int, generate_badges: bool, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
842
|
-
coverage_file_folder = os.path.join(repository_folder, codeunit_name, "Other/Artifacts/TestCoverage")
|
|
843
|
-
coveragefiletarget = os.path.join(coverage_file_folder, "TestCoverage.xml")
|
|
844
|
-
self.update_path_of_source_in_testcoverage_file(repository_folder, codeunit_name)
|
|
845
|
-
self.standardized_tasks_generate_coverage_report(repository_folder, codeunit_name, verbosity, generate_badges, targetenvironmenttype, commandline_arguments)
|
|
846
|
-
self.check_testcoverage(coveragefiletarget, repository_folder, codeunit_name)
|
|
847
|
-
|
|
848
|
-
@staticmethod
|
|
849
|
-
@GeneralUtilities.check_arguments
|
|
850
|
-
def update_path_of_source_in_testcoverage_file(repository_folder: str, codeunitname: str) -> None:
|
|
851
|
-
GeneralUtilities.write_message_to_stdout("Update paths of source files in testcoverage files..")
|
|
852
|
-
folder = f"{repository_folder}/{codeunitname}/Other/Artifacts/TestCoverage"
|
|
853
|
-
filename = "TestCoverage.xml"
|
|
854
|
-
full_file = os.path.join(folder, filename)
|
|
855
|
-
GeneralUtilities.write_text_to_file(full_file, re.sub("<source>.+<\\/source>", f"<source><!--[repository]/-->./{codeunitname}/</source>", GeneralUtilities.read_text_from_file(full_file)))
|
|
856
|
-
TasksForCommonProjectStructure.__remove_not_existing_files_from_testcoverage_file(full_file, repository_folder, codeunitname)
|
|
857
|
-
|
|
858
|
-
@GeneralUtilities.check_arguments
|
|
859
|
-
def __standardized_tasks_run_testcases_for_dotnet_project_helper(self, source: str, codeunit_folder: str, match: re.Match) -> str:
|
|
860
|
-
filename = match.group(1)
|
|
861
|
-
file = os.path.join(source, filename)
|
|
862
|
-
# GeneralUtilities.assert_condition(os.path.isfile(file),f"File \"{file}\" does not exist.")
|
|
863
|
-
GeneralUtilities.assert_condition(file.startswith(codeunit_folder), f"Unexpected path for coverage-file. File: \"{file}\"; codeunitfolder: \"{codeunit_folder}\"")
|
|
864
|
-
filename_relative = f".{file[len(codeunit_folder):]}"
|
|
865
|
-
return f'filename="{filename_relative}"'
|
|
866
|
-
|
|
867
|
-
@staticmethod
|
|
868
|
-
@GeneralUtilities.check_arguments
|
|
869
|
-
def __remove_not_existing_files_from_testcoverage_file(testcoveragefile: str, repository_folder: str, codeunit_name: str) -> None:
|
|
870
|
-
root: etree._ElementTree = etree.parse(testcoveragefile)
|
|
871
|
-
codeunit_folder = os.path.join(repository_folder, codeunit_name)
|
|
872
|
-
xpath = f"//coverage/packages/package[@name='{codeunit_name}']/classes/class"
|
|
873
|
-
coverage_report_classes = root.xpath(xpath)
|
|
874
|
-
found_existing_files = False
|
|
875
|
-
for coverage_report_class in coverage_report_classes:
|
|
876
|
-
filename = coverage_report_class.attrib['filename']
|
|
877
|
-
file = os.path.join(codeunit_folder, filename)
|
|
878
|
-
if os.path.isfile(file):
|
|
879
|
-
found_existing_files = True
|
|
880
|
-
else:
|
|
881
|
-
coverage_report_class.getparent().remove(coverage_report_class)
|
|
882
|
-
GeneralUtilities.assert_condition(found_existing_files, f"No existing files in testcoderage-report-file \"{testcoveragefile}\".")
|
|
883
|
-
result = etree.tostring(root).decode("utf-8")
|
|
884
|
-
GeneralUtilities.write_text_to_file(testcoveragefile, result)
|
|
885
|
-
|
|
886
|
-
@GeneralUtilities.check_arguments
|
|
887
|
-
def __remove_unrelated_package_from_testcoverage_file(self, file: str, codeunit_name: str) -> None:
|
|
888
|
-
root: etree._ElementTree = etree.parse(file)
|
|
889
|
-
packages = root.xpath('//coverage/packages/package')
|
|
890
|
-
for package in packages:
|
|
891
|
-
if package.attrib['name'] != codeunit_name:
|
|
892
|
-
package.getparent().remove(package)
|
|
893
|
-
result = etree.tostring(root).decode("utf-8")
|
|
894
|
-
GeneralUtilities.write_text_to_file(file, result)
|
|
895
|
-
|
|
896
|
-
@GeneralUtilities.check_arguments
|
|
897
|
-
def write_version_to_codeunit_file(self, codeunit_file: str, current_version: str) -> None:
|
|
898
|
-
versionregex = "\\d+\\.\\d+\\.\\d+"
|
|
899
|
-
versiononlyregex = f"^{versionregex}$"
|
|
900
|
-
pattern = re.compile(versiononlyregex)
|
|
901
|
-
if pattern.match(current_version):
|
|
902
|
-
GeneralUtilities.write_text_to_file(codeunit_file, re.sub(f"<cps:version>{versionregex}<\\/cps:version>", f"<cps:version>{current_version}</cps:version>", GeneralUtilities.read_text_from_file(codeunit_file)))
|
|
903
|
-
else:
|
|
904
|
-
raise ValueError(f"Version '{current_version}' does not match version-regex '{versiononlyregex}'.")
|
|
905
|
-
|
|
906
|
-
@GeneralUtilities.check_arguments
|
|
907
|
-
def standardized_tasks_linting_for_dotnet_project(self, linting_script_file: str, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
908
|
-
GeneralUtilities.write_message_to_stdout("Run linting...")
|
|
909
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
910
|
-
# TODO check if there are errors in sarif-file
|
|
911
|
-
|
|
912
|
-
@GeneralUtilities.check_arguments
|
|
913
|
-
def __export_codeunit_reference_content_to_reference_repository(self, project_version_identifier: str, replace_existing_content: bool, target_folder_for_reference_repository: str, repository: str, codeunitname: str, projectname: str, codeunit_version: str, public_repository_url: str, branch: str) -> None:
|
|
914
|
-
codeunit_folder = os.path.join(repository, codeunitname)
|
|
915
|
-
codeunit_file = os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml")
|
|
916
|
-
codeunit_has_testcases = self.codeunit_has_testable_sourcecode(codeunit_file)
|
|
917
|
-
target_folder = os.path.join(target_folder_for_reference_repository, project_version_identifier, codeunitname)
|
|
918
|
-
if os.path.isdir(target_folder) and not replace_existing_content:
|
|
919
|
-
raise ValueError(f"Folder '{target_folder}' already exists.")
|
|
920
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
921
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
922
|
-
codeunit_version_identifier = "Latest" if project_version_identifier == "Latest" else "v"+codeunit_version
|
|
923
|
-
page_title = f"{codeunitname} {codeunit_version_identifier} codeunit-reference"
|
|
924
|
-
diff_report = f"{repository}/{codeunitname}/Other/Artifacts/DiffReport/DiffReport.html"
|
|
925
|
-
diff_target_folder = os.path.join(target_folder, "DiffReport")
|
|
926
|
-
GeneralUtilities.ensure_directory_exists(diff_target_folder)
|
|
927
|
-
diff_target_file = os.path.join(diff_target_folder, "DiffReport.html")
|
|
928
|
-
title = (f'Reference of codeunit {codeunitname} {codeunit_version_identifier} (contained in project <a href="{public_repository_url}">{projectname}</a> {project_version_identifier})')
|
|
929
|
-
if public_repository_url is None:
|
|
930
|
-
repo_url_html = ""
|
|
931
|
-
else:
|
|
932
|
-
repo_url_html = f'<a href="{public_repository_url}/tree/{branch}/{codeunitname}">Source-code</a>'
|
|
933
|
-
if codeunit_has_testcases:
|
|
934
|
-
coverage_report_link = '<a href="./TestCoverageReport/index.html">Test-coverage-report</a><br>'
|
|
935
|
-
else:
|
|
936
|
-
coverage_report_link = ""
|
|
937
|
-
index_file_for_reference = os.path.join(target_folder, "index.html")
|
|
938
|
-
|
|
939
|
-
design_file = None
|
|
940
|
-
design = "ModestDark"
|
|
941
|
-
if design == "ModestDark":
|
|
942
|
-
design_file = GeneralUtilities.get_modest_dark_url()
|
|
943
|
-
# TODO make designs from customizable sources be available by a customizable name and outsource this to a class-property because this is duplicated code.
|
|
944
|
-
if design_file is None:
|
|
945
|
-
design_html = ""
|
|
946
|
-
else:
|
|
947
|
-
design_html = f'<link type="text/css" rel="stylesheet" href="{design_file}" />'
|
|
948
|
-
|
|
949
|
-
index_file_content = f"""<!DOCTYPE html>
|
|
950
|
-
<html lang="en">
|
|
951
|
-
|
|
952
|
-
<head>
|
|
953
|
-
<meta charset="UTF-8">
|
|
954
|
-
<title>{page_title}</title>
|
|
955
|
-
{design_html}
|
|
956
|
-
</head>
|
|
957
|
-
|
|
958
|
-
<body>
|
|
959
|
-
<h1>{title}</h1>
|
|
960
|
-
<hr/>
|
|
961
|
-
Available reference-content for {codeunitname}:<br>
|
|
962
|
-
{repo_url_html}<br>
|
|
963
|
-
<!--TODO add artefacts-link: <a href="./x">Artefacts</a><br>-->
|
|
964
|
-
<a href="./Reference/index.html">Reference</a><br>
|
|
965
|
-
<a href="./DiffReport/DiffReport.html">Diff-report</a><br>
|
|
966
|
-
{coverage_report_link}
|
|
967
|
-
</body>
|
|
968
|
-
|
|
969
|
-
</html>
|
|
970
|
-
"""
|
|
971
|
-
|
|
972
|
-
GeneralUtilities.ensure_file_exists(index_file_for_reference)
|
|
973
|
-
GeneralUtilities.write_text_to_file(index_file_for_reference, index_file_content)
|
|
974
|
-
other_folder_in_repository = os.path.join(repository, codeunitname, "Other")
|
|
975
|
-
source_generatedreference = os.path.join(other_folder_in_repository, "Artifacts", "Reference")
|
|
976
|
-
target_generatedreference = os.path.join(target_folder, "Reference")
|
|
977
|
-
shutil.copytree(source_generatedreference, target_generatedreference)
|
|
978
|
-
|
|
979
|
-
shutil.copyfile(diff_report, diff_target_file)
|
|
980
|
-
|
|
981
|
-
if codeunit_has_testcases:
|
|
982
|
-
source_testcoveragereport = os.path.join(other_folder_in_repository, "Artifacts", "TestCoverageReport")
|
|
983
|
-
target_testcoveragereport = os.path.join(target_folder, "TestCoverageReport")
|
|
984
|
-
shutil.copytree(source_testcoveragereport, target_testcoveragereport)
|
|
985
|
-
|
|
986
|
-
@GeneralUtilities.check_arguments
|
|
987
|
-
def __standardized_tasks_release_artifact(self, information: CreateReleaseInformationForProjectInCommonProjectFormat) -> None:
|
|
988
|
-
project_version = self.__sc.get_semver_version_from_gitversion(information.repository)
|
|
989
|
-
target_folder_base = os.path.join(information.artifacts_folder, information.projectname, project_version)
|
|
990
|
-
GeneralUtilities.ensure_directory_exists(target_folder_base)
|
|
991
|
-
|
|
992
|
-
self.build_codeunits(information.repository, information.verbosity, information.target_environmenttype_for_productive, information.additional_arguments_file, False, information.export_target)
|
|
993
|
-
|
|
994
|
-
reference_folder = os.path.join(information.reference_repository, "ReferenceContent")
|
|
995
|
-
|
|
996
|
-
for codeunitname in self.get_codeunits(information.repository):
|
|
997
|
-
# Push artifacts to registry
|
|
998
|
-
scriptfilename = f"PushArtifacts.{codeunitname}.py"
|
|
999
|
-
push_artifact_to_registry_script = os.path.join(information.push_artifacts_scripts_folder, scriptfilename)
|
|
1000
|
-
if os.path.isfile(push_artifact_to_registry_script):
|
|
1001
|
-
GeneralUtilities.write_message_to_stdout(f"Push artifacts of codeunit {codeunitname}...")
|
|
1002
|
-
self.__sc.run_program("python", push_artifact_to_registry_script, information.push_artifacts_scripts_folder, verbosity=information.verbosity, throw_exception_if_exitcode_is_not_zero=True)
|
|
1003
|
-
|
|
1004
|
-
# Copy reference of codeunit to reference-repository
|
|
1005
|
-
codeunit_version = self.get_version_of_codeunit_folder(os.path.join(information.repository, codeunitname))
|
|
1006
|
-
self.__export_codeunit_reference_content_to_reference_repository(f"v{project_version}", False, reference_folder, information.repository, codeunitname, information.projectname, codeunit_version, information.public_repository_url, f"v{project_version}")
|
|
1007
|
-
self.__export_codeunit_reference_content_to_reference_repository("Latest", True, reference_folder, information.repository, codeunitname, information.projectname, codeunit_version, information.public_repository_url, information.target_branch_name)
|
|
1008
|
-
|
|
1009
|
-
# Generate reference
|
|
1010
|
-
self.__generate_entire_reference(information.projectname, project_version, reference_folder)
|
|
1011
|
-
|
|
1012
|
-
@staticmethod
|
|
1013
|
-
@GeneralUtilities.check_arguments
|
|
1014
|
-
def _internal_sort_reference_folder(folder1: str, folder2: str) -> int:
|
|
1015
|
-
"""Returns a value greater than 0 if and only if folder1 has a base-folder-name with a with a higher version than the base-folder-name of folder2.
|
|
1016
|
-
Returns a value lower than 0 if and only if folder1 has a base-folder-name with a with a lower version than the base-folder-name of folder2.
|
|
1017
|
-
Returns 0 if both values are equal."""
|
|
1018
|
-
if (folder1 == folder2):
|
|
1019
|
-
return 0
|
|
1020
|
-
|
|
1021
|
-
version_identifier_1 = os.path.basename(folder1)
|
|
1022
|
-
if version_identifier_1 == "Latest":
|
|
1023
|
-
return -1
|
|
1024
|
-
version_identifier_1 = version_identifier_1[1:]
|
|
1025
|
-
|
|
1026
|
-
version_identifier_2 = os.path.basename(folder2)
|
|
1027
|
-
if version_identifier_2 == "Latest":
|
|
1028
|
-
return 1
|
|
1029
|
-
version_identifier_2 = version_identifier_2[1:]
|
|
1030
|
-
|
|
1031
|
-
if version.parse(version_identifier_1) < version.parse(version_identifier_2):
|
|
1032
|
-
return -1
|
|
1033
|
-
elif version.parse(version_identifier_1) > version.parse(version_identifier_2):
|
|
1034
|
-
return 1
|
|
1035
|
-
else:
|
|
1036
|
-
return 0
|
|
1037
|
-
|
|
1038
|
-
@GeneralUtilities.check_arguments
|
|
1039
|
-
def __generate_entire_reference(self, projectname: str, project_version: str, reference_folder: str) -> None:
|
|
1040
|
-
all_available_version_identifier_folders_of_reference: list[str] = list(folder for folder in GeneralUtilities.get_direct_folders_of_folder(reference_folder))
|
|
1041
|
-
all_available_version_identifier_folders_of_reference = sorted(all_available_version_identifier_folders_of_reference, key=cmp_to_key(TasksForCommonProjectStructure._internal_sort_reference_folder))
|
|
1042
|
-
reference_versions_html_lines = []
|
|
1043
|
-
reference_versions_html_lines.append(' <hr/>')
|
|
1044
|
-
for all_available_version_identifier_folder_of_reference in all_available_version_identifier_folders_of_reference:
|
|
1045
|
-
version_identifier_of_project = os.path.basename(all_available_version_identifier_folder_of_reference)
|
|
1046
|
-
if version_identifier_of_project == "Latest":
|
|
1047
|
-
latest_version_hint = f" (v{project_version})"
|
|
1048
|
-
else:
|
|
1049
|
-
latest_version_hint = ""
|
|
1050
|
-
reference_versions_html_lines.append(f' <h2>{version_identifier_of_project}{latest_version_hint}</h2>')
|
|
1051
|
-
reference_versions_html_lines.append(" Contained codeunits:<br/>")
|
|
1052
|
-
reference_versions_html_lines.append(" <ul>")
|
|
1053
|
-
for codeunit_reference_folder in list(folder for folder in GeneralUtilities.get_direct_folders_of_folder(all_available_version_identifier_folder_of_reference)):
|
|
1054
|
-
reference_versions_html_lines.append(f' <li><a href="./{version_identifier_of_project}/{os.path.basename(codeunit_reference_folder)}/index.html">' +
|
|
1055
|
-
f'{os.path.basename(codeunit_reference_folder)} {version_identifier_of_project}</a></li>')
|
|
1056
|
-
reference_versions_html_lines.append(" </ul>")
|
|
1057
|
-
reference_versions_html_lines.append(' <hr/>')
|
|
1058
|
-
if version_identifier_of_project == "Latest":
|
|
1059
|
-
latest_version_hint = " <h2>History</h2>"
|
|
1060
|
-
|
|
1061
|
-
design_file = None
|
|
1062
|
-
design = "ModestDark"
|
|
1063
|
-
if design == "ModestDark":
|
|
1064
|
-
design_file = GeneralUtilities.get_modest_dark_url()
|
|
1065
|
-
# TODO make designs from customizable sources be available by a customizable name and outsource this to a class-property because this is duplicated code.
|
|
1066
|
-
if design_file is None:
|
|
1067
|
-
design_html = ""
|
|
1068
|
-
else:
|
|
1069
|
-
design_html = f'<link type="text/css" rel="stylesheet" href="{design_file}" />'
|
|
1070
|
-
|
|
1071
|
-
reference_versions_links_file_content = " \n".join(reference_versions_html_lines)
|
|
1072
|
-
title = f"{projectname}-reference"
|
|
1073
|
-
reference_index_file = os.path.join(reference_folder, "index.html")
|
|
1074
|
-
reference_index_file_content = f"""<!DOCTYPE html>
|
|
1075
|
-
<html lang="en">
|
|
1076
|
-
|
|
1077
|
-
<head>
|
|
1078
|
-
<meta charset="UTF-8">
|
|
1079
|
-
<title>{title}</title>
|
|
1080
|
-
{design_html}
|
|
1081
|
-
</head>
|
|
1082
|
-
|
|
1083
|
-
<body>
|
|
1084
|
-
<h1>{title}</h1>
|
|
1085
|
-
{reference_versions_links_file_content}
|
|
1086
|
-
</body>
|
|
1087
|
-
|
|
1088
|
-
</html>
|
|
1089
|
-
""" # see https://getbootstrap.com/docs/5.1/getting-started/introduction/
|
|
1090
|
-
GeneralUtilities.write_text_to_file(reference_index_file, reference_index_file_content)
|
|
1091
|
-
|
|
1092
|
-
@GeneralUtilities.check_arguments
|
|
1093
|
-
def push_nuget_build_artifact(self, push_script_file: str, codeunitname: str, registry_address: str, api_key: str, repository_folder_name: str):
|
|
1094
|
-
# when pusing to "default public" nuget-server then use registry_address: "nuget.org"
|
|
1095
|
-
build_artifact_folder = GeneralUtilities.resolve_relative_path(f"../../Submodules/{repository_folder_name}/{codeunitname}/Other/Artifacts/BuildResult_NuGet", os.path.dirname(push_script_file))
|
|
1096
|
-
self.__sc.push_nuget_build_artifact(self.__sc.find_file_by_extension(build_artifact_folder, "nupkg"), registry_address, api_key)
|
|
1097
|
-
|
|
1098
|
-
@GeneralUtilities.check_arguments
|
|
1099
|
-
def assert_no_uncommitted_changes(self, repository_folder: str):
|
|
1100
|
-
if self.__sc.git_repository_has_uncommitted_changes(repository_folder):
|
|
1101
|
-
raise ValueError(f"Repository '{repository_folder}' has uncommitted changes.")
|
|
1102
|
-
|
|
1103
|
-
@GeneralUtilities.check_arguments
|
|
1104
|
-
def ensure_certificate_authority_for_development_purposes_is_generated(self, script_file: str):
|
|
1105
|
-
folder_of_current_file = os.path.dirname(script_file)
|
|
1106
|
-
product_folder: str = GeneralUtilities.resolve_relative_path("../..", folder_of_current_file)
|
|
1107
|
-
product_name: str = os.path.basename(product_folder)
|
|
1108
|
-
now = datetime.now()
|
|
1109
|
-
ca_name = f"{product_name}CA_{now.year:04}{now.month:02}{now.day:02}{now.hour:02}{now.min:02}{now.second:02}"
|
|
1110
|
-
ca_folder = os.path.join(product_folder, "Other", "Resources", "CA")
|
|
1111
|
-
generate_certificate = True
|
|
1112
|
-
if os.path.isdir(ca_folder):
|
|
1113
|
-
try:
|
|
1114
|
-
ca_file = self.__sc.find_file_by_extension(ca_folder, "crt") # pylint: disable=unused-variable
|
|
1115
|
-
certificate_is_valid = True # TODO check if certificate is not valid
|
|
1116
|
-
generate_certificate = not certificate_is_valid
|
|
1117
|
-
except FileNotFoundError:
|
|
1118
|
-
pass
|
|
1119
|
-
if generate_certificate:
|
|
1120
|
-
self.__sc.generate_certificate_authority(ca_folder, ca_name, "DE", "SubjST", "SubjL", "SubjO", "SubjOU")
|
|
1121
|
-
|
|
1122
|
-
@GeneralUtilities.check_arguments
|
|
1123
|
-
def generate_certificate_for_development_purposes_for_external_service(self, service_folder: str, domain: str = None):
|
|
1124
|
-
testservice_name = os.path.basename(service_folder)
|
|
1125
|
-
ca_folder: str = None # TODO
|
|
1126
|
-
self.__generate_certificate_for_development_purposes(testservice_name, os.path.join(service_folder, "Resources"), ca_folder, domain)
|
|
1127
|
-
|
|
1128
|
-
@GeneralUtilities.check_arguments
|
|
1129
|
-
def generate_certificate_for_development_purposes_for_codeunit(self, codeunit_folder: str, domain: str = None):
|
|
1130
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1131
|
-
self.ensure_product_resource_is_imported(codeunit_folder, "CA")
|
|
1132
|
-
ca_folder: str = os.path.join(codeunit_folder, "Other", "Resources", "CA")
|
|
1133
|
-
self.__generate_certificate_for_development_purposes(codeunit_name, os.path.join(codeunit_folder, "Other", "Resources"), ca_folder, domain)
|
|
1134
|
-
|
|
1135
|
-
@GeneralUtilities.check_arguments
|
|
1136
|
-
def __generate_certificate_for_development_purposes(self, service_name: str, resources_folder: str, ca_folder: str, domain: str = None):
|
|
1137
|
-
if domain is None:
|
|
1138
|
-
domain = f"{service_name}.test.local"
|
|
1139
|
-
domain = domain.lower()
|
|
1140
|
-
resource_name: str = "DevelopmentCertificate"
|
|
1141
|
-
certificate_folder: str = os.path.join(resources_folder, resource_name)
|
|
1142
|
-
|
|
1143
|
-
resource_content_filename: str = service_name+resource_name
|
|
1144
|
-
certificate_file = os.path.join(certificate_folder, f"{domain}.crt")
|
|
1145
|
-
unsignedcertificate_file = os.path.join(certificate_folder, f"{domain}.unsigned.crt")
|
|
1146
|
-
certificate_exists = os.path.exists(certificate_file)
|
|
1147
|
-
if certificate_exists:
|
|
1148
|
-
certificate_expired = GeneralUtilities.certificate_is_expired(certificate_file)
|
|
1149
|
-
generate_new_certificate = certificate_expired
|
|
1150
|
-
else:
|
|
1151
|
-
generate_new_certificate = True
|
|
1152
|
-
if generate_new_certificate:
|
|
1153
|
-
GeneralUtilities.ensure_directory_does_not_exist(certificate_folder)
|
|
1154
|
-
GeneralUtilities.ensure_directory_exists(certificate_folder)
|
|
1155
|
-
GeneralUtilities.write_message_to_stdout("Generate TLS-certificate for development-purposes.")
|
|
1156
|
-
self.__sc.generate_certificate(certificate_folder, domain, resource_content_filename, "DE", "SubjST", "SubjL", "SubjO", "SubjOU")
|
|
1157
|
-
self.__sc.generate_certificate_sign_request(certificate_folder, domain, resource_content_filename, "DE", "SubjST", "SubjL", "SubjO", "SubjOU")
|
|
1158
|
-
ca_name = os.path.basename(self.__sc.find_file_by_extension(ca_folder, "crt"))[:-4]
|
|
1159
|
-
self.__sc.sign_certificate(certificate_folder, ca_folder, ca_name, domain, resource_content_filename)
|
|
1160
|
-
GeneralUtilities.ensure_file_does_not_exist(unsignedcertificate_file)
|
|
1161
|
-
|
|
1162
|
-
@GeneralUtilities.check_arguments
|
|
1163
|
-
def ensure_product_resource_is_imported(self, codeunit_folder: str, product_resource_name: str) -> None:
|
|
1164
|
-
product_folder = os.path.dirname(codeunit_folder)
|
|
1165
|
-
source_folder = os.path.join(product_folder, "Other", "Resources", product_resource_name)
|
|
1166
|
-
target_folder = os.path.join(codeunit_folder, "Other", "Resources", product_resource_name)
|
|
1167
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
1168
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
1169
|
-
GeneralUtilities.copy_content_of_folder(source_folder, target_folder)
|
|
1170
|
-
|
|
1171
|
-
@GeneralUtilities.check_arguments
|
|
1172
|
-
def get_codeunits(self, repository_folder: str, ignore_disabled_codeunits: bool = True) -> list[str]:
|
|
1173
|
-
result: list[str] = []
|
|
1174
|
-
for direct_subfolder in GeneralUtilities.get_direct_folders_of_folder(repository_folder):
|
|
1175
|
-
subfoldername = os.path.basename(direct_subfolder)
|
|
1176
|
-
codeunit_file = os.path.join(direct_subfolder, f"{subfoldername}.codeunit.xml")
|
|
1177
|
-
if os.path.isfile(codeunit_file):
|
|
1178
|
-
if (ignore_disabled_codeunits):
|
|
1179
|
-
if (self.codeunit_is_enabled(codeunit_file)):
|
|
1180
|
-
result.append(subfoldername)
|
|
1181
|
-
else:
|
|
1182
|
-
result.append(subfoldername)
|
|
1183
|
-
return result
|
|
1184
|
-
|
|
1185
|
-
@GeneralUtilities.check_arguments
|
|
1186
|
-
def codeunit_is_enabled(self, codeunit_file: str) -> bool:
|
|
1187
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
1188
|
-
return GeneralUtilities.string_to_boolean(str(root.xpath('//cps:codeunit/@enabled', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'})[0]))
|
|
1189
|
-
|
|
1190
|
-
@GeneralUtilities.check_arguments
|
|
1191
|
-
def merge_to_main_branch(self, repository_folder: str, source_branch: str = "other/next-release", target_branch: str = "main", verbosity: int = 1, additional_arguments_file: str = None, fast_forward_source_branch: bool = False) -> None:
|
|
1192
|
-
# This is an automatization for automatic merges. Usual this merge would be done by a pull request in a sourcecode-version-control-platform
|
|
1193
|
-
# (like GitHub, GitLab or Azure DevOps)
|
|
1194
|
-
self.assert_no_uncommitted_changes(repository_folder)
|
|
1195
|
-
|
|
1196
|
-
src_branch_commit_id = self.__sc.git_get_commit_id(repository_folder, source_branch)
|
|
1197
|
-
if (src_branch_commit_id == self.__sc.git_get_commit_id(repository_folder, target_branch)):
|
|
1198
|
-
raise ValueError(f"Can not merge because the source-branch and the target-branch are on the same commit (commit-id: {src_branch_commit_id})")
|
|
1199
|
-
|
|
1200
|
-
self.__sc.git_checkout(repository_folder, source_branch)
|
|
1201
|
-
self.build_codeunits(repository_folder, verbosity, "QualityCheck", additional_arguments_file, True, None)
|
|
1202
|
-
self.__sc.git_merge(repository_folder, source_branch, target_branch, False, False, None)
|
|
1203
|
-
self.__sc.git_commit(repository_folder, f'Merge branch {source_branch} into {target_branch}', stage_all_changes=True, no_changes_behavior=1)
|
|
1204
|
-
self.__sc.git_checkout(repository_folder, target_branch)
|
|
1205
|
-
if fast_forward_source_branch:
|
|
1206
|
-
self.__sc.git_merge(repository_folder, target_branch, source_branch, True, True)
|
|
1207
|
-
|
|
1208
|
-
@GeneralUtilities.check_arguments
|
|
1209
|
-
def merge_to_stable_branch(self, create_release_file: str, createRelease_configuration: CreateReleaseConfiguration):
|
|
1210
|
-
|
|
1211
|
-
GeneralUtilities.write_message_to_stdout(f"Create release for project {createRelease_configuration.projectname}.")
|
|
1212
|
-
folder_of_create_release_file_file = os.path.abspath(os.path.dirname(create_release_file))
|
|
1213
|
-
|
|
1214
|
-
build_repository_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}..", folder_of_create_release_file_file)
|
|
1215
|
-
self.assert_no_uncommitted_changes(build_repository_folder)
|
|
1216
|
-
|
|
1217
|
-
self.__sc.git_checkout(build_repository_folder, createRelease_configuration.build_repository_branch)
|
|
1218
|
-
|
|
1219
|
-
repository_folder = GeneralUtilities.resolve_relative_path(f"Submodules{os.path.sep}{createRelease_configuration.repository_folder_name}", build_repository_folder)
|
|
1220
|
-
mergeInformation = MergeToStableBranchInformationForProjectInCommonProjectFormat(repository_folder, createRelease_configuration.additional_arguments_file, createRelease_configuration.artifacts_folder)
|
|
1221
|
-
|
|
1222
|
-
# TODO check if repository_folder-merge-source-branch and repository_folder-merge-target-branch have different commits
|
|
1223
|
-
self.assert_no_uncommitted_changes(repository_folder)
|
|
1224
|
-
mergeInformation.verbosity = createRelease_configuration.verbosity
|
|
1225
|
-
mergeInformation.push_target_branch = createRelease_configuration.remotename is not None
|
|
1226
|
-
mergeInformation.push_target_branch_remote_name = createRelease_configuration.remotename
|
|
1227
|
-
mergeInformation.push_source_branch = createRelease_configuration.remotename is not None
|
|
1228
|
-
mergeInformation.push_source_branch_remote_name = createRelease_configuration.remotename
|
|
1229
|
-
new_project_version = self.__standardized_tasks_merge_to_stable_branch(mergeInformation)
|
|
1230
|
-
|
|
1231
|
-
createReleaseInformation = CreateReleaseInformationForProjectInCommonProjectFormat(repository_folder, createRelease_configuration.artifacts_folder, createRelease_configuration.projectname, createRelease_configuration.public_repository_url, mergeInformation.targetbranch, mergeInformation.additional_arguments_file, mergeInformation.export_target, createRelease_configuration.push_artifacts_scripts_folder)
|
|
1232
|
-
createReleaseInformation.verbosity = createRelease_configuration.verbosity
|
|
1233
|
-
self.__standardized_tasks_release_artifact(createReleaseInformation)
|
|
1234
|
-
|
|
1235
|
-
self.__sc.git_commit(createReleaseInformation.reference_repository, f"Added reference of {createRelease_configuration.projectname} v{new_project_version}")
|
|
1236
|
-
if createRelease_configuration.reference_repository_remote_name is not None:
|
|
1237
|
-
self.__sc.git_push(createReleaseInformation.reference_repository, createRelease_configuration.reference_repository_remote_name, createRelease_configuration.reference_repository_branch_name, createRelease_configuration.reference_repository_branch_name, verbosity=createRelease_configuration.verbosity)
|
|
1238
|
-
self.__sc.git_commit(build_repository_folder, f"Added {createRelease_configuration.projectname} release v{new_project_version}")
|
|
1239
|
-
GeneralUtilities.write_message_to_stdout(f"Finished release for project {createRelease_configuration.projectname} v{new_project_version} successfully.")
|
|
1240
|
-
return new_project_version
|
|
1241
|
-
|
|
1242
|
-
@GeneralUtilities.check_arguments
|
|
1243
|
-
def create_release_starter_for_repository_in_standardized_format(self, create_release_file: str, logfile: str, verbosity: int, addLogOverhead: bool, commandline_arguments: list[str]):
|
|
1244
|
-
# hint: arguments can be overwritten by commandline_arguments
|
|
1245
|
-
folder_of_this_file = os.path.dirname(create_release_file)
|
|
1246
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1247
|
-
self.__sc.run_program("python", f"CreateRelease.py --overwrite_verbosity {str(verbosity)}", folder_of_this_file, verbosity=verbosity, log_file=logfile, addLogOverhead=addLogOverhead)
|
|
1248
|
-
|
|
1249
|
-
@GeneralUtilities.check_arguments
|
|
1250
|
-
def __standardized_tasks_merge_to_stable_branch(self, information: MergeToStableBranchInformationForProjectInCommonProjectFormat) -> str:
|
|
1251
|
-
src_branch_commit_id = self.__sc.git_get_commit_id(information.repository, information.sourcebranch)
|
|
1252
|
-
if (src_branch_commit_id == self.__sc.git_get_commit_id(information.repository, information.targetbranch)):
|
|
1253
|
-
raise ValueError(f"Can not merge because the source-branch and the target-branch are on the same commit (commit-id: {src_branch_commit_id})")
|
|
1254
|
-
|
|
1255
|
-
self.assert_no_uncommitted_changes(information.repository)
|
|
1256
|
-
self.__sc.git_checkout(information.repository, information.sourcebranch)
|
|
1257
|
-
self.__sc.run_program("git", "clean -dfx", information.repository, verbosity=information.verbosity, throw_exception_if_exitcode_is_not_zero=True)
|
|
1258
|
-
project_version = self.__sc.get_semver_version_from_gitversion(information.repository)
|
|
1259
|
-
|
|
1260
|
-
self.build_codeunits(information.repository, information.verbosity, information.target_environmenttype_for_qualitycheck, information.additional_arguments_file, False, information.export_target)
|
|
1261
|
-
|
|
1262
|
-
self.assert_no_uncommitted_changes(information.repository)
|
|
1263
|
-
|
|
1264
|
-
commit_id = self.__sc.git_merge(information.repository, information.sourcebranch, information.targetbranch, True, True)
|
|
1265
|
-
self.__sc.git_create_tag(information.repository, commit_id, f"v{project_version}", information.sign_git_tags)
|
|
1266
|
-
|
|
1267
|
-
if information.push_source_branch:
|
|
1268
|
-
GeneralUtilities.write_message_to_stdout("Push source-branch...")
|
|
1269
|
-
self.__sc.git_push(information.repository, information.push_source_branch_remote_name, information.sourcebranch, information.sourcebranch, pushalltags=True, verbosity=information.verbosity)
|
|
1270
|
-
|
|
1271
|
-
if information.push_target_branch:
|
|
1272
|
-
GeneralUtilities.write_message_to_stdout("Push target-branch...")
|
|
1273
|
-
self.__sc.git_push(information.repository, information.push_target_branch_remote_name, information.targetbranch, information.targetbranch, pushalltags=True, verbosity=information.verbosity)
|
|
1274
|
-
|
|
1275
|
-
return project_version
|
|
1276
|
-
|
|
1277
|
-
@GeneralUtilities.check_arguments
|
|
1278
|
-
def standardized_tasks_build_for_docker_project(self, build_script_file: str, target_environment_type: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1279
|
-
use_cache: bool = False
|
|
1280
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1281
|
-
sc: ScriptCollectionCore = ScriptCollectionCore()
|
|
1282
|
-
codeunitname: str = Path(os.path.dirname(build_script_file)).parent.parent.name
|
|
1283
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", str(os.path.dirname(build_script_file)))
|
|
1284
|
-
codeunitname_lower = codeunitname.lower()
|
|
1285
|
-
codeunitversion = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml"))
|
|
1286
|
-
args = ["image", "build", "--pull", "--force-rm", "--progress=plain", "--build-arg", f"TargetEnvironmentType={target_environment_type}", "--build-arg", f"Version={codeunitversion}", "--tag", f"{codeunitname_lower}:latest", "--tag", f"{codeunitname_lower}:{codeunitversion}", "--file", f"{codeunitname}/Dockerfile"]
|
|
1287
|
-
if not use_cache:
|
|
1288
|
-
args.append("--no-cache")
|
|
1289
|
-
args.append(".")
|
|
1290
|
-
codeunit_content_folder = os.path.join(codeunit_folder)
|
|
1291
|
-
sc.run_program_argsasarray("docker", args, codeunit_content_folder, verbosity=verbosity, print_errors_as_information=True)
|
|
1292
|
-
artifacts_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts", codeunit_folder)
|
|
1293
|
-
app_artifacts_folder = os.path.join(artifacts_folder, "BuildResult_OCIImage")
|
|
1294
|
-
GeneralUtilities.ensure_directory_does_not_exist(app_artifacts_folder)
|
|
1295
|
-
GeneralUtilities.ensure_directory_exists(app_artifacts_folder)
|
|
1296
|
-
self.__sc.run_program_argsasarray("docker", ["save", "--output", f"{codeunitname}_v{codeunitversion}.tar", f"{codeunitname_lower}:{codeunitversion}"], app_artifacts_folder, verbosity=verbosity, print_errors_as_information=True)
|
|
1297
|
-
self.copy_source_files_to_output_directory(build_script_file)
|
|
1298
|
-
|
|
1299
|
-
@GeneralUtilities.check_arguments
|
|
1300
|
-
def generate_sbom_for_docker_image(self, build_script_file: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1301
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1302
|
-
codeunitname: str = Path(os.path.dirname(build_script_file)).parent.parent.name
|
|
1303
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", str(os.path.dirname(build_script_file)))
|
|
1304
|
-
artifacts_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts", codeunit_folder)
|
|
1305
|
-
codeunitname_lower = codeunitname.lower()
|
|
1306
|
-
sbom_folder = os.path.join(artifacts_folder, "BOM")
|
|
1307
|
-
codeunitversion = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml"))
|
|
1308
|
-
GeneralUtilities.ensure_directory_exists(sbom_folder)
|
|
1309
|
-
self.__sc.run_program_argsasarray("docker", ["sbom", "--format", "cyclonedx", f"{codeunitname_lower}:{codeunitversion}", "--output", f"{codeunitname}.{codeunitversion}.sbom.xml"], sbom_folder, verbosity=verbosity, print_errors_as_information=True)
|
|
1310
|
-
|
|
1311
|
-
@GeneralUtilities.check_arguments
|
|
1312
|
-
def push_docker_build_artifact(self, push_artifacts_file: str, registry: str, verbosity: int, push_readme: bool, commandline_arguments: list[str], repository_folder_name: str) -> None:
|
|
1313
|
-
folder_of_this_file = os.path.dirname(push_artifacts_file)
|
|
1314
|
-
filename = os.path.basename(push_artifacts_file)
|
|
1315
|
-
codeunitname_regex: str = "([a-zA-Z0-9]+)"
|
|
1316
|
-
filename_regex: str = f"PushArtifacts\\.{codeunitname_regex}\\.py"
|
|
1317
|
-
if match := re.search(filename_regex, filename, re.IGNORECASE):
|
|
1318
|
-
codeunitname = match.group(1)
|
|
1319
|
-
else:
|
|
1320
|
-
raise ValueError(f"Expected push-artifacts-file to match the regex \"{filename_regex}\" where \"{codeunitname_regex}\" represents the codeunit-name.")
|
|
1321
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1322
|
-
repository_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}..{os.path.sep}Submodules{os.path.sep}{repository_folder_name}", folder_of_this_file)
|
|
1323
|
-
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
1324
|
-
artifacts_folder = self.get_artifacts_folder(repository_folder, codeunitname)
|
|
1325
|
-
applicationimage_folder = os.path.join(artifacts_folder, "BuildResult_OCIImage")
|
|
1326
|
-
sc = ScriptCollectionCore()
|
|
1327
|
-
image_file = sc.find_file_by_extension(applicationimage_folder, "tar")
|
|
1328
|
-
image_filename = os.path.basename(image_file)
|
|
1329
|
-
codeunit_version = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml"))
|
|
1330
|
-
image_tag_name = codeunitname.lower()
|
|
1331
|
-
repo = f"{registry}/{image_tag_name}"
|
|
1332
|
-
image_latest = f"{repo}:latest"
|
|
1333
|
-
image_version = f"{repo}:{codeunit_version}"
|
|
1334
|
-
GeneralUtilities.write_message_to_stdout("Load image...")
|
|
1335
|
-
sc.run_program("docker", f"load --input {image_filename}", applicationimage_folder, verbosity=verbosity)
|
|
1336
|
-
GeneralUtilities.write_message_to_stdout("Tag image...")
|
|
1337
|
-
sc.run_program("docker", f"tag {image_tag_name}:{codeunit_version} {image_latest}", verbosity=verbosity)
|
|
1338
|
-
sc.run_program("docker", f"tag {image_tag_name}:{codeunit_version} {image_version}", verbosity=verbosity)
|
|
1339
|
-
GeneralUtilities.write_message_to_stdout("Push image...")
|
|
1340
|
-
sc.run_program("docker", f"push {image_latest}", verbosity=verbosity)
|
|
1341
|
-
sc.run_program("docker", f"push {image_version}", verbosity=verbosity)
|
|
1342
|
-
if push_readme:
|
|
1343
|
-
sc.run_program("docker", f"pushrm {repo}", codeunit_folder, verbosity=verbosity)
|
|
1344
|
-
|
|
1345
|
-
@GeneralUtilities.check_arguments
|
|
1346
|
-
def get_dependent_code_units(self, codeunit_file: str) -> set[str]:
|
|
1347
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
1348
|
-
return set(root.xpath('//cps:dependentcodeunit/text()', namespaces={'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'}))
|
|
1349
|
-
|
|
1350
|
-
@GeneralUtilities.check_arguments
|
|
1351
|
-
def dependent_codeunit_exists(self, repository: str, codeunit: str) -> None:
|
|
1352
|
-
codeunit_file = f"{repository}/{codeunit}/{codeunit}.codeunit.xml"
|
|
1353
|
-
return os.path.isfile(codeunit_file)
|
|
1354
|
-
|
|
1355
|
-
@GeneralUtilities.check_arguments
|
|
1356
|
-
def standardized_tasks_linting_for_docker_project(self, linting_script_file: str, verbosity: int, targetenvironmenttype: str, commandline_arguments: list[str]) -> None:
|
|
1357
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1358
|
-
# TODO check if there are errors in sarif-file
|
|
1359
|
-
|
|
1360
|
-
@GeneralUtilities.check_arguments
|
|
1361
|
-
def copy_licence_file(self, common_tasks_scripts_file: str) -> None:
|
|
1362
|
-
folder_of_current_file = os.path.dirname(common_tasks_scripts_file)
|
|
1363
|
-
license_file = GeneralUtilities.resolve_relative_path("../../License.txt", folder_of_current_file)
|
|
1364
|
-
target_folder = GeneralUtilities.resolve_relative_path("Artifacts/License", folder_of_current_file)
|
|
1365
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
1366
|
-
shutil.copy(license_file, target_folder)
|
|
1367
|
-
|
|
1368
|
-
@GeneralUtilities.check_arguments
|
|
1369
|
-
def take_readmefile_from_main_readmefile_of_repository(self, common_tasks_scripts_file: str) -> None:
|
|
1370
|
-
folder_of_current_file = os.path.dirname(common_tasks_scripts_file)
|
|
1371
|
-
source_file = GeneralUtilities.resolve_relative_path("../../ReadMe.md", folder_of_current_file)
|
|
1372
|
-
target_file = GeneralUtilities.resolve_relative_path("../ReadMe.md", folder_of_current_file)
|
|
1373
|
-
GeneralUtilities.ensure_file_does_not_exist(target_file)
|
|
1374
|
-
shutil.copyfile(source_file, target_file)
|
|
1375
|
-
|
|
1376
|
-
@GeneralUtilities.check_arguments
|
|
1377
|
-
def standardized_tasks_do_common_tasks(self, common_tasks_scripts_file: str, codeunit_version: str, verbosity: int, targetenvironmenttype: str, clear_artifacts_folder: bool, additional_arguments_file: str, assume_dependent_codeunits_are_already_built: bool, commandline_arguments: list[str]) -> None:
|
|
1378
|
-
additional_arguments_file = self.get_additionalargumentsfile_from_commandline_arguments(commandline_arguments, additional_arguments_file)
|
|
1379
|
-
target_environmenttype = self.get_targetenvironmenttype_from_commandline_arguments(commandline_arguments, targetenvironmenttype)
|
|
1380
|
-
assume_dependent_codeunits_are_already_built = self.get_assume_dependent_codeunits_are_already_built_from_commandline_arguments(commandline_arguments, assume_dependent_codeunits_are_already_built)
|
|
1381
|
-
if commandline_arguments is None:
|
|
1382
|
-
raise ValueError('The "commandline_arguments"-parameter is not defined.')
|
|
1383
|
-
if len(commandline_arguments) == 0:
|
|
1384
|
-
raise ValueError('An empty array as argument for the "commandline_arguments"-parameter is not valid.')
|
|
1385
|
-
commandline_arguments = commandline_arguments[1:]
|
|
1386
|
-
repository_folder: str = str(Path(os.path.dirname(common_tasks_scripts_file)).parent.parent.absolute())
|
|
1387
|
-
codeunit_name: str = str(os.path.basename(Path(os.path.dirname(common_tasks_scripts_file)).parent.absolute()))
|
|
1388
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1389
|
-
project_version = self.get_version_of_project(repository_folder)
|
|
1390
|
-
codeunit_folder = os.path.join(repository_folder, codeunit_name)
|
|
1391
|
-
|
|
1392
|
-
# Check codeunit-conformity
|
|
1393
|
-
# TODO check if foldername=="<codeunitname>[.codeunit.xml]" == <codeunitname> in file
|
|
1394
|
-
supported_codeunitspecificationversion = "2.9.0" # should always be the latest version of the ProjectTemplates-repository
|
|
1395
|
-
codeunit_file = os.path.join(codeunit_folder, f"{codeunit_name}.codeunit.xml")
|
|
1396
|
-
if not os.path.isfile(codeunit_file):
|
|
1397
|
-
raise ValueError(f'Codeunitfile "{codeunit_file}" does not exist.')
|
|
1398
|
-
# TODO implement usage of self.reference_latest_version_of_xsd_when_generating_xml
|
|
1399
|
-
namespaces = {'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure',
|
|
1400
|
-
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
|
|
1401
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
1402
|
-
|
|
1403
|
-
# Check codeunit-spcecification-version
|
|
1404
|
-
try:
|
|
1405
|
-
codeunit_file_version = root.xpath('//cps:codeunit/@codeunitspecificationversion', namespaces=namespaces)[0]
|
|
1406
|
-
if codeunit_file_version != supported_codeunitspecificationversion:
|
|
1407
|
-
raise ValueError(f"ScriptCollection only supports processing codeunits with codeunit-specification-version={supported_codeunitspecificationversion}.")
|
|
1408
|
-
schemaLocation = root.xpath('//cps:codeunit/@xsi:schemaLocation', namespaces=namespaces)[0]
|
|
1409
|
-
xmlschema.validate(codeunit_file, schemaLocation)
|
|
1410
|
-
# TODO check if the properties codeunithastestablesourcecode, codeunithasupdatabledependencies, throwexceptionifcodeunitfilecannotbevalidated, developmentState and description exist and the values are valid
|
|
1411
|
-
except Exception as exception:
|
|
1412
|
-
if self.codeunit_throws_exception_if_codeunitfile_is_not_validatable(codeunit_file):
|
|
1413
|
-
raise exception
|
|
1414
|
-
else:
|
|
1415
|
-
GeneralUtilities.write_message_to_stderr(f'Warning: Codeunitfile "{codeunit_file}" can not be validated due to the following exception:')
|
|
1416
|
-
GeneralUtilities.write_exception_to_stderr(exception)
|
|
1417
|
-
|
|
1418
|
-
# Check codeunit-name
|
|
1419
|
-
codeunit_name_in_codeunit_file = root.xpath('//cps:codeunit/cps:name/text()', namespaces=namespaces)[0]
|
|
1420
|
-
if codeunit_name != codeunit_name_in_codeunit_file:
|
|
1421
|
-
raise ValueError(f"The folder-name ('{codeunit_name}') is not equal to the codeunit-name ('{codeunit_name_in_codeunit_file}').")
|
|
1422
|
-
|
|
1423
|
-
# Check owner-name
|
|
1424
|
-
codeunit_ownername_in_codeunit_file = root.xpath('//cps:codeunit/cps:codeunitownername/text()', namespaces=namespaces)[0]
|
|
1425
|
-
GeneralUtilities.assert_condition(GeneralUtilities.string_has_content(codeunit_ownername_in_codeunit_file), "No valid name for codeunitowner given.")
|
|
1426
|
-
|
|
1427
|
-
# Check owner-emailaddress
|
|
1428
|
-
codeunit_owneremailaddress_in_codeunit_file = root.xpath('//cps:codeunit/cps:codeunitowneremailaddress/text()', namespaces=namespaces)[0]
|
|
1429
|
-
GeneralUtilities.assert_condition(GeneralUtilities.string_has_content(codeunit_owneremailaddress_in_codeunit_file), "No valid email-address for codeunitowner given.")
|
|
1430
|
-
|
|
1431
|
-
# Check for mandatory files
|
|
1432
|
-
files = ["Other/Build/Build.py", "Other/QualityCheck/Linting.py", "Other/Reference/GenerateReference.py"]
|
|
1433
|
-
if self.codeunit_has_testable_sourcecode(codeunit_file):
|
|
1434
|
-
# TODO check if the testsettings-section appears in the codeunit-file
|
|
1435
|
-
files.append("Other/QualityCheck/RunTestcases.py")
|
|
1436
|
-
if self.codeunit_has_updatable_dependencies(codeunit_file):
|
|
1437
|
-
# TODO check if the updatesettings-section appears in the codeunit-file
|
|
1438
|
-
files.append("Other/UpdateDependencies.py")
|
|
1439
|
-
for file in files:
|
|
1440
|
-
combined_file = os.path.join(codeunit_folder, file)
|
|
1441
|
-
if not os.path.isfile(combined_file):
|
|
1442
|
-
raise ValueError(f'The mandatory file "{file}" does not exist in the codeunit-folder.')
|
|
1443
|
-
|
|
1444
|
-
# Check developer
|
|
1445
|
-
if self.validate_developers_of_repository:
|
|
1446
|
-
expected_authors: list[tuple[str, str]] = []
|
|
1447
|
-
expected_authors_in_xml = root.xpath('//cps:codeunit/cps:developerteam/cps:developer', namespaces=namespaces)
|
|
1448
|
-
for expected_author in expected_authors_in_xml:
|
|
1449
|
-
author_name = expected_author.xpath('./cps:developername/text()', namespaces=namespaces)[0]
|
|
1450
|
-
author_emailaddress = expected_author.xpath('./cps:developeremailaddress/text()', namespaces=namespaces)[0]
|
|
1451
|
-
expected_authors.append((author_name, author_emailaddress))
|
|
1452
|
-
actual_authors: list[tuple[str, str]] = self.__sc.get_all_authors_and_committers_of_repository(repository_folder, codeunit_name, verbosity)
|
|
1453
|
-
for actual_author in actual_authors:
|
|
1454
|
-
if not (actual_author) in expected_authors:
|
|
1455
|
-
actual_author_formatted = f"{actual_author[0]} <{actual_author[1]}>"
|
|
1456
|
-
raise ValueError(f'Author/Comitter "{actual_author_formatted}" is not in the codeunit-developer-team. If {actual_author} is a authorized developer for this codeunit you should consider defining this in the codeunit-file or adapting the name using a .mailmap-file (see https://git-scm.com/docs/gitmailmap). The developer-team-check can also be disabled using the property validate_developers_of_repository.')
|
|
1457
|
-
|
|
1458
|
-
dependent_codeunits = self.get_dependent_code_units(codeunit_file)
|
|
1459
|
-
for dependent_codeunit in dependent_codeunits:
|
|
1460
|
-
if not self.dependent_codeunit_exists(repository_folder, dependent_codeunit):
|
|
1461
|
-
raise ValueError(f"Codeunit {codeunit_name} does have dependent codeunit {dependent_codeunit} which does not exist.")
|
|
1462
|
-
|
|
1463
|
-
# TODO implement cycle-check for dependent codeunits
|
|
1464
|
-
|
|
1465
|
-
# Clear previously builded artifacts if desired:
|
|
1466
|
-
if clear_artifacts_folder:
|
|
1467
|
-
artifacts_folder = os.path.join(codeunit_folder, "Other", "Artifacts")
|
|
1468
|
-
GeneralUtilities.ensure_directory_does_not_exist(artifacts_folder)
|
|
1469
|
-
|
|
1470
|
-
# Get artifacts from dependent codeunits
|
|
1471
|
-
if assume_dependent_codeunits_are_already_built:
|
|
1472
|
-
self.build_dependent_code_units(repository_folder, codeunit_name, verbosity, target_environmenttype, additional_arguments_file, commandline_arguments)
|
|
1473
|
-
self.copy_artifacts_from_dependent_code_units(repository_folder, codeunit_name)
|
|
1474
|
-
|
|
1475
|
-
# Update codeunit-version
|
|
1476
|
-
self.update_version_of_codeunit(common_tasks_scripts_file, codeunit_version)
|
|
1477
|
-
|
|
1478
|
-
# set default constants
|
|
1479
|
-
self.set_default_constants(os.path.join(codeunit_folder))
|
|
1480
|
-
|
|
1481
|
-
# Copy changelog-file
|
|
1482
|
-
changelog_folder = os.path.join(repository_folder, "Other", "Resources", "Changelog")
|
|
1483
|
-
changelog_file = os.path.join(changelog_folder, f"v{project_version}.md")
|
|
1484
|
-
target_folder = os.path.join(codeunit_folder, "Other", "Artifacts", "Changelog")
|
|
1485
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
1486
|
-
shutil.copy(changelog_file, target_folder)
|
|
1487
|
-
|
|
1488
|
-
# Hints-file
|
|
1489
|
-
hints_file = os.path.join(codeunit_folder, "Other", "Hints.md")
|
|
1490
|
-
if not os.path.isfile(hints_file):
|
|
1491
|
-
raise ValueError(f"Hints-file '{hints_file}' does not exist.")
|
|
1492
|
-
|
|
1493
|
-
# Copy license-file
|
|
1494
|
-
self.copy_licence_file(common_tasks_scripts_file)
|
|
1495
|
-
|
|
1496
|
-
# Generate diff-report
|
|
1497
|
-
self.generate_diff_report(repository_folder, codeunit_name, codeunit_version)
|
|
1498
|
-
|
|
1499
|
-
@GeneralUtilities.check_arguments
|
|
1500
|
-
def generate_diff_report(self, repository_folder: str, codeunit_name: str, current_version: str) -> None:
|
|
1501
|
-
codeunit_folder = os.path.join(repository_folder, codeunit_name)
|
|
1502
|
-
target_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts/DiffReport", codeunit_folder)
|
|
1503
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
1504
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
1505
|
-
target_file = os.path.join(target_folder, "DiffReport.html").replace("\\", "/")
|
|
1506
|
-
src = "4b825dc642cb6eb9a060e54bf8d69288fbee4904" # hash/id of empty git-tree
|
|
1507
|
-
src_prefix = "Begin"
|
|
1508
|
-
if self.__sc.get_current_git_branch_has_tag(repository_folder):
|
|
1509
|
-
latest_tag = self.__sc.get_latest_git_tag(repository_folder)
|
|
1510
|
-
src = self.__sc.git_get_commitid_of_tag(repository_folder, latest_tag)
|
|
1511
|
-
src_prefix = latest_tag
|
|
1512
|
-
dst = "HEAD"
|
|
1513
|
-
dst_prefix = f"v{current_version}"
|
|
1514
|
-
self.__sc.run_program_argsasarray("sh", ['-c', f'git diff --src-prefix={src_prefix}/ --dst-prefix={dst_prefix}/ {src} {dst} -- {codeunit_name} | pygmentize -l diff -f html -O full -o {target_file} -P style=github-dark'], repository_folder)
|
|
1515
|
-
|
|
1516
|
-
@GeneralUtilities.check_arguments
|
|
1517
|
-
def get_version_of_project(self, repository_folder: str) -> str:
|
|
1518
|
-
return ScriptCollectionCore().get_semver_version_from_gitversion(repository_folder)
|
|
1519
|
-
|
|
1520
|
-
@GeneralUtilities.check_arguments
|
|
1521
|
-
def replace_common_variables_in_nuspec_file(self, codeunit_folder: str) -> None:
|
|
1522
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1523
|
-
codeunit_version = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
1524
|
-
nuspec_file = os.path.join(codeunit_folder, "Other", "Build", f"{codeunit_name}.nuspec")
|
|
1525
|
-
self.__sc.replace_version_in_nuspec_file(nuspec_file, codeunit_version)
|
|
1526
|
-
|
|
1527
|
-
@GeneralUtilities.check_arguments
|
|
1528
|
-
def standardized_tasks_build_for_angular_codeunit(self, build_script_file: str, build_environment_target_type: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1529
|
-
build_script_folder = os.path.dirname(build_script_file)
|
|
1530
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", build_script_folder)
|
|
1531
|
-
GeneralUtilities.ensure_directory_does_not_exist(f"{codeunit_folder}/.angular")
|
|
1532
|
-
self.standardized_tasks_build_for_node_codeunit(build_script_file, build_environment_target_type, verbosity, commandline_arguments)
|
|
1533
|
-
|
|
1534
|
-
@GeneralUtilities.check_arguments
|
|
1535
|
-
def standardized_tasks_build_for_node_codeunit(self, build_script_file: str, build_environment_target_type: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1536
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1537
|
-
build_script_folder = os.path.dirname(build_script_file)
|
|
1538
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", build_script_folder)
|
|
1539
|
-
self.run_with_epew("npm", f"run build-{build_environment_target_type}", codeunit_folder, verbosity=verbosity)
|
|
1540
|
-
self.standardized_tasks_build_bom_for_node_project(codeunit_folder, verbosity, commandline_arguments)
|
|
1541
|
-
self.copy_source_files_to_output_directory(build_script_file)
|
|
1542
|
-
|
|
1543
|
-
@GeneralUtilities.check_arguments
|
|
1544
|
-
def standardized_tasks_build_bom_for_node_project(self, codeunit_folder: str, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1545
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1546
|
-
# TODO
|
|
1547
|
-
|
|
1548
|
-
@GeneralUtilities.check_arguments
|
|
1549
|
-
def standardized_tasks_linting_for_angular_codeunit(self, linting_script_file: str, verbosity: int, build_environment_target_type: str, commandline_arguments: list[str]) -> None:
|
|
1550
|
-
self.standardized_tasks_linting_for_node_codeunit(linting_script_file, verbosity, build_environment_target_type, commandline_arguments)
|
|
1551
|
-
|
|
1552
|
-
@GeneralUtilities.check_arguments
|
|
1553
|
-
def standardized_tasks_linting_for_node_codeunit(self, linting_script_file: str, verbosity: int, build_environment_target_type: str, commandline_arguments: list[str]) -> None:
|
|
1554
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1555
|
-
build_script_folder = os.path.dirname(linting_script_file)
|
|
1556
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", build_script_folder)
|
|
1557
|
-
self.run_with_epew("ng", "lint", codeunit_folder, verbosity=verbosity)
|
|
1558
|
-
|
|
1559
|
-
@GeneralUtilities.check_arguments
|
|
1560
|
-
def standardized_tasks_run_testcases_for_flutter_project_in_common_project_structure(self, script_file: str, verbosity: int, args: list[str], package_name: str, build_environment_target_type: str, generate_badges: bool):
|
|
1561
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../../..", script_file)
|
|
1562
|
-
repository_folder = GeneralUtilities.resolve_relative_path("..", codeunit_folder)
|
|
1563
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1564
|
-
src_folder = GeneralUtilities.resolve_relative_path(package_name, codeunit_folder)
|
|
1565
|
-
verbosity = self.get_verbosity_from_commandline_arguments(args, verbosity)
|
|
1566
|
-
self.run_with_epew("flutter", "test --coverage", src_folder, verbosity)
|
|
1567
|
-
test_coverage_folder_relative = "Other/Artifacts/TestCoverage"
|
|
1568
|
-
test_coverage_folder = GeneralUtilities.resolve_relative_path(test_coverage_folder_relative, codeunit_folder)
|
|
1569
|
-
GeneralUtilities.ensure_directory_exists(test_coverage_folder)
|
|
1570
|
-
coverage_file_relative = f"{test_coverage_folder_relative}/TestCoverage.xml"
|
|
1571
|
-
coverage_file = GeneralUtilities.resolve_relative_path(coverage_file_relative, codeunit_folder)
|
|
1572
|
-
self.run_with_epew("lcov_cobertura", f"coverage/lcov.info --base-dir . --excludes test --output ../{coverage_file_relative} --demangle", src_folder, verbosity)
|
|
1573
|
-
content = GeneralUtilities.read_text_from_file(coverage_file)
|
|
1574
|
-
content = re.sub('<![^<]+>', '', content)
|
|
1575
|
-
content = re.sub('\\\\', '/', content)
|
|
1576
|
-
content = re.sub('\\ name=\\"lib\\"', '', content)
|
|
1577
|
-
content = re.sub('<package ', f'<package name="{codeunit_name}" ', content)
|
|
1578
|
-
content = re.sub('\\ filename=\\"lib/', f' filename="{package_name}/lib/', content)
|
|
1579
|
-
GeneralUtilities.write_text_to_file(coverage_file, content)
|
|
1580
|
-
self.run_testcases_common_post_task(repository_folder, codeunit_name, verbosity, generate_badges, build_environment_target_type, args)
|
|
1581
|
-
|
|
1582
|
-
@GeneralUtilities.check_arguments
|
|
1583
|
-
def standardized_tasks_run_testcases_for_angular_codeunit(self, runtestcases_script_file: str, build_environment_target_type: str, generate_badges: bool, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1584
|
-
# prepare
|
|
1585
|
-
codeunit_name: str = os.path.basename(str(Path(os.path.dirname(runtestcases_script_file)).parent.parent.absolute()))
|
|
1586
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1587
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", os.path.dirname(runtestcases_script_file))
|
|
1588
|
-
repository_folder = os.path.dirname(codeunit_folder)
|
|
1589
|
-
|
|
1590
|
-
# run testcases
|
|
1591
|
-
self.standardized_tasks_run_testcases_for_node_codeunit(runtestcases_script_file, build_environment_target_type, generate_badges, verbosity, commandline_arguments)
|
|
1592
|
-
|
|
1593
|
-
# rename file
|
|
1594
|
-
coverage_folder = os.path.join(codeunit_folder, "Other", "Artifacts", "TestCoverage")
|
|
1595
|
-
target_file = os.path.join(coverage_folder, "TestCoverage.xml")
|
|
1596
|
-
GeneralUtilities.ensure_file_does_not_exist(target_file)
|
|
1597
|
-
os.rename(os.path.join(coverage_folder, "cobertura-coverage.xml"), target_file)
|
|
1598
|
-
self.__rename_packagename_in_coverage_file(target_file, codeunit_name)
|
|
1599
|
-
|
|
1600
|
-
# adapt backslashs to slashs
|
|
1601
|
-
content = GeneralUtilities.read_text_from_file(target_file)
|
|
1602
|
-
content = re.sub('\\\\', '/', content)
|
|
1603
|
-
GeneralUtilities.write_text_to_file(target_file, content)
|
|
1604
|
-
|
|
1605
|
-
# aggregate packages in testcoverage-file
|
|
1606
|
-
roottree: etree._ElementTree = etree.parse(target_file)
|
|
1607
|
-
existing_classes = list(roottree.xpath('//coverage/packages/package/classes/class'))
|
|
1608
|
-
|
|
1609
|
-
old_packages_list = roottree.xpath('//coverage/packages/package')
|
|
1610
|
-
for package in old_packages_list:
|
|
1611
|
-
package.getparent().remove(package)
|
|
1612
|
-
|
|
1613
|
-
root = roottree.getroot()
|
|
1614
|
-
packages_element = root.find("packages")
|
|
1615
|
-
package_element = etree.SubElement(packages_element, "package")
|
|
1616
|
-
package_element.attrib['name'] = codeunit_name
|
|
1617
|
-
package_element.attrib['lines-valid'] = root.attrib["lines-valid"]
|
|
1618
|
-
package_element.attrib['lines-covered'] = root.attrib["lines-covered"]
|
|
1619
|
-
package_element.attrib['line-rate'] = root.attrib["line-rate"]
|
|
1620
|
-
package_element.attrib['branches-valid'] = root.attrib["branches-valid"]
|
|
1621
|
-
package_element.attrib['branches-covered'] = root.attrib["branches-covered"]
|
|
1622
|
-
package_element.attrib['branch-rate'] = root.attrib["branch-rate"]
|
|
1623
|
-
package_element.attrib['timestamp'] = root.attrib["timestamp"]
|
|
1624
|
-
package_element.attrib['complexity'] = root.attrib["complexity"]
|
|
1625
|
-
|
|
1626
|
-
classes_element = etree.SubElement(package_element, "classes")
|
|
1627
|
-
|
|
1628
|
-
for existing_class in existing_classes:
|
|
1629
|
-
classes_element.append(existing_class)
|
|
1630
|
-
|
|
1631
|
-
result = etree.tostring(roottree, pretty_print=True).decode("utf-8")
|
|
1632
|
-
GeneralUtilities.write_text_to_file(target_file, result)
|
|
1633
|
-
|
|
1634
|
-
# post tasks
|
|
1635
|
-
self.run_testcases_common_post_task(repository_folder, codeunit_name, verbosity, generate_badges, build_environment_target_type, commandline_arguments)
|
|
1636
|
-
|
|
1637
|
-
@GeneralUtilities.check_arguments
|
|
1638
|
-
def standardized_tasks_run_testcases_for_node_codeunit(self, runtestcases_script_file: str, build_environment_target_type: str, generate_badges: bool, verbosity: int, commandline_arguments: list[str]) -> None:
|
|
1639
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1640
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("../..", os.path.dirname(runtestcases_script_file))
|
|
1641
|
-
self.run_with_epew("npm", f"run test-{build_environment_target_type}", codeunit_folder, verbosity=verbosity)
|
|
1642
|
-
|
|
1643
|
-
@GeneralUtilities.check_arguments
|
|
1644
|
-
def __rename_packagename_in_coverage_file(self, file: str, codeunit_name: str) -> None:
|
|
1645
|
-
root: etree._ElementTree = etree.parse(file)
|
|
1646
|
-
packages = root.xpath('//coverage/packages/package')
|
|
1647
|
-
for package in packages:
|
|
1648
|
-
package.attrib['name'] = codeunit_name
|
|
1649
|
-
result = etree.tostring(root).decode("utf-8")
|
|
1650
|
-
GeneralUtilities.write_text_to_file(file, result)
|
|
1651
|
-
|
|
1652
|
-
@GeneralUtilities.check_arguments
|
|
1653
|
-
def do_npm_install(self, package_json_folder: str, verbosity: int) -> None:
|
|
1654
|
-
self.run_with_epew("npm", "clean-install", package_json_folder, verbosity=verbosity)
|
|
1655
|
-
|
|
1656
|
-
@GeneralUtilities.check_arguments
|
|
1657
|
-
def run_with_epew(self, program: str, argument: str = "", working_directory: str = None, verbosity: int = 1, print_errors_as_information: bool = False, log_file: str = None, timeoutInSeconds: int = 600, addLogOverhead: bool = False, title: str = None, log_namespace: str = "", arguments_for_log: list[str] = None, throw_exception_if_exitcode_is_not_zero: bool = True, custom_argument: object = None, interactive: bool = False) -> tuple[int, str, str, int]:
|
|
1658
|
-
sc: ScriptCollectionCore = ScriptCollectionCore()
|
|
1659
|
-
sc.program_runner = ProgramRunnerEpew()
|
|
1660
|
-
return sc.run_program(program, argument, working_directory, verbosity, print_errors_as_information, log_file, timeoutInSeconds, addLogOverhead, title, log_namespace, arguments_for_log, throw_exception_if_exitcode_is_not_zero, custom_argument, interactive)
|
|
1661
|
-
|
|
1662
|
-
@GeneralUtilities.check_arguments
|
|
1663
|
-
def set_default_constants(self, codeunit_folder: str) -> None:
|
|
1664
|
-
self.set_constant_for_commitid(codeunit_folder)
|
|
1665
|
-
self.set_constant_for_commitdate(codeunit_folder)
|
|
1666
|
-
self.set_constant_for_commitname(codeunit_folder)
|
|
1667
|
-
self.set_constant_for_codeunitversion(codeunit_folder)
|
|
1668
|
-
self.set_constant_for_codeunitmajorversion(codeunit_folder)
|
|
1669
|
-
self.set_constant_for_description(codeunit_folder)
|
|
1670
|
-
|
|
1671
|
-
@GeneralUtilities.check_arguments
|
|
1672
|
-
def set_constant_for_commitid(self, codeunit_folder: str) -> None:
|
|
1673
|
-
commit_id = self.__sc.git_get_commit_id(codeunit_folder)
|
|
1674
|
-
self.set_constant(codeunit_folder, "CommitId", commit_id)
|
|
1675
|
-
|
|
1676
|
-
@GeneralUtilities.check_arguments
|
|
1677
|
-
def set_constant_for_commitdate(self, codeunit_folder: str) -> None:
|
|
1678
|
-
commit_date: datetime = self.__sc.git_get_commit_date(codeunit_folder)
|
|
1679
|
-
self.set_constant(codeunit_folder, "CommitDate", GeneralUtilities.datetime_to_string(commit_date))
|
|
1680
|
-
|
|
1681
|
-
@GeneralUtilities.check_arguments
|
|
1682
|
-
def set_constant_for_commitname(self, codeunit_folder: str) -> None:
|
|
1683
|
-
codeunit_name: str = os.path.basename(codeunit_folder)
|
|
1684
|
-
self.set_constant(codeunit_folder, "CodeUnitName", codeunit_name)
|
|
1685
|
-
|
|
1686
|
-
@GeneralUtilities.check_arguments
|
|
1687
|
-
def set_constant_for_codeunitversion(self, codeunit_folder: str) -> None:
|
|
1688
|
-
codeunit_version: str = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
1689
|
-
self.set_constant(codeunit_folder, "CodeUnitVersion", codeunit_version)
|
|
1690
|
-
|
|
1691
|
-
@GeneralUtilities.check_arguments
|
|
1692
|
-
def set_constant_for_codeunitmajorversion(self, codeunit_folder: str) -> None:
|
|
1693
|
-
codeunit_version: str = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
1694
|
-
major_version = int(codeunit_version.split(".")[0])
|
|
1695
|
-
self.set_constant(codeunit_folder, "CodeUnitMajorVersion", str(major_version))
|
|
1696
|
-
|
|
1697
|
-
@GeneralUtilities.check_arguments
|
|
1698
|
-
def set_constant_for_description(self, codeunit_folder: str) -> None:
|
|
1699
|
-
codeunit_name: str = os.path.basename(codeunit_folder)
|
|
1700
|
-
codeunit_description: str = self.get_codeunit_description(f"{codeunit_folder}/{codeunit_name}.codeunit.xml")
|
|
1701
|
-
self.set_constant(codeunit_folder, "CodeUnitDescription", codeunit_description)
|
|
1702
|
-
|
|
1703
|
-
@GeneralUtilities.check_arguments
|
|
1704
|
-
def set_constant(self, codeunit_folder: str, constantname: str, constant_value: str, documentationsummary: str = None, constants_valuefile: str = None) -> None:
|
|
1705
|
-
if documentationsummary is None:
|
|
1706
|
-
documentationsummary = ""
|
|
1707
|
-
constants_folder = os.path.join(codeunit_folder, "Other", "Resources", "Constants")
|
|
1708
|
-
GeneralUtilities.ensure_directory_exists(constants_folder)
|
|
1709
|
-
constants_metafile = os.path.join(constants_folder, f"{constantname}.constant.xml")
|
|
1710
|
-
if constants_valuefile is None:
|
|
1711
|
-
constants_valuefile_folder = constants_folder
|
|
1712
|
-
constants_valuefile_name = f"{constantname}.value.txt"
|
|
1713
|
-
constants_valuefiler_reference = f"./{constants_valuefile_name}"
|
|
1714
|
-
else:
|
|
1715
|
-
constants_valuefile_folder = os.path.dirname(constants_valuefile)
|
|
1716
|
-
constants_valuefile_name = os.path.basename(constants_valuefile)
|
|
1717
|
-
constants_valuefiler_reference = os.path.join(constants_valuefile_folder, constants_valuefile_name)
|
|
1718
|
-
|
|
1719
|
-
# TODO implement usage of self.reference_latest_version_of_xsd_when_generating_xml
|
|
1720
|
-
GeneralUtilities.write_text_to_file(constants_metafile, f"""<?xml version="1.0" encoding="UTF-8" ?>
|
|
1721
|
-
<cps:constant xmlns:cps="https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure" constantspecificationversion="1.1.0"
|
|
1722
|
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/raw/main/Conventions/RepositoryStructure/CommonProjectStructure/constant.xsd">
|
|
1723
|
-
<cps:name>{constantname}</cps:name>
|
|
1724
|
-
<cps:documentationsummary>{documentationsummary}</cps:documentationsummary>
|
|
1725
|
-
<cps:path>{constants_valuefiler_reference}</cps:path>
|
|
1726
|
-
</cps:constant>""")
|
|
1727
|
-
# TODO validate generated xml against xsd
|
|
1728
|
-
GeneralUtilities.write_text_to_file(os.path.join(constants_valuefile_folder, constants_valuefile_name), constant_value)
|
|
1729
|
-
|
|
1730
|
-
@GeneralUtilities.check_arguments
|
|
1731
|
-
def get_constant_value(self, source_codeunit_folder: str, constant_name: str) -> str:
|
|
1732
|
-
value_file_relative = self.__get_constant_helper(source_codeunit_folder, constant_name, "path")
|
|
1733
|
-
value_file = GeneralUtilities.resolve_relative_path(value_file_relative, os.path.join(source_codeunit_folder, "Other", "Resources", "Constants"))
|
|
1734
|
-
return GeneralUtilities.read_text_from_file(value_file)
|
|
1735
|
-
|
|
1736
|
-
@GeneralUtilities.check_arguments
|
|
1737
|
-
def get_constant_documentation(self, source_codeunit_folder: str, constant_name: str) -> str:
|
|
1738
|
-
return self.__get_constant_helper(source_codeunit_folder, constant_name, "documentationsummary")
|
|
1739
|
-
|
|
1740
|
-
@GeneralUtilities.check_arguments
|
|
1741
|
-
def __get_constant_helper(self, source_codeunit_folder: str, constant_name: str, propertyname: str) -> str:
|
|
1742
|
-
root: etree._ElementTree = etree.parse(os.path.join(source_codeunit_folder, "Other", "Resources", "Constants", f"{constant_name}.constant.xml"))
|
|
1743
|
-
results = root.xpath(f'//cps:{propertyname}/text()', namespaces={
|
|
1744
|
-
'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure'
|
|
1745
|
-
})
|
|
1746
|
-
length = len(results)
|
|
1747
|
-
if (length == 0):
|
|
1748
|
-
return ""
|
|
1749
|
-
elif length == 1:
|
|
1750
|
-
return results[0]
|
|
1751
|
-
else:
|
|
1752
|
-
raise ValueError("Too many results found.")
|
|
1753
|
-
|
|
1754
|
-
@GeneralUtilities.check_arguments
|
|
1755
|
-
def copy_development_certificate_to_default_development_directory(self, codeunit_folder: str, build_environment: str, domain: str = None, certificate_resource_name: str = "DevelopmentCertificate") -> None:
|
|
1756
|
-
if build_environment == "Development":
|
|
1757
|
-
codeunit_name: str = os.path.basename(codeunit_folder)
|
|
1758
|
-
if domain is None:
|
|
1759
|
-
domain = f"{codeunit_name}.test.local".lower()
|
|
1760
|
-
|
|
1761
|
-
src_folder = os.path.join(codeunit_folder, "Other", "Resources", certificate_resource_name)
|
|
1762
|
-
src_file_pfx = os.path.join(src_folder, f"{codeunit_name}{certificate_resource_name}.pfx")
|
|
1763
|
-
src_file_psw = os.path.join(src_folder, f"{codeunit_name}{certificate_resource_name}.password")
|
|
1764
|
-
|
|
1765
|
-
trg_folder = os.path.join(codeunit_folder, "Other", "Workspace", "Configuration", "Certificates")
|
|
1766
|
-
trg_file_pfx = os.path.join(trg_folder, f"{domain}.pfx")
|
|
1767
|
-
trg_file_psw = os.path.join(trg_folder, f"{domain}.password")
|
|
1768
|
-
|
|
1769
|
-
GeneralUtilities.ensure_directory_exists(trg_folder)
|
|
1770
|
-
shutil.copyfile(src_file_pfx, trg_file_pfx)
|
|
1771
|
-
shutil.copyfile(src_file_psw, trg_file_psw)
|
|
1772
|
-
|
|
1773
|
-
@GeneralUtilities.check_arguments
|
|
1774
|
-
def set_constants_for_certificate_public_information(self, codeunit_folder: str, source_constant_name: str = "DevelopmentCertificate", domain: str = None) -> None:
|
|
1775
|
-
"""Expects a certificate-resource and generates a constant for its public information"""
|
|
1776
|
-
# codeunit_name = os.path.basename(codeunit_folder)
|
|
1777
|
-
certificate_file = os.path.join(codeunit_folder, "Other", "Resources", source_constant_name, f"{source_constant_name}.crt")
|
|
1778
|
-
with open(certificate_file, encoding="utf-8") as text_wrapper:
|
|
1779
|
-
certificate = crypto.load_certificate(crypto.FILETYPE_PEM, text_wrapper.read())
|
|
1780
|
-
certificate_publickey = crypto.dump_publickey(crypto.FILETYPE_PEM, certificate.get_pubkey()).decode("utf-8")
|
|
1781
|
-
self.set_constant(codeunit_folder, source_constant_name+"PublicKey", certificate_publickey)
|
|
1782
|
-
|
|
1783
|
-
@GeneralUtilities.check_arguments
|
|
1784
|
-
def set_constants_for_certificate_private_information(self, codeunit_folder: str) -> None:
|
|
1785
|
-
"""Expects a certificate-resource and generates a constant for its sensitive information in hex-format"""
|
|
1786
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1787
|
-
resource_name: str = "DevelopmentCertificate"
|
|
1788
|
-
filename: str = codeunit_name+"DevelopmentCertificate"
|
|
1789
|
-
self.generate_constant_from_resource_by_filename(codeunit_folder, resource_name, f"{filename}.pfx", "PFX")
|
|
1790
|
-
self.generate_constant_from_resource_by_filename(codeunit_folder, resource_name, f"{filename}.password", "Password")
|
|
1791
|
-
|
|
1792
|
-
@GeneralUtilities.check_arguments
|
|
1793
|
-
def generate_constant_from_resource_by_filename(self, codeunit_folder: str, resource_name: str, filename: str, constant_name: str) -> None:
|
|
1794
|
-
certificate_resource_folder = GeneralUtilities.resolve_relative_path(f"Other/Resources/{resource_name}", codeunit_folder)
|
|
1795
|
-
resource_file = os.path.join(certificate_resource_folder, filename)
|
|
1796
|
-
resource_file_content = GeneralUtilities.read_binary_from_file(resource_file)
|
|
1797
|
-
resource_file_as_hex = resource_file_content.hex()
|
|
1798
|
-
self.set_constant(codeunit_folder, f"{resource_name}{constant_name}Hex", resource_file_as_hex)
|
|
1799
|
-
|
|
1800
|
-
@GeneralUtilities.check_arguments
|
|
1801
|
-
def generate_constant_from_resource_by_extension(self, codeunit_folder: str, resource_name: str, extension: str, constant_name: str) -> None:
|
|
1802
|
-
certificate_resource_folder = GeneralUtilities.resolve_relative_path(f"Other/Resources/{resource_name}", codeunit_folder)
|
|
1803
|
-
resource_file = self.__sc.find_file_by_extension(certificate_resource_folder, extension)
|
|
1804
|
-
resource_file_content = GeneralUtilities.read_binary_from_file(resource_file)
|
|
1805
|
-
resource_file_as_hex = resource_file_content.hex()
|
|
1806
|
-
self.set_constant(codeunit_folder, f"{resource_name}{constant_name}Hex", resource_file_as_hex)
|
|
1807
|
-
|
|
1808
|
-
@GeneralUtilities.check_arguments
|
|
1809
|
-
def copy_constant_from_dependent_codeunit(self, codeunit_folder: str, constant_name: str, source_codeunit_name: str) -> None:
|
|
1810
|
-
source_codeunit_folder: str = GeneralUtilities.resolve_relative_path(f"../{source_codeunit_name}", codeunit_folder)
|
|
1811
|
-
value = self.get_constant_value(source_codeunit_folder, constant_name)
|
|
1812
|
-
documentation = self.get_constant_documentation(source_codeunit_folder, constant_name)
|
|
1813
|
-
self.set_constant(codeunit_folder, constant_name, value, documentation)
|
|
1814
|
-
|
|
1815
|
-
@GeneralUtilities.check_arguments
|
|
1816
|
-
def copy_resources_from_dependent_codeunit(self, codeunit_folder: str, resource_name: str, source_codeunit_name: str) -> None:
|
|
1817
|
-
source_folder: str = GeneralUtilities.resolve_relative_path(f"../{source_codeunit_name}/Other/Resources/{resource_name}", codeunit_folder)
|
|
1818
|
-
target_folder: str = GeneralUtilities.resolve_relative_path(f"Other/Resources/{resource_name}", codeunit_folder)
|
|
1819
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
1820
|
-
shutil.copytree(source_folder, target_folder)
|
|
1821
|
-
|
|
1822
|
-
@GeneralUtilities.check_arguments
|
|
1823
|
-
def generate_openapi_file(self, buildscript_file: str, runtime: str, verbosity: int, commandline_arguments: list[str], swagger_document_name: str = "APISpecification") -> None:
|
|
1824
|
-
GeneralUtilities.write_message_to_stdout("Generate OpenAPI-specification-file...")
|
|
1825
|
-
codeunitname = os.path.basename(str(Path(os.path.dirname(buildscript_file)).parent.parent.absolute()))
|
|
1826
|
-
repository_folder = str(Path(os.path.dirname(buildscript_file)).parent.parent.parent.absolute())
|
|
1827
|
-
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
1828
|
-
artifacts_folder = os.path.join(codeunit_folder, "Other", "Artifacts")
|
|
1829
|
-
GeneralUtilities.ensure_directory_exists(os.path.join(artifacts_folder, "APISpecification"))
|
|
1830
|
-
verbosity = self.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1831
|
-
codeunit_version = self.get_version_of_codeunit_folder(codeunit_folder)
|
|
1832
|
-
versioned_api_spec_file = f"APISpecification/{codeunitname}.v{codeunit_version}.api.json"
|
|
1833
|
-
self.__sc.run_program("swagger", f"tofile --output {versioned_api_spec_file} BuildResult_DotNet_{runtime}/{codeunitname}.dll {swagger_document_name}", artifacts_folder, verbosity=verbosity)
|
|
1834
|
-
shutil.copyfile(os.path.join(artifacts_folder, versioned_api_spec_file), os.path.join(artifacts_folder, f"APISpecification/{codeunitname}.latest.api.json"))
|
|
1835
|
-
|
|
1836
|
-
@GeneralUtilities.check_arguments
|
|
1837
|
-
def replace_version_in_packagejson_file(self, packagejson_file: str, codeunit_version: str) -> None:
|
|
1838
|
-
encoding = "utf-8"
|
|
1839
|
-
with open(packagejson_file, encoding=encoding) as f:
|
|
1840
|
-
data = json.load(f)
|
|
1841
|
-
data['version'] = codeunit_version
|
|
1842
|
-
with open(packagejson_file, 'w', encoding=encoding) as f:
|
|
1843
|
-
json.dump(data, f, indent=2)
|
|
1844
|
-
|
|
1845
|
-
@GeneralUtilities.check_arguments
|
|
1846
|
-
def build_dependent_code_units(self, repo_folder: str, codeunit_name: str, verbosity: int, target_environmenttype: str, additional_arguments_file: str, commandlinearguments: list[str]) -> None:
|
|
1847
|
-
verbosity = self.get_verbosity_from_commandline_arguments(commandlinearguments, verbosity)
|
|
1848
|
-
codeunit_file = os.path.join(repo_folder, codeunit_name, codeunit_name + ".codeunit.xml")
|
|
1849
|
-
dependent_codeunits = self.get_dependent_code_units(codeunit_file)
|
|
1850
|
-
dependent_codeunits_folder = os.path.join(repo_folder, codeunit_name, "Other", "Resources", "DependentCodeUnits")
|
|
1851
|
-
GeneralUtilities.ensure_directory_does_not_exist(dependent_codeunits_folder)
|
|
1852
|
-
if 0 < len(dependent_codeunits):
|
|
1853
|
-
GeneralUtilities.write_message_to_stdout(f"Start building dependent codeunits for codeunit {codeunit_name}.")
|
|
1854
|
-
for dependent_codeunit in dependent_codeunits:
|
|
1855
|
-
self.__build_codeunit(os.path.join(repo_folder, dependent_codeunit), verbosity, target_environmenttype, additional_arguments_file, False, False, commandlinearguments)
|
|
1856
|
-
if 0 < len(dependent_codeunits):
|
|
1857
|
-
GeneralUtilities.write_message_to_stdout(f"Finished building dependent codeunits for codeunit {codeunit_name}.")
|
|
1858
|
-
|
|
1859
|
-
@GeneralUtilities.check_arguments
|
|
1860
|
-
def copy_artifacts_from_dependent_code_units(self, repo_folder: str, codeunit_name: str) -> None:
|
|
1861
|
-
codeunit_file = os.path.join(repo_folder, codeunit_name, codeunit_name + ".codeunit.xml")
|
|
1862
|
-
dependent_codeunits = self.get_dependent_code_units(codeunit_file)
|
|
1863
|
-
if len(dependent_codeunits) > 0:
|
|
1864
|
-
GeneralUtilities.write_message_to_stdout(f"Get dependent artifacts for codeunit {codeunit_name}.")
|
|
1865
|
-
dependent_codeunits_folder = os.path.join(repo_folder, codeunit_name, "Other", "Resources", "DependentCodeUnits")
|
|
1866
|
-
GeneralUtilities.ensure_directory_does_not_exist(dependent_codeunits_folder)
|
|
1867
|
-
for dependent_codeunit in dependent_codeunits:
|
|
1868
|
-
target_folder = os.path.join(dependent_codeunits_folder, dependent_codeunit)
|
|
1869
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
1870
|
-
other_folder = os.path.join(repo_folder, dependent_codeunit, "Other")
|
|
1871
|
-
artifacts_folder = os.path.join(other_folder, "Artifacts")
|
|
1872
|
-
shutil.copytree(artifacts_folder, target_folder)
|
|
1873
|
-
|
|
1874
|
-
@GeneralUtilities.check_arguments
|
|
1875
|
-
def add_github_release(self, productname: str, projectversion: str, build_artifacts_folder: str, github_username: str, repository_folder: str, commandline_arguments: list[str]) -> None:
|
|
1876
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, 1)
|
|
1877
|
-
github_repo = f"{github_username}/{productname}"
|
|
1878
|
-
artifact_files = []
|
|
1879
|
-
codeunits = self.get_codeunits(repository_folder)
|
|
1880
|
-
for codeunit in codeunits:
|
|
1881
|
-
artifact_files.append(self.__sc.find_file_by_extension(f"{build_artifacts_folder}\\{productname}\\{projectversion}\\{codeunit}", "Productive.Artifacts.zip"))
|
|
1882
|
-
changelog_file = os.path.join(repository_folder, "Other", "Resources", "Changelog", f"v{projectversion}.md")
|
|
1883
|
-
self.__sc.run_program_argsasarray("gh", ["release", "create", f"v{projectversion}", "--repo", github_repo, "--notes-file", changelog_file, "--title", f"Release v{projectversion}"]+artifact_files, verbosity=verbosity)
|
|
1884
|
-
|
|
1885
|
-
@GeneralUtilities.check_arguments
|
|
1886
|
-
def get_dependencies_which_are_ignored_from_updates(self, codeunit_folder: str, print_warnings_for_ignored_dependencies: bool) -> list[str]:
|
|
1887
|
-
namespaces = {'cps': 'https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure',
|
|
1888
|
-
'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
|
|
1889
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1890
|
-
codeunit_file = os.path.join(codeunit_folder, f"{codeunit_name}.codeunit.xml")
|
|
1891
|
-
root: etree._ElementTree = etree.parse(codeunit_file)
|
|
1892
|
-
ignoreddependencies = root.xpath('//cps:codeunit/cps:properties/cps:updatesettings/cps:ignoreddependencies/cps:ignoreddependency', namespaces=namespaces)
|
|
1893
|
-
result = [x.text.replace("\\n", "").replace("\\r", "").replace("\n", "").replace("\r", "").strip() for x in ignoreddependencies]
|
|
1894
|
-
if print_warnings_for_ignored_dependencies and len(result) > 0:
|
|
1895
|
-
GeneralUtilities.write_message_to_stderr(f"Warning: Codeunit {codeunit_name} contains the following dependencies which will are ignoed for automatic updates: "+', '.join(result))
|
|
1896
|
-
return result
|
|
1897
|
-
|
|
1898
|
-
@GeneralUtilities.check_arguments
|
|
1899
|
-
def update_dependencies_of_typical_flutter_codeunit(self, update_script_file: str, verbosity: int, cmd_args: list[str]) -> None:
|
|
1900
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("..", os.path.dirname(update_script_file))
|
|
1901
|
-
ignored_dependencies = self.get_dependencies_which_are_ignored_from_updates(codeunit_folder, True)
|
|
1902
|
-
# TODO implement
|
|
1903
|
-
|
|
1904
|
-
@GeneralUtilities.check_arguments
|
|
1905
|
-
def update_dependencies_of_typical_python_codeunit(self, update_script_file: str, verbosity: int, cmd_args: list[str]) -> None:
|
|
1906
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("..", os.path.dirname(update_script_file))
|
|
1907
|
-
ignored_dependencies = self.get_dependencies_which_are_ignored_from_updates(codeunit_folder, True)
|
|
1908
|
-
# TODO consider ignored_dependencies
|
|
1909
|
-
verbosity = self.get_verbosity_from_commandline_arguments(cmd_args, verbosity)
|
|
1910
|
-
self.__sc.update_dependencies_of_python_in_setupcfg_file(os.path.join(codeunit_folder, "setup.cfg"), verbosity)
|
|
1911
|
-
development_requirements_file = os.path.join(codeunit_folder, "requirements.txt")
|
|
1912
|
-
if (os.path.isfile(development_requirements_file)):
|
|
1913
|
-
self.__sc.update_dependencies_of_python_in_requirementstxt_file(development_requirements_file, verbosity)
|
|
1914
|
-
|
|
1915
|
-
@GeneralUtilities.check_arguments
|
|
1916
|
-
def update_dependencies_of_typical_dotnet_codeunit(self, update_script_file: str, verbosity: int, cmd_args: list[str]) -> None:
|
|
1917
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("..", os.path.dirname(update_script_file))
|
|
1918
|
-
ignored_dependencies = self.get_dependencies_which_are_ignored_from_updates(codeunit_folder, True)
|
|
1919
|
-
verbosity = self.get_verbosity_from_commandline_arguments(cmd_args, verbosity)
|
|
1920
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1921
|
-
|
|
1922
|
-
build_folder = os.path.join(codeunit_folder, "Other", "Build")
|
|
1923
|
-
self.__sc.run_program("python", "Build.py", build_folder, verbosity)
|
|
1924
|
-
|
|
1925
|
-
csproj_file = os.path.join(codeunit_folder, codeunit_name, f"{codeunit_name}.csproj")
|
|
1926
|
-
self.__sc.update_dependencies_of_dotnet_project(csproj_file, verbosity, ignored_dependencies)
|
|
1927
|
-
test_csproj_file = os.path.join(codeunit_folder, f"{codeunit_name}Tests", f"{codeunit_name}Tests.csproj")
|
|
1928
|
-
self.__sc.update_dependencies_of_dotnet_project(test_csproj_file, verbosity, ignored_dependencies)
|
|
1929
|
-
|
|
1930
|
-
@GeneralUtilities.check_arguments
|
|
1931
|
-
def update_dependencies_of_typical_node_codeunit(self, update_script_file: str, verbosity: int, cmd_args: list[str]) -> None:
|
|
1932
|
-
current_folder = os.path.dirname(update_script_file)
|
|
1933
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("..", os.path.dirname(update_script_file))
|
|
1934
|
-
ignored_dependencies = self.get_dependencies_which_are_ignored_from_updates(codeunit_folder, True)
|
|
1935
|
-
# TODO consider ignored_dependencies
|
|
1936
|
-
result = self.run_with_epew("npm", "outdated", current_folder, verbosity, throw_exception_if_exitcode_is_not_zero=False)
|
|
1937
|
-
if result[0] == 0:
|
|
1938
|
-
return # all dependencies up to date
|
|
1939
|
-
elif result[0] == 1:
|
|
1940
|
-
package_json_content = None
|
|
1941
|
-
package_json_file = f"{current_folder}/package.json"
|
|
1942
|
-
with open(package_json_file, "r", encoding="utf-8") as package_json_file_object:
|
|
1943
|
-
package_json_content = json.load(package_json_file_object)
|
|
1944
|
-
lines = GeneralUtilities.string_to_lines(result[1])[1:][:-1]
|
|
1945
|
-
for line in lines:
|
|
1946
|
-
normalized_line_splitted = ' '.join(line.split()).split(" ")
|
|
1947
|
-
package = normalized_line_splitted[0]
|
|
1948
|
-
latest_version = normalized_line_splitted[3]
|
|
1949
|
-
if package in package_json_content["dependencies"]:
|
|
1950
|
-
package_json_content["dependencies"][package] = latest_version
|
|
1951
|
-
if package in package_json_content["devDependencies"]:
|
|
1952
|
-
package_json_content["devDependencies"][package] = latest_version
|
|
1953
|
-
with open(package_json_file, "w", encoding="utf-8") as package_json_file_object:
|
|
1954
|
-
json.dump(package_json_content, package_json_file_object, indent=4)
|
|
1955
|
-
self.run_with_epew("npm", "install --force", current_folder, verbosity)
|
|
1956
|
-
else:
|
|
1957
|
-
GeneralUtilities.write_message_to_stderr("Update dependencies resulted in an error.")
|
|
1958
|
-
|
|
1959
|
-
@GeneralUtilities.check_arguments
|
|
1960
|
-
def run_local_test_service(self, file: str):
|
|
1961
|
-
example_folder = os.path.dirname(file)
|
|
1962
|
-
docker_compose_file = os.path.join(example_folder, "docker-compose.yml")
|
|
1963
|
-
for service in self.__sc.get_services_from_yaml_file(docker_compose_file):
|
|
1964
|
-
self.__sc.kill_docker_container(service)
|
|
1965
|
-
example_name = os.path.basename(example_folder)
|
|
1966
|
-
title = f"Test{example_name}"
|
|
1967
|
-
self.__sc.run_program("docker", f"compose -p {title.lower()} up", example_folder, title=title)
|
|
1968
|
-
|
|
1969
|
-
@GeneralUtilities.check_arguments
|
|
1970
|
-
def standardized_tasks_update_version_in_docker_examples(self, file, codeunit_version) -> None:
|
|
1971
|
-
folder_of_current_file = os.path.dirname(file)
|
|
1972
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path("..", folder_of_current_file)
|
|
1973
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
1974
|
-
codeunit_name_lower = codeunit_name.lower()
|
|
1975
|
-
examples_folder = GeneralUtilities.resolve_relative_path("Other/Reference/ReferenceContent/Examples", codeunit_folder)
|
|
1976
|
-
for example_folder in GeneralUtilities.get_direct_folders_of_folder(examples_folder):
|
|
1977
|
-
docker_compose_file = os.path.join(example_folder, "docker-compose.yml")
|
|
1978
|
-
if os.path.isfile(docker_compose_file):
|
|
1979
|
-
filecontent = GeneralUtilities.read_text_from_file(docker_compose_file)
|
|
1980
|
-
replaced = re.sub(f'image:\\s+{codeunit_name_lower}:\\d+\\.\\d+\\.\\d+', f"image: {codeunit_name_lower}:{codeunit_version}", filecontent)
|
|
1981
|
-
GeneralUtilities.write_text_to_file(docker_compose_file, replaced)
|
|
1982
|
-
|
|
1983
|
-
@GeneralUtilities.check_arguments
|
|
1984
|
-
def run_dockerfile_example(self, current_file: str, verbosity: int, remove_old_container: bool, remove_volumes_folder: bool, commandline_arguments: list[str]) -> None:
|
|
1985
|
-
verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
1986
|
-
folder = os.path.dirname(current_file)
|
|
1987
|
-
example_name = os.path.basename(folder)
|
|
1988
|
-
GeneralUtilities.write_message_to_stdout(f'Run "{example_name}"-example')
|
|
1989
|
-
sc = ScriptCollectionCore()
|
|
1990
|
-
oci_image_artifacts_folder = GeneralUtilities.resolve_relative_path("../../../../Artifacts/BuildResult_OCIImage", folder)
|
|
1991
|
-
image_filename = os.path.basename(sc.find_file_by_extension(oci_image_artifacts_folder, "tar"))
|
|
1992
|
-
codeunit_name = os.path.basename(GeneralUtilities.resolve_relative_path("../../../../..", folder))
|
|
1993
|
-
if remove_old_container:
|
|
1994
|
-
docker_compose_file = f"{folder}/docker-compose.yml"
|
|
1995
|
-
container_names = []
|
|
1996
|
-
lines = GeneralUtilities.read_lines_from_file(docker_compose_file)
|
|
1997
|
-
for line in lines:
|
|
1998
|
-
if match := re.search("container_name:\\s*'?([^']+)'?", line):
|
|
1999
|
-
container_names.append(match.group(1))
|
|
2000
|
-
GeneralUtilities.write_message_to_stdout(f"Ensure container of {docker_compose_file} do not exist...")
|
|
2001
|
-
for container_name in container_names:
|
|
2002
|
-
GeneralUtilities.write_message_to_stdout(f"Ensure container of {container_name} does not exist")
|
|
2003
|
-
sc.run_program("docker", f"container rm -f {container_name}", oci_image_artifacts_folder, verbosity=0, throw_exception_if_exitcode_is_not_zero=False)
|
|
2004
|
-
if remove_volumes_folder:
|
|
2005
|
-
volumes_folder = os.path.join(folder, "Volumes")
|
|
2006
|
-
GeneralUtilities.write_message_to_stdout(f"Ensure volumes-folder '{volumes_folder}' does not exist...")
|
|
2007
|
-
GeneralUtilities.ensure_directory_does_not_exist(volumes_folder)
|
|
2008
|
-
GeneralUtilities.ensure_directory_exists(volumes_folder)
|
|
2009
|
-
GeneralUtilities.write_message_to_stdout("Load docker-image...")
|
|
2010
|
-
sc.run_program("docker", f"load -i {image_filename}", oci_image_artifacts_folder, verbosity=verbosity)
|
|
2011
|
-
docker_project_name = f"{codeunit_name}_{example_name}".lower()
|
|
2012
|
-
sc_epew = ScriptCollectionCore()
|
|
2013
|
-
sc_epew.program_runner = ProgramRunnerEpew()
|
|
2014
|
-
GeneralUtilities.write_message_to_stdout("Start docker-container...")
|
|
2015
|
-
sc_epew.run_program("docker", f"compose --project-name {docker_project_name} up --abort-on-container-exit", folder, verbosity=verbosity)
|
|
2016
|
-
|
|
2017
|
-
@GeneralUtilities.check_arguments
|
|
2018
|
-
def create_artifact_for_development_certificate(self, codeunit_folder: str):
|
|
2019
|
-
ce_source_folder = GeneralUtilities.resolve_relative_path("Other/Resources/DevelopmentCertificate", codeunit_folder)
|
|
2020
|
-
ca_source_folder = GeneralUtilities.resolve_relative_path("Other/Resources/CA", codeunit_folder)
|
|
2021
|
-
ce_target_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts/DevelopmentCertificate", codeunit_folder)
|
|
2022
|
-
ca_target_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts/A", codeunit_folder)
|
|
2023
|
-
|
|
2024
|
-
GeneralUtilities.ensure_directory_exists(ce_target_folder)
|
|
2025
|
-
GeneralUtilities.copy_content_of_folder(ce_source_folder, ce_target_folder)
|
|
2026
|
-
GeneralUtilities.ensure_directory_exists(ca_target_folder)
|
|
2027
|
-
GeneralUtilities.copy_content_of_folder(ca_source_folder, ca_target_folder)
|
|
2028
|
-
|
|
2029
|
-
@GeneralUtilities.check_arguments
|
|
2030
|
-
def get_sorted_codeunits(self, codeunits=dict[str, set[str]]) -> list[str]:
|
|
2031
|
-
result_typed = list(TopologicalSorter(codeunits).static_order())
|
|
2032
|
-
result = list()
|
|
2033
|
-
for item in result_typed:
|
|
2034
|
-
result.append(str(item))
|
|
2035
|
-
return result
|
|
2036
|
-
|
|
2037
|
-
@GeneralUtilities.check_arguments
|
|
2038
|
-
def get_project_name(self, repository_folder: str) -> str:
|
|
2039
|
-
for file in GeneralUtilities.get_direct_files_of_folder(repository_folder):
|
|
2040
|
-
if file.endswith(".code-workspace"):
|
|
2041
|
-
return Path(file).stem
|
|
2042
|
-
raise ValueError(f'Project-name can not be calculated for repository "{repository_folder}"')
|
|
2043
|
-
|
|
2044
|
-
def __check_target_environmenttype(self, target_environmenttype: str):
|
|
2045
|
-
allowed_values = list(self.get_default_target_environmenttype_mapping().values())
|
|
2046
|
-
if not (target_environmenttype in allowed_values):
|
|
2047
|
-
raise ValueError(f"Invalid target-environmenttype: '{target_environmenttype}'")
|
|
2048
|
-
|
|
2049
|
-
@GeneralUtilities.check_arguments
|
|
2050
|
-
def build_codeunit(self, codeunit_folder: str, verbosity: int = 1, target_environmenttype: str = "QualityCheck", additional_arguments_file: str = None, is_pre_merge: bool = False, export_target_directory: str = None, assume_dependent_codeunits_are_already_built: bool = False, commandlinearguments: list[str] = []) -> None:
|
|
2051
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path_from_current_working_directory(codeunit_folder)
|
|
2052
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
2053
|
-
repository_folder = os.path.dirname(codeunit_folder)
|
|
2054
|
-
self.build_specific_codeunits(repository_folder, [codeunit_name], verbosity, target_environmenttype, additional_arguments_file, is_pre_merge, export_target_directory, assume_dependent_codeunits_are_already_built, commandlinearguments)
|
|
2055
|
-
|
|
2056
|
-
@GeneralUtilities.check_arguments
|
|
2057
|
-
def build_codeunitsC(self, repository_folder: str, image: str, verbosity: int = 1, target_environmenttype: str = "QualityCheck", additional_arguments_file: str = None, commandlinearguments: list[str] = []) -> None:
|
|
2058
|
-
if target_environmenttype == "Development":
|
|
2059
|
-
raise ValueError(f"build_codeunitsC is not available for target_environmenttype {target_environmenttype}.")
|
|
2060
|
-
# TODO handle additional_arguments_file
|
|
2061
|
-
# TODO add option to allow building different codeunits in same project with different images due to their demands
|
|
2062
|
-
# TODO check if image provides all demands of codeunit
|
|
2063
|
-
self.__sc.run_program("docker", f"run --volume {repository_folder}:/Workspace/Repository " + f"-e repositoryfolder=/Workspace/Repository -e verbosity={verbosity} -e targetenvironment={target_environmenttype} {image}", repository_folder)
|
|
2064
|
-
|
|
2065
|
-
@GeneralUtilities.check_arguments
|
|
2066
|
-
def build_codeunits(self, repository_folder: str, verbosity: int = 1, target_environmenttype: str = "QualityCheck", additional_arguments_file: str = None, is_pre_merge: bool = False, export_target_directory: str = None, commandline_arguments: list[str] = []) -> None:
|
|
2067
|
-
self.__check_target_environmenttype(target_environmenttype)
|
|
2068
|
-
repository_folder = GeneralUtilities.resolve_relative_path_from_current_working_directory(repository_folder)
|
|
2069
|
-
codeunits = self.get_codeunits(repository_folder, False)
|
|
2070
|
-
self.build_specific_codeunits(repository_folder, codeunits, verbosity, target_environmenttype, additional_arguments_file, is_pre_merge, export_target_directory, False, commandline_arguments)
|
|
2071
|
-
|
|
2072
|
-
@GeneralUtilities.check_arguments
|
|
2073
|
-
def build_specific_codeunits(self, repository_folder: str, codeunits: list[str], verbosity: int = 1, target_environmenttype: str = "QualityCheck", additional_arguments_file: str = None, is_pre_merge: bool = False, export_target_directory: str = None, assume_dependent_codeunits_are_already_built: bool = True, commandline_arguments: list[str] = []) -> None:
|
|
2074
|
-
self.__check_target_environmenttype(target_environmenttype)
|
|
2075
|
-
repository_folder = GeneralUtilities.resolve_relative_path_from_current_working_directory(repository_folder)
|
|
2076
|
-
contains_uncommitted_changes = self.__sc.git_repository_has_uncommitted_changes(repository_folder)
|
|
2077
|
-
if is_pre_merge and contains_uncommitted_changes:
|
|
2078
|
-
raise ValueError(f'Repository "{repository_folder}" has uncommitted changes.')
|
|
2079
|
-
subfolders = [os.path.join(repository_folder, codeunit) for codeunit in codeunits]
|
|
2080
|
-
codeunits_with_dependent_codeunits: dict[str, set[str]] = dict[str, set[str]]()
|
|
2081
|
-
|
|
2082
|
-
project_resources_folder = os.path.join(repository_folder, "Other", "Scripts")
|
|
2083
|
-
PrepareBuildCodeunits_script_name = "PrepareBuildCodeunits.py"
|
|
2084
|
-
prepare_build_codeunits_scripts = os.path.join(project_resources_folder, PrepareBuildCodeunits_script_name)
|
|
2085
|
-
if os.path.isfile(prepare_build_codeunits_scripts):
|
|
2086
|
-
GeneralUtilities.write_message_to_stdout(f'Run "{PrepareBuildCodeunits_script_name}"')
|
|
2087
|
-
self.__sc.run_program("python", f"{PrepareBuildCodeunits_script_name}", project_resources_folder)
|
|
2088
|
-
|
|
2089
|
-
for subfolder in subfolders:
|
|
2090
|
-
codeunit_name: str = os.path.basename(subfolder)
|
|
2091
|
-
codeunit_file = os.path.join(subfolder, f"{codeunit_name}.codeunit.xml")
|
|
2092
|
-
if os.path.exists(codeunit_file):
|
|
2093
|
-
codeunits_with_dependent_codeunits[codeunit_name] = self.get_dependent_code_units(codeunit_file)
|
|
2094
|
-
else:
|
|
2095
|
-
raise ValueError(f"{repository_folder} does not have a codeunit with name {codeunit_name}.")
|
|
2096
|
-
sorted_codeunits = self.get_sorted_codeunits(codeunits_with_dependent_codeunits)
|
|
2097
|
-
project_version = self.get_version_of_project(repository_folder)
|
|
2098
|
-
if len(sorted_codeunits) == 0:
|
|
2099
|
-
raise ValueError(f'No codeunit found in subfolders of "{repository_folder}".')
|
|
2100
|
-
else:
|
|
2101
|
-
if verbosity > 1:
|
|
2102
|
-
GeneralUtilities.write_message_to_stdout(f"Attempt to build codeunits for version {project_version} in the following order:")
|
|
2103
|
-
i = 0
|
|
2104
|
-
for codeunit in sorted_codeunits:
|
|
2105
|
-
i = i+1
|
|
2106
|
-
GeneralUtilities.write_message_to_stdout(f"{i}.: {codeunit}")
|
|
2107
|
-
self.__do_repository_checks(repository_folder, project_version)
|
|
2108
|
-
line = "----------"
|
|
2109
|
-
for codeunit in sorted_codeunits:
|
|
2110
|
-
GeneralUtilities.write_message_to_stdout(line)
|
|
2111
|
-
self.__build_codeunit(os.path.join(repository_folder, codeunit), verbosity, target_environmenttype, additional_arguments_file, is_pre_merge, assume_dependent_codeunits_are_already_built, commandline_arguments)
|
|
2112
|
-
GeneralUtilities.write_message_to_stdout(line)
|
|
2113
|
-
if not contains_uncommitted_changes and self.__sc.git_repository_has_uncommitted_changes(repository_folder) and not is_pre_merge:
|
|
2114
|
-
message = f'Due to the build-process the repository "{repository_folder}" has new uncommitted changes.'
|
|
2115
|
-
if target_environmenttype == "Development":
|
|
2116
|
-
GeneralUtilities.write_message_to_stdout(message)
|
|
2117
|
-
else:
|
|
2118
|
-
raise ValueError(message)
|
|
2119
|
-
if export_target_directory is not None:
|
|
2120
|
-
project_name = self.get_project_name(repository_folder)
|
|
2121
|
-
for codeunit in sorted_codeunits:
|
|
2122
|
-
codeunit_version = self.get_version_of_codeunit_folder(os.path.join(repository_folder, codeunit))
|
|
2123
|
-
artifacts_folder = os.path.join(repository_folder, codeunit, "Other", "Artifacts")
|
|
2124
|
-
target_folder = os.path.join(export_target_directory, project_name, project_version, codeunit)
|
|
2125
|
-
GeneralUtilities.ensure_directory_does_not_exist(target_folder)
|
|
2126
|
-
GeneralUtilities.ensure_directory_exists(target_folder)
|
|
2127
|
-
filename_without_extension = f"{codeunit}.v{codeunit_version}.{target_environmenttype}.Artifacts"
|
|
2128
|
-
shutil.make_archive(filename_without_extension, 'zip', artifacts_folder)
|
|
2129
|
-
archive_file = os.path.join(os.getcwd(), f"{filename_without_extension}.zip")
|
|
2130
|
-
shutil.move(archive_file, target_folder)
|
|
2131
|
-
|
|
2132
|
-
@GeneralUtilities.check_arguments
|
|
2133
|
-
def __do_repository_checks(self, repository_folder: str, project_version: str) -> None:
|
|
2134
|
-
self.__check_if_folder_is_git_repository(repository_folder)
|
|
2135
|
-
self.__check_if_changelog_exists(repository_folder, project_version)
|
|
2136
|
-
self.__check_whether_security_txt_exists(repository_folder)
|
|
2137
|
-
self.__check_whether_workspace_file_exists(repository_folder)
|
|
2138
|
-
self.__check_for_staged_or_committed_ignored_files(repository_folder)
|
|
2139
|
-
|
|
2140
|
-
@GeneralUtilities.check_arguments
|
|
2141
|
-
def __check_if_folder_is_git_repository(self, repository_folder: str) -> None:
|
|
2142
|
-
if (not self.__sc.is_git_repository(repository_folder)):
|
|
2143
|
-
raise ValueError(f"Folder {repository_folder} is not a git-repository")
|
|
2144
|
-
|
|
2145
|
-
@GeneralUtilities.check_arguments
|
|
2146
|
-
def __check_if_changelog_exists(self, repository_folder: str, project_version: str) -> None:
|
|
2147
|
-
changelog_folder = os.path.join(repository_folder, "Other", "Resources", "Changelog")
|
|
2148
|
-
changelog_file = os.path.join(changelog_folder, f"v{project_version}.md")
|
|
2149
|
-
if not os.path.isfile(changelog_file):
|
|
2150
|
-
raise ValueError(f"Changelog-file '{changelog_file}' does not exist.")
|
|
2151
|
-
|
|
2152
|
-
@GeneralUtilities.check_arguments
|
|
2153
|
-
def __check_whether_security_txt_exists(self, repository_folder: str) -> None:
|
|
2154
|
-
security_txt_file_relative = ".well-known/security.txt"
|
|
2155
|
-
security_txt_file = GeneralUtilities.resolve_relative_path(security_txt_file_relative, repository_folder)
|
|
2156
|
-
if not os.path.isfile(security_txt_file):
|
|
2157
|
-
raise ValueError(f"The repository does not contain a '{security_txt_file_relative}'-file. See https://securitytxt.org/ for more information.")
|
|
2158
|
-
|
|
2159
|
-
@GeneralUtilities.check_arguments
|
|
2160
|
-
def __check_for_staged_or_committed_ignored_files(self, repository_folder: str) -> None:
|
|
2161
|
-
for file in self.__sc.get_staged_or_committed_git_ignored_files(repository_folder):
|
|
2162
|
-
GeneralUtilities.write_message_to_stderr(f'Warning: Repository contains staged or committed file "{file}" which is git-ignored.')
|
|
2163
|
-
|
|
2164
|
-
@GeneralUtilities.check_arguments
|
|
2165
|
-
def __check_whether_workspace_file_exists(self, repository_folder: str) -> None:
|
|
2166
|
-
count = 0
|
|
2167
|
-
for file in GeneralUtilities.get_direct_files_of_folder(repository_folder):
|
|
2168
|
-
if file.endswith(".code-workspace"):
|
|
2169
|
-
count = count + 1
|
|
2170
|
-
if count != 1:
|
|
2171
|
-
raise ValueError('The repository must contain exactly one ".code-workspace"-file on the top-level.')
|
|
2172
|
-
|
|
2173
|
-
@GeneralUtilities.check_arguments
|
|
2174
|
-
def update_dependency_in_resources_folder(self, update_dependencies_file, dependency_name: str, latest_version_function: str) -> None:
|
|
2175
|
-
dependency_folder = GeneralUtilities.resolve_relative_path(f"../Resources/Dependencies/{dependency_name}", update_dependencies_file)
|
|
2176
|
-
version_file = os.path.join(dependency_folder, "Version.txt")
|
|
2177
|
-
version_file_exists = os.path.isfile(version_file)
|
|
2178
|
-
write_to_file = False
|
|
2179
|
-
if version_file_exists:
|
|
2180
|
-
current_version = GeneralUtilities.read_text_from_file(version_file)
|
|
2181
|
-
if current_version != latest_version_function:
|
|
2182
|
-
write_to_file = True
|
|
2183
|
-
else:
|
|
2184
|
-
GeneralUtilities.ensure_directory_exists(dependency_folder)
|
|
2185
|
-
GeneralUtilities.ensure_file_exists(version_file)
|
|
2186
|
-
write_to_file = True
|
|
2187
|
-
if write_to_file:
|
|
2188
|
-
GeneralUtilities.write_text_to_file(version_file, latest_version_function)
|
|
2189
|
-
|
|
2190
|
-
@GeneralUtilities.check_arguments
|
|
2191
|
-
def __ensure_grylibrary_is_available(self, codeunit_folder: str) -> None:
|
|
2192
|
-
grylibrary_folder = os.path.join(codeunit_folder, "Other", "Resources", "GRYLibrary")
|
|
2193
|
-
grylibrary_dll_file = os.path.join(grylibrary_folder, "BuildResult_DotNet_win-x64", "GRYLibrary.dll")
|
|
2194
|
-
internet_connection_is_available = GeneralUtilities.internet_connection_is_available()
|
|
2195
|
-
grylibrary_dll_file_exists = os.path.isfile(grylibrary_dll_file)
|
|
2196
|
-
if internet_connection_is_available: # Load/Update GRYLibrary
|
|
2197
|
-
grylibrary_latest_codeunit_file = "https://raw.githubusercontent.com/anionDev/GRYLibrary/stable/GRYLibrary/GRYLibrary.codeunit.xml"
|
|
2198
|
-
with urllib.request.urlopen(grylibrary_latest_codeunit_file) as url_result:
|
|
2199
|
-
grylibrary_latest_version = self.get_version_of_codeunit_file_content(url_result.read().decode("utf-8"))
|
|
2200
|
-
if grylibrary_dll_file_exists:
|
|
2201
|
-
grylibrary_existing_codeunit_file = os.path.join(grylibrary_folder, "SourceCode", "GRYLibrary.codeunit.xml")
|
|
2202
|
-
grylibrary_existing_codeunit_version = self.get_version_of_codeunit(grylibrary_existing_codeunit_file)
|
|
2203
|
-
if grylibrary_existing_codeunit_version != grylibrary_latest_version:
|
|
2204
|
-
GeneralUtilities.ensure_directory_does_not_exist(grylibrary_folder)
|
|
2205
|
-
if not os.path.isfile(grylibrary_dll_file):
|
|
2206
|
-
GeneralUtilities.ensure_directory_does_not_exist(grylibrary_folder)
|
|
2207
|
-
GeneralUtilities.ensure_directory_exists(grylibrary_folder)
|
|
2208
|
-
archive_name = f"GRYLibrary.v{grylibrary_latest_version}.Productive.Artifacts.zip"
|
|
2209
|
-
archive_download_link = f"https://github.com/anionDev/GRYLibrary/releases/download/v{grylibrary_latest_version}/{archive_name}"
|
|
2210
|
-
archive_file = os.path.join(grylibrary_folder, archive_name)
|
|
2211
|
-
urllib.request.urlretrieve(archive_download_link, archive_file)
|
|
2212
|
-
with zipfile.ZipFile(archive_file, 'r') as zip_ref:
|
|
2213
|
-
zip_ref.extractall(grylibrary_folder)
|
|
2214
|
-
GeneralUtilities.ensure_file_does_not_exist(archive_file)
|
|
2215
|
-
else:
|
|
2216
|
-
if grylibrary_dll_file_exists:
|
|
2217
|
-
GeneralUtilities.write_message_to_stdout("Warning: Can not check for updates of GRYLibrary due to missing internet-connection.")
|
|
2218
|
-
else:
|
|
2219
|
-
raise ValueError("Can not download GRYLibrary.")
|
|
2220
|
-
|
|
2221
|
-
@GeneralUtilities.check_arguments
|
|
2222
|
-
def ensure_ffmpeg_is_available(self, codeunit_folder: str) -> None:
|
|
2223
|
-
ffmpeg_folder = os.path.join(codeunit_folder, "Other", "Resources", "FFMPEG")
|
|
2224
|
-
internet_connection_is_available = GeneralUtilities.internet_connection_is_available()
|
|
2225
|
-
exe_file = f"{ffmpeg_folder}/ffmpeg.exe"
|
|
2226
|
-
exe_file_exists = os.path.isfile(exe_file)
|
|
2227
|
-
if internet_connection_is_available: # Load/Update
|
|
2228
|
-
GeneralUtilities.ensure_directory_does_not_exist(ffmpeg_folder)
|
|
2229
|
-
GeneralUtilities.ensure_directory_exists(ffmpeg_folder)
|
|
2230
|
-
ffmpeg_temp_folder = ffmpeg_folder+"Temp"
|
|
2231
|
-
GeneralUtilities.ensure_directory_does_not_exist(ffmpeg_temp_folder)
|
|
2232
|
-
GeneralUtilities.ensure_directory_exists(ffmpeg_temp_folder)
|
|
2233
|
-
zip_file_on_disk = os.path.join(ffmpeg_temp_folder, "ffmpeg.zip")
|
|
2234
|
-
original_zip_filename = "ffmpeg-master-latest-win64-gpl-shared"
|
|
2235
|
-
zip_link = f"https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/{original_zip_filename}.zip"
|
|
2236
|
-
urllib.request.urlretrieve(zip_link, zip_file_on_disk)
|
|
2237
|
-
shutil.unpack_archive(zip_file_on_disk, ffmpeg_temp_folder)
|
|
2238
|
-
bin_folder_source = os.path.join(ffmpeg_temp_folder, "ffmpeg-master-latest-win64-gpl-shared/bin")
|
|
2239
|
-
bin_folder_target = ffmpeg_folder
|
|
2240
|
-
GeneralUtilities.copy_content_of_folder(bin_folder_source, bin_folder_target)
|
|
2241
|
-
GeneralUtilities.ensure_directory_does_not_exist(ffmpeg_temp_folder)
|
|
2242
|
-
else:
|
|
2243
|
-
if exe_file_exists:
|
|
2244
|
-
GeneralUtilities.write_message_to_stdout("Warning: Can not check for updates of FFMPEG due to missing internet-connection.")
|
|
2245
|
-
else:
|
|
2246
|
-
raise ValueError("Can not download FFMPEG.")
|
|
2247
|
-
|
|
2248
|
-
@GeneralUtilities.check_arguments
|
|
2249
|
-
def ensure_plantuml_is_available(self, target_folder: str) -> None:
|
|
2250
|
-
self.ensure_file_from_github_assets_is_available(target_folder, "plantuml", "plantuml", "PlantUML", "plantuml.jar", lambda latest_version: "plantuml.jar")
|
|
2251
|
-
|
|
2252
|
-
@GeneralUtilities.check_arguments
|
|
2253
|
-
def ensure_androidappbundletool_is_available(self, target_folder: str) -> None:
|
|
2254
|
-
self.ensure_file_from_github_assets_is_available(target_folder, "google", "bundletool", "AndroidAppBundleTool", "bundletool.jar", lambda latest_version: f"bundletool-all-{latest_version}.jar")
|
|
2255
|
-
|
|
2256
|
-
@GeneralUtilities.check_arguments
|
|
2257
|
-
def ensure_file_from_github_assets_is_available(self, target_folder: str, githubuser: str, githubprojectname: str, resource_name: str, local_filename: str, get_filename_on_github) -> None:
|
|
2258
|
-
resource_folder = os.path.join(target_folder, "Other", "Resources", resource_name)
|
|
2259
|
-
internet_connection_is_available = GeneralUtilities.internet_connection_is_available()
|
|
2260
|
-
file = f"{resource_folder}/{local_filename}"
|
|
2261
|
-
file_exists = os.path.isfile(file)
|
|
2262
|
-
if internet_connection_is_available: # Load/Update
|
|
2263
|
-
GeneralUtilities.ensure_directory_does_not_exist(resource_folder)
|
|
2264
|
-
GeneralUtilities.ensure_directory_exists(resource_folder)
|
|
2265
|
-
response = requests.get(f"https://api.github.com/repos/{githubuser}/{githubprojectname}/releases/latest", timeout=5)
|
|
2266
|
-
latest_version = response.json()["name"]
|
|
2267
|
-
filename_on_github = get_filename_on_github(latest_version)
|
|
2268
|
-
jar_link = f"https://github.com/{githubuser}/{githubprojectname}/releases/download/{latest_version}/{filename_on_github}"
|
|
2269
|
-
urllib.request.urlretrieve(jar_link, file)
|
|
2270
|
-
else:
|
|
2271
|
-
if file_exists:
|
|
2272
|
-
GeneralUtilities.write_message_to_stdout(f"Warning: Can not check for updates of {resource_name} due to missing internet-connection.")
|
|
2273
|
-
else:
|
|
2274
|
-
raise ValueError(f"Can not download {resource_name}.")
|
|
2275
|
-
|
|
2276
|
-
@GeneralUtilities.check_arguments
|
|
2277
|
-
def generate_svg_files_from_plantuml_files(self, target_folder: str) -> None:
|
|
2278
|
-
self.ensure_plantuml_is_available(target_folder)
|
|
2279
|
-
plant_uml_folder = os.path.join(target_folder, "Other", "Resources", "PlantUML")
|
|
2280
|
-
files_folder = os.path.join(target_folder, "Other/Reference")
|
|
2281
|
-
sc = ScriptCollectionCore()
|
|
2282
|
-
for file in GeneralUtilities.get_all_files_of_folder(files_folder):
|
|
2283
|
-
if file.endswith(".plantuml"):
|
|
2284
|
-
argument = ['-jar', f'{plant_uml_folder}/plantuml.jar', os.path.basename(file).replace("\\", "/"), '-tsvg']
|
|
2285
|
-
sc.run_program_argsasarray("java", argument, os.path.dirname(file), verbosity=0)
|
|
2286
|
-
|
|
2287
|
-
@GeneralUtilities.check_arguments
|
|
2288
|
-
def load_deb_control_file_content(self, file: str, codeunitname: str, codeunitversion: str, installedsize: int, maintainername: str, maintaineremail: str, description: str,) -> str:
|
|
2289
|
-
content = GeneralUtilities.read_text_from_file(file)
|
|
2290
|
-
content = GeneralUtilities.replace_variable_in_string(content, "codeunitname", codeunitname)
|
|
2291
|
-
content = GeneralUtilities.replace_variable_in_string(content, "codeunitversion", codeunitversion)
|
|
2292
|
-
content = GeneralUtilities.replace_variable_in_string(content, "installedsize", str(installedsize))
|
|
2293
|
-
content = GeneralUtilities.replace_variable_in_string(content, "maintainername", maintainername)
|
|
2294
|
-
content = GeneralUtilities.replace_variable_in_string(content, "maintaineremail", maintaineremail)
|
|
2295
|
-
content = GeneralUtilities.replace_variable_in_string(content, "description", description)
|
|
2296
|
-
return content
|
|
2297
|
-
|
|
2298
|
-
@GeneralUtilities.check_arguments
|
|
2299
|
-
def calculate_deb_package_size(self, binary_folder: str) -> int:
|
|
2300
|
-
size_in_bytes = 0
|
|
2301
|
-
for file in GeneralUtilities.get_all_files_of_folder(binary_folder):
|
|
2302
|
-
size_in_bytes = size_in_bytes+os.path.getsize(file)
|
|
2303
|
-
result = math.ceil(size_in_bytes/1024)
|
|
2304
|
-
return result
|
|
2305
|
-
|
|
2306
|
-
@GeneralUtilities.check_arguments
|
|
2307
|
-
def create_deb_package_for_artifact(self, codeunit_folder: str, maintainername: str, maintaineremail: str, description: str, verbosity: int, cmd_arguments: list[str]) -> None:
|
|
2308
|
-
verbosity = self.get_verbosity_from_commandline_arguments(cmd_arguments, verbosity)
|
|
2309
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
2310
|
-
binary_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts/BuildResult_DotNet_linux-x64", codeunit_folder)
|
|
2311
|
-
deb_output_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts/BuildResult_Deb", codeunit_folder)
|
|
2312
|
-
control_file = GeneralUtilities.resolve_relative_path("Other/Build/DebControlFile.txt", codeunit_folder)
|
|
2313
|
-
installedsize = self.calculate_deb_package_size(binary_folder)
|
|
2314
|
-
control_file_content = self.load_deb_control_file_content(control_file, codeunit_name, self.get_version_of_codeunit_folder(codeunit_folder), installedsize, maintainername, maintaineremail, description)
|
|
2315
|
-
self.__sc.create_deb_package(codeunit_name, binary_folder, control_file_content, deb_output_folder, verbosity, 555)
|
|
2316
|
-
|
|
2317
|
-
@GeneralUtilities.check_arguments
|
|
2318
|
-
def update_year_in_license_file_in_common_scripts_file(self, common_tasks_scripts_file: str) -> None:
|
|
2319
|
-
self.update_year_in_license_file(GeneralUtilities.resolve_relative_path("../../..", common_tasks_scripts_file))
|
|
2320
|
-
|
|
2321
|
-
@GeneralUtilities.check_arguments
|
|
2322
|
-
def update_year_in_license_file(self, repository_folder: str) -> None:
|
|
2323
|
-
self.__sc.update_year_in_first_line_of_file(os.path.join(repository_folder, "License.txt"))
|
|
2324
|
-
|
|
2325
|
-
@GeneralUtilities.check_arguments
|
|
2326
|
-
def update_year_for_dotnet_codeunit_in_common_scripts_file(self, common_tasks_scripts_file: str) -> None:
|
|
2327
|
-
self.update_year_for_dotnet_codeunit(GeneralUtilities.resolve_relative_path("../..", common_tasks_scripts_file))
|
|
2328
|
-
|
|
2329
|
-
@GeneralUtilities.check_arguments
|
|
2330
|
-
def update_year_for_dotnet_codeunit(self, codeunit_folder: str) -> None:
|
|
2331
|
-
codeunit_name = os.path.basename(codeunit_folder)
|
|
2332
|
-
csproj_file = os.path.join(codeunit_folder, codeunit_name, f"{codeunit_name}.csproj")
|
|
2333
|
-
self.__sc.update_year_in_copyright_tags(csproj_file)
|
|
2334
|
-
csprojtests_file = os.path.join(codeunit_folder, f"{codeunit_name}Tests", f"{codeunit_name}Tests.csproj")
|
|
2335
|
-
self.__sc.update_year_in_copyright_tags(csprojtests_file)
|
|
2336
|
-
nuspec_file = os.path.join(codeunit_folder, "Other", "Build", f"{codeunit_name}.nuspec")
|
|
2337
|
-
if os.path.isfile(nuspec_file):
|
|
2338
|
-
self.__sc.update_year_in_copyright_tags(nuspec_file)
|
|
2339
|
-
|
|
2340
|
-
@GeneralUtilities.check_arguments
|
|
2341
|
-
def repository_has_codeunits(self, repository: str, ignore_disabled_codeunits: bool = True) -> bool:
|
|
2342
|
-
return len(self.get_codeunits(repository, ignore_disabled_codeunits))
|
|
2343
|
-
|
|
2344
|
-
@GeneralUtilities.check_arguments
|
|
2345
|
-
def verify_artifact_exists(self, codeunit_folder: str, artifact_name_regexes: dict[str, bool]) -> None:
|
|
2346
|
-
codeunit_name: str = os.path.basename(codeunit_folder)
|
|
2347
|
-
artifacts_folder = os.path.join(codeunit_folder, "Other/Artifacts")
|
|
2348
|
-
existing_artifacts = [os.path.basename(x) for x in GeneralUtilities.get_direct_folders_of_folder(artifacts_folder)]
|
|
2349
|
-
for artifact_name_regex, required in artifact_name_regexes.items():
|
|
2350
|
-
artifact_exists = False
|
|
2351
|
-
for existing_artifact in existing_artifacts:
|
|
2352
|
-
pattern = re.compile(artifact_name_regex)
|
|
2353
|
-
if pattern.match(existing_artifact):
|
|
2354
|
-
artifact_exists = True
|
|
2355
|
-
if not artifact_exists:
|
|
2356
|
-
message = f"Codeunit {codeunit_name} does not contain an artifact which matches the name '{artifact_name_regex}'."
|
|
2357
|
-
if required:
|
|
2358
|
-
raise ValueError(message)
|
|
2359
|
-
else:
|
|
2360
|
-
GeneralUtilities.write_message_to_stderr(f"Warning: {message}")
|
|
2361
|
-
|
|
2362
|
-
@GeneralUtilities.check_arguments
|
|
2363
|
-
def __build_codeunit(self, codeunit_folder: str, verbosity: int = 1, target_environmenttype: str = "QualityCheck", additional_arguments_file: str = None, is_pre_merge: bool = False, assume_dependent_codeunits_are_already_built: bool = False, commandline_arguments: list[str] = []) -> None:
|
|
2364
|
-
now = datetime.now()
|
|
2365
|
-
codeunit_folder = GeneralUtilities.resolve_relative_path_from_current_working_directory(codeunit_folder)
|
|
2366
|
-
codeunit_name: str = os.path.basename(codeunit_folder)
|
|
2367
|
-
codeunit_file = os.path.join(codeunit_folder, f"{codeunit_name}.codeunit.xml")
|
|
2368
|
-
|
|
2369
|
-
if (not os.path.isfile(codeunit_file)):
|
|
2370
|
-
raise ValueError(f'"{codeunit_folder}" is no codeunit-folder.')
|
|
2371
|
-
|
|
2372
|
-
if not self.codeunit_is_enabled(codeunit_file):
|
|
2373
|
-
GeneralUtilities.write_message_to_stdout(f"Warning: Codeunit {codeunit_name} is disabled.")
|
|
2374
|
-
return
|
|
2375
|
-
|
|
2376
|
-
artifacts_folder = os.path.join(codeunit_folder, "Other", "Artifacts")
|
|
2377
|
-
GeneralUtilities.write_message_to_stdout(f"Start building codeunit {codeunit_name}.")
|
|
2378
|
-
GeneralUtilities.write_message_to_stdout(f"Build-environmenttype: {target_environmenttype}")
|
|
2379
|
-
GeneralUtilities.ensure_directory_does_not_exist(artifacts_folder)
|
|
2380
|
-
|
|
2381
|
-
verbosity_for_executed_programs = self.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
|
|
2382
|
-
|
|
2383
|
-
other_folder = os.path.join(codeunit_folder, "Other")
|
|
2384
|
-
build_folder = os.path.join(other_folder, "Build")
|
|
2385
|
-
quality_folder = os.path.join(other_folder, "QualityCheck")
|
|
2386
|
-
reference_folder = os.path.join(other_folder, "Reference")
|
|
2387
|
-
additional_arguments_c: str = ""
|
|
2388
|
-
additional_arguments_b: str = ""
|
|
2389
|
-
additional_arguments_r: str = ""
|
|
2390
|
-
additional_arguments_l: str = ""
|
|
2391
|
-
additional_arguments_g: str = ""
|
|
2392
|
-
additional_arguments_f: str = ""
|
|
2393
|
-
general_argument = f' --overwrite_verbosity {str(verbosity)} --overwrite_targetenvironmenttype {target_environmenttype}'
|
|
2394
|
-
|
|
2395
|
-
c_additionalargumentsfile_argument = ""
|
|
2396
|
-
|
|
2397
|
-
if is_pre_merge:
|
|
2398
|
-
general_argument = general_argument+" --overwrite_is_pre_merge true"
|
|
2399
|
-
GeneralUtilities.write_message_to_stdout("This is a pre-merge-build")
|
|
2400
|
-
|
|
2401
|
-
if assume_dependent_codeunits_are_already_built:
|
|
2402
|
-
c_additionalargumentsfile_argument = c_additionalargumentsfile_argument+" --overwrite_assume_dependent_codeunits_are_already_built true"
|
|
2403
|
-
diagnostic = False
|
|
2404
|
-
if diagnostic:
|
|
2405
|
-
GeneralUtilities.write_message_to_stdout("Assume dependent codeunits are already built")
|
|
2406
|
-
|
|
2407
|
-
if additional_arguments_file is not None:
|
|
2408
|
-
config = configparser.ConfigParser()
|
|
2409
|
-
config.read(additional_arguments_file)
|
|
2410
|
-
section_name = f"{codeunit_name}_Configuration"
|
|
2411
|
-
if config.has_option(section_name, "ArgumentsForCommonTasks"):
|
|
2412
|
-
additional_arguments_c = " " + config.get(section_name, "ArgumentsForCommonTasks")
|
|
2413
|
-
if config.has_option(section_name, "ArgumentsForBuild"):
|
|
2414
|
-
additional_arguments_b = " " + config.get(section_name, "ArgumentsForBuild")
|
|
2415
|
-
if config.has_option(section_name, "ArgumentsForRunTestcases"):
|
|
2416
|
-
additional_arguments_r = " " + config.get(section_name, "ArgumentsForRunTestcases")
|
|
2417
|
-
if config.has_option(section_name, "ArgumentsForLinting"):
|
|
2418
|
-
additional_arguments_l = " " + config.get(section_name, "ArgumentsForLinting")
|
|
2419
|
-
if config.has_option(section_name, "ArgumentsForGenerateReference"):
|
|
2420
|
-
additional_arguments_g = " " + config.get(section_name, "ArgumentsForGenerateReference")
|
|
2421
|
-
if config.has_option(section_name, "ArgumentsForOnFinish"):
|
|
2422
|
-
additional_arguments_f = " " + config.get(section_name, "ArgumentsForOnFinish")
|
|
2423
|
-
c_additionalargumentsfile_argument = f' --overwrite_additionalargumentsfile "{additional_arguments_file}"'
|
|
2424
|
-
|
|
2425
|
-
GeneralUtilities.write_message_to_stdout('Run "CommonTasks.py"...')
|
|
2426
|
-
self.__sc.run_program("python", f"CommonTasks.py{additional_arguments_c}{general_argument}{c_additionalargumentsfile_argument}", other_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2427
|
-
self.verify_artifact_exists(codeunit_folder, dict[str, bool]({"Changelog": False, "License": True, "DiffReport": True}))
|
|
2428
|
-
|
|
2429
|
-
GeneralUtilities.write_message_to_stdout('Run "Build.py"...')
|
|
2430
|
-
self.__sc.run_program("python", f"Build.py{additional_arguments_b}{general_argument}", build_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2431
|
-
self.verify_artifact_exists(codeunit_folder, dict[str, bool]({"BuildResult_.+": True, "BOM": False, "CodeAnalysisResult": False, "SourceCode": True}))
|
|
2432
|
-
|
|
2433
|
-
codeunit_hast_testable_sourcecode = self.codeunit_has_testable_sourcecode(codeunit_file)
|
|
2434
|
-
if codeunit_hast_testable_sourcecode:
|
|
2435
|
-
GeneralUtilities.write_message_to_stdout('Run "RunTestcases.py"...')
|
|
2436
|
-
self.__sc.run_program("python", f"RunTestcases.py{additional_arguments_r}{general_argument}", quality_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2437
|
-
self.verify_artifact_exists(codeunit_folder, dict[str, bool]({"TestCoverage": True, "TestCoverageReport": False}))
|
|
2438
|
-
|
|
2439
|
-
GeneralUtilities.write_message_to_stdout('Run "Linting.py"...')
|
|
2440
|
-
self.__sc.run_program("python", f"Linting.py{additional_arguments_l}{general_argument}", quality_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2441
|
-
self.verify_artifact_exists(codeunit_folder, dict[str, bool]())
|
|
2442
|
-
|
|
2443
|
-
GeneralUtilities.write_message_to_stdout('Run "GenerateReference.py"...')
|
|
2444
|
-
self.__sc.run_program("python", f"GenerateReference.py{additional_arguments_g}{general_argument}", reference_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2445
|
-
self.verify_artifact_exists(codeunit_folder, dict[str, bool]({"Reference": True}))
|
|
2446
|
-
|
|
2447
|
-
if os.path.isfile(os.path.join(other_folder, "OnBuildingFinished.py")):
|
|
2448
|
-
GeneralUtilities.write_message_to_stdout('Run "OnBuildingFinished.py"...')
|
|
2449
|
-
self.__sc.run_program("python", f"OnBuildingFinished.py{additional_arguments_f}{general_argument}", other_folder, verbosity=verbosity_for_executed_programs, throw_exception_if_exitcode_is_not_zero=True)
|
|
2450
|
-
|
|
2451
|
-
artifactsinformation_file = os.path.join(artifacts_folder, f"{codeunit_name}.artifactsinformation.xml")
|
|
2452
|
-
codeunit_version = self.get_version_of_codeunit(codeunit_file)
|
|
2453
|
-
GeneralUtilities.ensure_file_exists(artifactsinformation_file)
|
|
2454
|
-
artifacts_list = []
|
|
2455
|
-
for artifact_folder in GeneralUtilities.get_direct_folders_of_folder(artifacts_folder):
|
|
2456
|
-
artifact_name = os.path.basename(artifact_folder)
|
|
2457
|
-
artifacts_list.append(f" <cps:artifact>{artifact_name}<cps:artifact>")
|
|
2458
|
-
artifacts = '\n'.join(artifacts_list)
|
|
2459
|
-
moment = GeneralUtilities.datetime_to_string(now)
|
|
2460
|
-
# TODO implement usage of self.reference_latest_version_of_xsd_when_generating_xml
|
|
2461
|
-
GeneralUtilities.write_text_to_file(artifactsinformation_file, f"""<?xml version="1.0" encoding="UTF-8" ?>
|
|
2462
|
-
<cps:artifactsinformation xmlns:cps="https://projects.aniondev.de/PublicProjects/Common/ProjectTemplates/-/tree/main/Conventions/RepositoryStructure/CommonProjectStructure" artifactsinformationspecificationversion="1.0.0"
|
|
2463
|
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://raw.githubusercontent.com/anionDev/ProjectTemplates/main/Templates/Conventions/RepositoryStructure/CommonProjectStructure/artifactsinformation.xsd">
|
|
2464
|
-
<cps:name>{codeunit_name}</cps:name>
|
|
2465
|
-
<cps:version>{codeunit_version}</cps:version>
|
|
2466
|
-
<cps:timestamp>{moment}</cps:timestamp>
|
|
2467
|
-
<cps:targetenvironmenttype>{target_environmenttype}</cps:targetenvironmenttype>
|
|
2468
|
-
<cps:artifacts>
|
|
2469
|
-
{artifacts}
|
|
2470
|
-
</cps:artifacts>
|
|
2471
|
-
</cps:artifactsinformation>""")
|
|
2472
|
-
# TODO validate artifactsinformation_file against xsd
|
|
2473
|
-
GeneralUtilities.write_message_to_stdout(f"Finished building codeunit {codeunit_name} without errors.")
|
|
2474
|
-
|
|
2475
|
-
@GeneralUtilities.check_arguments
|
|
2476
|
-
def generic_update_dependencies(self, repository_folder: str):
|
|
2477
|
-
codeunits = self.get_codeunits(repository_folder)
|
|
2478
|
-
updated_dependencies = False
|
|
2479
|
-
verbosity: int = 1 # TODO set value dynamically
|
|
2480
|
-
for codeunit in codeunits:
|
|
2481
|
-
codeunit_file = os.path.join(repository_folder, codeunit, f"{codeunit}.codeunit.xml")
|
|
2482
|
-
codeunit_has_updatable_dependencies = self.codeunit_has_updatable_dependencies(codeunit_file)
|
|
2483
|
-
if codeunit_has_updatable_dependencies:
|
|
2484
|
-
codeunit_folder = os.path.join(repository_folder, codeunit)
|
|
2485
|
-
update_dependencies_script_folder = os.path.join(codeunit_folder, "Other")
|
|
2486
|
-
GeneralUtilities.ensure_directory_exists(os.path.join(update_dependencies_script_folder, "Resources", "CodeAnalysisResult"))
|
|
2487
|
-
self.__sc.run_program("python", "UpdateDependencies.py", update_dependencies_script_folder, verbosity)
|
|
2488
|
-
if self.__sc.git_repository_has_uncommitted_changes(repository_folder):
|
|
2489
|
-
updated_dependencies = True
|
|
2490
|
-
version_of_project = self.get_version_of_project(repository_folder)
|
|
2491
|
-
changelog_file = os.path.join(repository_folder, "Other", "Resources", "Changelog", f"v{version_of_project}.md")
|
|
2492
|
-
if not os.path.isfile(changelog_file):
|
|
2493
|
-
GeneralUtilities.write_text_to_file(changelog_file, """# Release notes
|
|
2494
|
-
|
|
2495
|
-
## Changes
|
|
2496
|
-
|
|
2497
|
-
- Updated dependencies.
|
|
2498
|
-
""")
|
|
2499
|
-
GeneralUtilities.write_message_to_stdout(f"Updated dependencies in codeunit {codeunit}.")
|
|
2500
|
-
else:
|
|
2501
|
-
GeneralUtilities.write_message_to_stdout(f"There are no dependencies to update in codeunit {codeunit}.")
|
|
2502
|
-
if updated_dependencies:
|
|
2503
|
-
self.__sc.run_program("scbuildcodeunits", "--targetenvironment QualityCheck", repository_folder) # TODO set verbosity
|
|
2504
|
-
self.__sc.git_commit(repository_folder, "Updated dependencies")
|
|
2505
|
-
|
|
2506
|
-
class GenericPrepareNewReleaseArguments:
|
|
2507
|
-
current_file: str
|
|
2508
|
-
product_name: str
|
|
2509
|
-
commandline_arguments: list[str]
|
|
2510
|
-
|
|
2511
|
-
def __init__(self, current_file: str, product_name: str, commandline_arguments: list[str]):
|
|
2512
|
-
self.current_file = current_file
|
|
2513
|
-
self.product_name = product_name
|
|
2514
|
-
self.commandline_arguments = commandline_arguments
|
|
2515
|
-
|
|
2516
|
-
@GeneralUtilities.check_arguments
|
|
2517
|
-
def generic_prepare_new_release(self, generic_prepare_new_release_arguments: GenericPrepareNewReleaseArguments):
|
|
2518
|
-
|
|
2519
|
-
# constants
|
|
2520
|
-
folder_of_this_file = os.path.dirname(generic_prepare_new_release_arguments.current_file)
|
|
2521
|
-
build_repository_folder = GeneralUtilities.resolve_relative_path("../..", folder_of_this_file)
|
|
2522
|
-
|
|
2523
|
-
repository_folder = GeneralUtilities.resolve_relative_path(f"../../Submodules/{generic_prepare_new_release_arguments.product_name}", folder_of_this_file)
|
|
2524
|
-
verbosity: int = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(generic_prepare_new_release_arguments.commandline_arguments, 1)
|
|
2525
|
-
|
|
2526
|
-
merge_source_branch = "other/next-release" # TODO make this configurable
|
|
2527
|
-
main_branch = "main" # TODO make this configurable
|
|
2528
|
-
|
|
2529
|
-
# prepare
|
|
2530
|
-
self.assert_no_uncommitted_changes(repository_folder)
|
|
2531
|
-
self.__sc.git_checkout(repository_folder, merge_source_branch)
|
|
2532
|
-
self.assert_no_uncommitted_changes(repository_folder)
|
|
2533
|
-
|
|
2534
|
-
if "--dependencyupdate" in generic_prepare_new_release_arguments.commandline_arguments:
|
|
2535
|
-
self.generic_update_dependencies(repository_folder)
|
|
2536
|
-
self.assert_no_uncommitted_changes(repository_folder)
|
|
2537
|
-
|
|
2538
|
-
merge_source_branch_commit_id = self.__sc.git_get_commit_id(repository_folder, merge_source_branch)
|
|
2539
|
-
main_branch_commit_id = self.__sc.git_get_commit_id(repository_folder, main_branch)
|
|
2540
|
-
if merge_source_branch_commit_id == main_branch_commit_id:
|
|
2541
|
-
GeneralUtilities.write_message_to_stdout("Release will not be prepared because there are no changed which can be released.")
|
|
2542
|
-
else:
|
|
2543
|
-
self.merge_to_main_branch(repository_folder, merge_source_branch, verbosity=verbosity, fast_forward_source_branch=True)
|
|
2544
|
-
self.__sc.git_commit(build_repository_folder, "Updated submodule due to merge to main-branch.")
|
|
2545
|
-
|
|
2546
|
-
class GenericCreateReleaseArguments():
|
|
2547
|
-
current_file: str
|
|
2548
|
-
product_name: str
|
|
2549
|
-
common_remote_name: str
|
|
2550
|
-
artifacts_target_folder: str
|
|
2551
|
-
commandline_arguments: list[str]
|
|
2552
|
-
|
|
2553
|
-
def __init__(self, current_file: str, product_name: str, common_remote_name: str, artifacts_target_folder: str, commandline_arguments: list[str]):
|
|
2554
|
-
self.current_file = current_file
|
|
2555
|
-
self.product_name = product_name
|
|
2556
|
-
self.common_remote_name = common_remote_name
|
|
2557
|
-
self.artifacts_target_folder = artifacts_target_folder
|
|
2558
|
-
self.commandline_arguments = commandline_arguments
|
|
2559
|
-
|
|
2560
|
-
@GeneralUtilities.check_arguments
|
|
2561
|
-
def generic_create_release(self, generic_create_release_arguments: GenericCreateReleaseArguments) -> tuple[bool, str]:
|
|
2562
|
-
folder_of_this_file = os.path.dirname(generic_create_release_arguments.current_file)
|
|
2563
|
-
build_repository_folder = GeneralUtilities.resolve_relative_path("../..", folder_of_this_file)
|
|
2564
|
-
repository_folder_name = generic_create_release_arguments.product_name
|
|
2565
|
-
repository_folder = GeneralUtilities.resolve_relative_path(f"../../Submodules/{generic_create_release_arguments.product_name}", folder_of_this_file)
|
|
2566
|
-
|
|
2567
|
-
merge_source_branch = "main" # TODO make this configurable
|
|
2568
|
-
main_branch = "stable" # TODO make this configurable
|
|
2569
|
-
|
|
2570
|
-
additional_arguments_file = os.path.join(folder_of_this_file, "AdditionalArguments.configuration")
|
|
2571
|
-
verbosity: int = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(generic_create_release_arguments.commandline_arguments, 1)
|
|
2572
|
-
createReleaseConfiguration: CreateReleaseConfiguration = CreateReleaseConfiguration(generic_create_release_arguments.product_name, generic_create_release_arguments.common_remote_name, generic_create_release_arguments.artifacts_target_folder, folder_of_this_file, verbosity, repository_folder, additional_arguments_file, repository_folder_name)
|
|
2573
|
-
|
|
2574
|
-
merge_source_branch_commit_id = self.__sc.git_get_commit_id(repository_folder, merge_source_branch)
|
|
2575
|
-
main_branch_commit_id = self.__sc.git_get_commit_id(repository_folder, main_branch)
|
|
2576
|
-
if merge_source_branch_commit_id == main_branch_commit_id:
|
|
2577
|
-
GeneralUtilities.write_message_to_stdout("Release will not be done because there are no changed which can be released.")
|
|
2578
|
-
return False, None
|
|
2579
|
-
else:
|
|
2580
|
-
self.__sc.git_checkout(repository_folder, merge_source_branch)
|
|
2581
|
-
reference_repo: str = os.path.join(build_repository_folder, "Submodules", f"{generic_create_release_arguments.product_name}Reference")
|
|
2582
|
-
self.__sc.git_commit(reference_repo, "Updated reference")
|
|
2583
|
-
self.__sc.git_commit(build_repository_folder, "Updated submodule")
|
|
2584
|
-
|
|
2585
|
-
# create release
|
|
2586
|
-
new_version = self.merge_to_stable_branch(generic_create_release_arguments.current_file, createReleaseConfiguration)
|
|
2587
|
-
return True, new_version
|
|
2588
|
-
|
|
2589
|
-
class UpdateHTTPDocumentationArguments:
|
|
2590
|
-
current_file: str
|
|
2591
|
-
product_name: str
|
|
2592
|
-
common_remote_name: str
|
|
2593
|
-
new_project_version: str
|
|
2594
|
-
reference_repository_name: str
|
|
2595
|
-
commandline_arguments: list[str]
|
|
2596
|
-
main_branch_name: str
|
|
2597
|
-
|
|
2598
|
-
def __init__(self, current_file: str, product_name: str, common_remote_name: str, new_project_version: str, reference_repository_name: str, commandline_arguments: list[str]):
|
|
2599
|
-
self.current_file = current_file
|
|
2600
|
-
self.product_name = product_name
|
|
2601
|
-
self.common_remote_name = common_remote_name
|
|
2602
|
-
self.new_project_version = new_project_version
|
|
2603
|
-
self.reference_repository_name = reference_repository_name
|
|
2604
|
-
self.commandline_arguments = commandline_arguments
|
|
2605
|
-
self.main_branch_name = "main"
|
|
2606
|
-
|
|
2607
|
-
@GeneralUtilities.check_arguments
|
|
2608
|
-
def update_http_documentation(self, update_http_documentation_arguments: UpdateHTTPDocumentationArguments):
|
|
2609
|
-
folder_of_this_file = str(os.path.dirname(update_http_documentation_arguments.current_file))
|
|
2610
|
-
|
|
2611
|
-
ref_repo = GeneralUtilities.resolve_relative_path(f"../../Submodules/{update_http_documentation_arguments.reference_repository_name}", folder_of_this_file)
|
|
2612
|
-
self.__sc.git_checkout(ref_repo, update_http_documentation_arguments.main_branch_name)
|
|
2613
|
-
|
|
2614
|
-
# update reference
|
|
2615
|
-
target = os.path.join(ref_repo, "Reference", update_http_documentation_arguments.product_name)
|
|
2616
|
-
GeneralUtilities.ensure_directory_does_not_exist(target)
|
|
2617
|
-
shutil.copytree(GeneralUtilities.resolve_relative_path(f"../../Submodules/{update_http_documentation_arguments.product_name}Reference/ReferenceContent", folder_of_this_file), target)
|
|
2618
|
-
self.__sc.git_commit(ref_repo, f"Added reference of {update_http_documentation_arguments.product_name} v{update_http_documentation_arguments.new_project_version}")
|
|
2619
|
-
|
|
2620
|
-
# Sync reference-repository
|
|
2621
|
-
self.__sc.git_fetch(ref_repo, update_http_documentation_arguments.common_remote_name)
|
|
2622
|
-
self.__sc.git_merge(ref_repo, update_http_documentation_arguments.common_remote_name+"/"+update_http_documentation_arguments.main_branch_name, update_http_documentation_arguments.main_branch_name)
|
|
2623
|
-
self.__sc.git_checkout(ref_repo, update_http_documentation_arguments.main_branch_name)
|
|
2624
|
-
self.__sc.git_push(ref_repo, update_http_documentation_arguments.common_remote_name, update_http_documentation_arguments.main_branch_name, update_http_documentation_arguments.main_branch_name)
|
|
2625
|
-
self.__sc.git_commit(GeneralUtilities.resolve_relative_path("../..", folder_of_this_file), f"Updated content of {update_http_documentation_arguments.product_name} v{update_http_documentation_arguments.new_project_version} in {update_http_documentation_arguments.reference_repository_name}-submodule")
|