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.
Files changed (45) hide show
  1. ScriptCollection/AnionBuildPlatform.py +206 -0
  2. ScriptCollection/{UpdateCertificates.py → CertificateUpdater.py} +69 -46
  3. ScriptCollection/Executables.py +515 -18
  4. ScriptCollection/GeneralUtilities.py +1272 -873
  5. ScriptCollection/ImageUpdater.py +648 -0
  6. ScriptCollection/ProgramRunnerBase.py +10 -10
  7. ScriptCollection/ProgramRunnerMock.py +2 -0
  8. ScriptCollection/ProgramRunnerPopen.py +7 -1
  9. ScriptCollection/ProgramRunnerSudo.py +108 -0
  10. ScriptCollection/SCLog.py +115 -0
  11. ScriptCollection/ScriptCollectionCore.py +942 -266
  12. ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +95 -0
  13. ScriptCollection/TFCPS/Docker/__init__.py +0 -0
  14. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py +8 -0
  15. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py +6 -0
  16. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationNoGenerate.py +7 -0
  17. ScriptCollection/TFCPS/DotNet/TFCPS_CodeUnitSpecific_DotNet.py +485 -0
  18. ScriptCollection/TFCPS/DotNet/__init__.py +0 -0
  19. ScriptCollection/TFCPS/Flutter/TFCPS_CodeUnitSpecific_Flutter.py +130 -0
  20. ScriptCollection/TFCPS/Flutter/__init__.py +0 -0
  21. ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py +74 -0
  22. ScriptCollection/TFCPS/Go/__init__.py +0 -0
  23. ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py +131 -0
  24. ScriptCollection/TFCPS/NodeJS/__init__.py +0 -0
  25. ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +227 -0
  26. ScriptCollection/TFCPS/Python/__init__.py +0 -0
  27. ScriptCollection/TFCPS/TFCPS_CodeUnitSpecific_Base.py +418 -0
  28. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnit.py +128 -0
  29. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnits.py +136 -0
  30. ScriptCollection/TFCPS/TFCPS_CreateRelease.py +95 -0
  31. ScriptCollection/TFCPS/TFCPS_Generic.py +43 -0
  32. ScriptCollection/TFCPS/TFCPS_MergeToMain.py +122 -0
  33. ScriptCollection/TFCPS/TFCPS_MergeToStable.py +350 -0
  34. ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py +47 -0
  35. ScriptCollection/TFCPS/TFCPS_Tools_General.py +1356 -0
  36. ScriptCollection/TFCPS/__init__.py +0 -0
  37. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/METADATA +23 -22
  38. scriptcollection-4.0.78.dist-info/RECORD +43 -0
  39. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/WHEEL +1 -1
  40. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/entry_points.txt +32 -0
  41. ScriptCollection/ProgramRunnerEpew.py +0 -122
  42. ScriptCollection/RPStream.py +0 -42
  43. ScriptCollection/TasksForCommonProjectStructure.py +0 -2625
  44. ScriptCollection-3.5.16.dist-info/RECORD +0 -16
  45. {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")