ScriptCollection 4.2.58__py3-none-any.whl → 4.2.60__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ScriptCollection/Executables.py +1 -1
- ScriptCollection/GeneralUtilities.py +105 -1
- ScriptCollection/ScriptCollectionCore.py +71 -29
- ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +34 -18
- ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +2 -3
- ScriptCollection/TFCPS/TFCPS_Tools_General.py +125 -52
- {scriptcollection-4.2.58.dist-info → scriptcollection-4.2.60.dist-info}/METADATA +1 -1
- {scriptcollection-4.2.58.dist-info → scriptcollection-4.2.60.dist-info}/RECORD +11 -11
- {scriptcollection-4.2.58.dist-info → scriptcollection-4.2.60.dist-info}/WHEEL +0 -0
- {scriptcollection-4.2.58.dist-info → scriptcollection-4.2.60.dist-info}/entry_points.txt +0 -0
- {scriptcollection-4.2.58.dist-info → scriptcollection-4.2.60.dist-info}/top_level.txt +0 -0
ScriptCollection/Executables.py
CHANGED
|
@@ -900,7 +900,7 @@ def AddImageToCustomRegistry()->int:
|
|
|
900
900
|
sc:ScriptCollectionCore=ScriptCollectionCore()
|
|
901
901
|
verbosity=int(args.verbosity)
|
|
902
902
|
sc.log.loglevel=LogLevel(verbosity)
|
|
903
|
-
sc.add_image_to_custom_docker_image_registry(args.remotehub,args.imagenameonremotehub,args.ownregistryaddress,args.imagenameonownregistry,args.tag,args.username,args.password
|
|
903
|
+
sc.add_image_to_custom_docker_image_registry(args.remotehub,args.imagenameonremotehub,args.ownregistryaddress,args.imagenameonownregistry,args.tag,args.username,args.password)
|
|
904
904
|
return 0
|
|
905
905
|
|
|
906
906
|
def SyncXlfFiles()->int:
|
|
@@ -35,6 +35,12 @@ class VersionEcholon(Enum):
|
|
|
35
35
|
LatestPatchOrLatestMinorOrNextMajor = 2
|
|
36
36
|
LatestVersion = 3
|
|
37
37
|
|
|
38
|
+
class Platform(Enum):
|
|
39
|
+
Windows_AMD64 = 0
|
|
40
|
+
Linux_AMD64 = 1
|
|
41
|
+
Linux_ARM64 = 2
|
|
42
|
+
MacOS_ARM64 = 3
|
|
43
|
+
|
|
38
44
|
|
|
39
45
|
class Dependency:
|
|
40
46
|
dependencyname:str
|
|
@@ -1429,4 +1435,102 @@ class GeneralUtilities:
|
|
|
1429
1435
|
result= GeneralUtilities.escape_json_property_value(str_value)
|
|
1430
1436
|
#do other adaptions here if desired/required
|
|
1431
1437
|
return result
|
|
1432
|
-
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
@staticmethod
|
|
1441
|
+
@check_arguments
|
|
1442
|
+
def get_current_platform() -> Platform:
|
|
1443
|
+
system = platform.system().lower()
|
|
1444
|
+
machine = platform.machine().lower()
|
|
1445
|
+
|
|
1446
|
+
if system == "windows" and machine in ("x86_64", "amd64"):
|
|
1447
|
+
return Platform.Windows_AMD64
|
|
1448
|
+
elif system == "linux" and machine in ("x86_64", "amd64"):
|
|
1449
|
+
return Platform.Linux_AMD64
|
|
1450
|
+
elif system == "linux" and machine in ("arm64", "aarch64"):
|
|
1451
|
+
return Platform.Linux_ARM64
|
|
1452
|
+
elif system == "darwin" and machine in ("x86_64", "amd64"):
|
|
1453
|
+
return Platform.MacOS_ARM64
|
|
1454
|
+
else:
|
|
1455
|
+
raise ValueError(f"Unsupported platform: {system}/{machine}")
|
|
1456
|
+
|
|
1457
|
+
@staticmethod
|
|
1458
|
+
@check_arguments
|
|
1459
|
+
def platform_to_short_str(platform_value: Platform) -> str:
|
|
1460
|
+
mapping = {
|
|
1461
|
+
Platform.Windows_AMD64: "win-x64",
|
|
1462
|
+
Platform.Linux_AMD64: "linux-x64",
|
|
1463
|
+
Platform.Linux_ARM64: "linux-arm64",
|
|
1464
|
+
Platform.MacOS_ARM64: "osx-arm64",
|
|
1465
|
+
}
|
|
1466
|
+
return mapping[platform_value]
|
|
1467
|
+
|
|
1468
|
+
@staticmethod
|
|
1469
|
+
@check_arguments
|
|
1470
|
+
def platform_from_short_str(platform_str: str) -> Platform:
|
|
1471
|
+
mapping = {
|
|
1472
|
+
"win-x64": Platform.Windows_AMD64,
|
|
1473
|
+
"linux-x64": Platform.Linux_AMD64,
|
|
1474
|
+
"linux-arm64": Platform.Linux_ARM64,
|
|
1475
|
+
"osx-arm64": Platform.MacOS_ARM64,
|
|
1476
|
+
}
|
|
1477
|
+
if platform_str not in mapping:
|
|
1478
|
+
raise ValueError(f"Unsupported platform string: {platform_str}")
|
|
1479
|
+
return mapping[platform_str]
|
|
1480
|
+
|
|
1481
|
+
@staticmethod
|
|
1482
|
+
@check_arguments
|
|
1483
|
+
def platform_to_dash_str(platform_value: Platform) -> str:
|
|
1484
|
+
mapping = {
|
|
1485
|
+
Platform.Windows_AMD64: "Windows-x64",
|
|
1486
|
+
Platform.Linux_AMD64: "Linux-x64",
|
|
1487
|
+
Platform.Linux_ARM64: "Linux-arm64",
|
|
1488
|
+
Platform.MacOS_ARM64: "MacOS-arm64",
|
|
1489
|
+
}
|
|
1490
|
+
return mapping[platform_value]
|
|
1491
|
+
|
|
1492
|
+
@staticmethod
|
|
1493
|
+
@check_arguments
|
|
1494
|
+
def platform_from_dash_str(platform_str: str) -> Platform:
|
|
1495
|
+
mapping = {
|
|
1496
|
+
"Windows-x64": Platform.Windows_AMD64,
|
|
1497
|
+
"Linux-x64": Platform.Linux_AMD64,
|
|
1498
|
+
"Linux-arm64": Platform.Linux_ARM64,
|
|
1499
|
+
"MacOS-arm64": Platform.MacOS_ARM64,
|
|
1500
|
+
}
|
|
1501
|
+
if platform_str not in mapping:
|
|
1502
|
+
raise ValueError(f"Unsupported platform string: {platform_str}")
|
|
1503
|
+
return mapping[platform_str]
|
|
1504
|
+
|
|
1505
|
+
@staticmethod
|
|
1506
|
+
@check_arguments
|
|
1507
|
+
def platform_to_docker_platform_str(platform_value: Platform) -> str:
|
|
1508
|
+
mapping = {
|
|
1509
|
+
Platform.Windows_AMD64: "windows/amd64",
|
|
1510
|
+
Platform.Linux_AMD64: "linux/amd64",
|
|
1511
|
+
Platform.Linux_ARM64: "linux/arm64",
|
|
1512
|
+
Platform.MacOS_ARM64: "linux/arm64", # macOS → linux container
|
|
1513
|
+
}
|
|
1514
|
+
return mapping[platform_value]
|
|
1515
|
+
|
|
1516
|
+
@staticmethod
|
|
1517
|
+
@check_arguments
|
|
1518
|
+
def platform_to_dotnet_runtime_identifier(platform_value: Platform) -> str:
|
|
1519
|
+
mapping = {
|
|
1520
|
+
Platform.Windows_AMD64: "win-x64",
|
|
1521
|
+
Platform.Linux_AMD64: "linux-x64",
|
|
1522
|
+
Platform.Linux_ARM64: "linux-arm64",
|
|
1523
|
+
Platform.MacOS_ARM64: "osx-arm64",
|
|
1524
|
+
}
|
|
1525
|
+
return mapping[platform_value]
|
|
1526
|
+
|
|
1527
|
+
@staticmethod
|
|
1528
|
+
@check_arguments
|
|
1529
|
+
def get_all_platforms() -> list[Platform]:
|
|
1530
|
+
return [
|
|
1531
|
+
Platform.Windows_AMD64,
|
|
1532
|
+
Platform.Linux_AMD64,
|
|
1533
|
+
Platform.Linux_ARM64,
|
|
1534
|
+
Platform.MacOS_ARM64,
|
|
1535
|
+
]
|
|
1536
|
+
|
|
@@ -31,12 +31,12 @@ import qrcode
|
|
|
31
31
|
import pycdlib
|
|
32
32
|
import send2trash
|
|
33
33
|
from pypdf import PdfReader, PdfWriter
|
|
34
|
-
from .GeneralUtilities import GeneralUtilities
|
|
34
|
+
from .GeneralUtilities import GeneralUtilities,Platform
|
|
35
35
|
from .ProgramRunnerBase import ProgramRunnerBase
|
|
36
36
|
from .ProgramRunnerPopen import ProgramRunnerPopen
|
|
37
37
|
from .SCLog import SCLog, LogLevel
|
|
38
38
|
|
|
39
|
-
version = "4.2.
|
|
39
|
+
version = "4.2.60"
|
|
40
40
|
__version__ = version
|
|
41
41
|
|
|
42
42
|
class VSCodeWorkspaceShellTask:
|
|
@@ -215,29 +215,49 @@ class ScriptCollectionCore:
|
|
|
215
215
|
|
|
216
216
|
def registry_contains_image(self,registry_url:str,image:str,registry_username:str,registry_password:str)->bool:
|
|
217
217
|
"""This function assumes that the registry is a custom deployed docker-registry (see https://hub.docker.com/_/registry )"""
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
try:
|
|
219
|
+
if "/" in image:
|
|
220
|
+
image=image.rsplit("/", 1)[-1]
|
|
221
|
+
registry_username,registry_password=self.__load_credentials_if_required_and_available(registry_url,registry_username,registry_password)
|
|
222
|
+
catalog_url = f"{registry_url}/v2/_catalog"
|
|
223
|
+
response = requests.get(catalog_url, auth=(registry_username, registry_password),timeout=20)
|
|
224
|
+
response.raise_for_status() # check if statuscode = 200
|
|
225
|
+
data = response.json()
|
|
226
|
+
# expected: {"repositories": ["nginx", "myapp"]}
|
|
227
|
+
images = data.get("repositories", [])
|
|
228
|
+
if not (image in images):
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
if self.get_tags_of_images_from_registry(registry_url,image,registry_username,registry_password)<1:
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
return True
|
|
235
|
+
except Exception:
|
|
236
|
+
return False
|
|
229
237
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
def docker_platform_to_slug(self,platform_value: Platform) -> str:
|
|
239
|
+
if platform_value == Platform.Linux_AMD64:
|
|
240
|
+
return "linux-amd64"
|
|
241
|
+
elif platform_value == Platform.Linux_ARM64:
|
|
242
|
+
return "linux-arm64"
|
|
243
|
+
raise ValueError(f"Unsupported platform: {platform_value}")
|
|
244
|
+
|
|
245
|
+
@GeneralUtilities.check_arguments
|
|
246
|
+
def add_image_to_custom_docker_image_registry(
|
|
247
|
+
self,
|
|
248
|
+
remote_hub: str,
|
|
249
|
+
imagename_on_remote_hub: str,
|
|
250
|
+
own_registry_address: str,
|
|
251
|
+
imagename_on_own_registry: str,
|
|
252
|
+
tag: str,
|
|
253
|
+
registry_username: str,
|
|
254
|
+
registry_password: str,
|
|
255
|
+
) -> None:
|
|
256
|
+
registry_username, registry_password = self.__load_credentials_if_required_and_available(remote_hub, registry_username, registry_password)
|
|
257
|
+
source_address = f"{remote_hub}/{imagename_on_remote_hub}:{tag}"
|
|
258
|
+
target_address = f"{own_registry_address}/{imagename_on_own_registry}:{tag}"
|
|
259
|
+
self.run_program("docker", f"buildx imagetools create --tag {target_address} {source_address}")#this does pull and push for each platform
|
|
260
|
+
|
|
241
261
|
|
|
242
262
|
def get_tags_of_images_from_registry(self,registry_base_url:str,image:str,registry_username:str,registry_password:str)->list[str]:
|
|
243
263
|
"""registry_base_url must be in the format 'https://myregistry.example.com'
|
|
@@ -2870,6 +2890,7 @@ OCR-content:
|
|
|
2870
2890
|
if remove_images:
|
|
2871
2891
|
self.run_program_with_retry("docker","image prune -a -f",amount_of_attempts=amount_of_attempts)
|
|
2872
2892
|
self.run_program_with_retry("docker","builder prune -a -f",amount_of_attempts=amount_of_attempts)
|
|
2893
|
+
self.run_program_with_retry("docker","buildx prune -f",amount_of_attempts=amount_of_attempts,throw_exception_if_exitcode_is_not_zero=False) # buildx prune is not available on every machine.
|
|
2873
2894
|
self.run_program_with_retry("docker","system df",print_live_output=self.log.loglevel==LogLevel.Debug,amount_of_attempts=amount_of_attempts)
|
|
2874
2895
|
|
|
2875
2896
|
@GeneralUtilities.check_arguments
|
|
@@ -3202,9 +3223,11 @@ OCR-content:
|
|
|
3202
3223
|
return results[0]["language"]
|
|
3203
3224
|
|
|
3204
3225
|
@GeneralUtilities.check_arguments
|
|
3205
|
-
def get_all_files_in_git_repository(self,repository_folder:str,include_submodules: bool = True) -> list[str]:
|
|
3206
|
-
"""
|
|
3207
|
-
cmd = ["ls-files", "--cached"
|
|
3226
|
+
def get_all_files_in_git_repository(self,repository_folder:str,ignore_ignored_files:bool=True,include_submodules: bool = True) -> list[str]:
|
|
3227
|
+
"""Returns a list of all files in a git-repository."""
|
|
3228
|
+
cmd = ["ls-files", "--cached"]
|
|
3229
|
+
if ignore_ignored_files:
|
|
3230
|
+
cmd.append("--exclude-standard")
|
|
3208
3231
|
if include_submodules:
|
|
3209
3232
|
cmd.append("--recurse-submodules")
|
|
3210
3233
|
output=self.run_program_argsasarray("git", cmd,repository_folder)
|
|
@@ -3212,10 +3235,29 @@ OCR-content:
|
|
|
3212
3235
|
return files
|
|
3213
3236
|
|
|
3214
3237
|
@GeneralUtilities.check_arguments
|
|
3215
|
-
def write_file_list_for_repository(self,repository_folder:str,target_file:str="./FileList.txt") -> None:
|
|
3238
|
+
def write_file_list_for_repository(self,repository_folder:str,target_file:str="./FileList.txt",ignore_ignored_files:bool=True,include_submodules: bool = True) -> None:
|
|
3216
3239
|
if os.path.isabs(target_file):
|
|
3217
3240
|
target_file=GeneralUtilities.resolve_relative_path(target_file,repository_folder)
|
|
3218
3241
|
target_file=GeneralUtilities.normalize_path(target_file)
|
|
3219
|
-
files=self.get_all_files_in_git_repository(repository_folder)
|
|
3242
|
+
files=self.get_all_files_in_git_repository(repository_folder,ignore_ignored_files,include_submodules)
|
|
3220
3243
|
GeneralUtilities.ensure_file_exists(target_file)
|
|
3221
3244
|
GeneralUtilities.write_lines_to_file(target_file, files)
|
|
3245
|
+
|
|
3246
|
+
@GeneralUtilities.check_arguments
|
|
3247
|
+
def get_all_commits_in_git_repository(self,repository_folder:str,include_all_heads:bool=False) -> list[str]:
|
|
3248
|
+
"""Returns a textual visualization of all commits in a git-repository."""
|
|
3249
|
+
#do 'git log --reverse --all --pretty=format:"%ci | %H | %cn <%ce> | %s"'
|
|
3250
|
+
args = ["log", "--reverse", "--pretty=format:%ci | %H | %cn <%ce> | %s"]
|
|
3251
|
+
if include_all_heads:
|
|
3252
|
+
args.append("--all")
|
|
3253
|
+
result=self.run_program_argsasarray("git", args, repository_folder, throw_exception_if_exitcode_is_not_zero=True)
|
|
3254
|
+
return result[1]
|
|
3255
|
+
|
|
3256
|
+
@GeneralUtilities.check_arguments
|
|
3257
|
+
def write_commit_list_for_repository(self,repository_folder:str,target_file:str,include_all_heads:bool=False) -> None:
|
|
3258
|
+
if os.path.isabs(target_file):
|
|
3259
|
+
target_file=GeneralUtilities.resolve_relative_path(target_file,repository_folder)
|
|
3260
|
+
target_file=GeneralUtilities.normalize_path(target_file)
|
|
3261
|
+
commits=self.get_all_commits_in_git_repository(repository_folder, include_all_heads)
|
|
3262
|
+
GeneralUtilities.ensure_file_exists(target_file)
|
|
3263
|
+
GeneralUtilities.write_lines_to_file(target_file, commits)
|
|
@@ -3,7 +3,7 @@ from urllib import request
|
|
|
3
3
|
import time
|
|
4
4
|
import ssl
|
|
5
5
|
from datetime import timedelta,datetime
|
|
6
|
-
from ...GeneralUtilities import GeneralUtilities
|
|
6
|
+
from ...GeneralUtilities import GeneralUtilities, Platform
|
|
7
7
|
from ...SCLog import LogLevel
|
|
8
8
|
from ..TFCPS_CodeUnitSpecific_Base import TFCPS_CodeUnitSpecific_Base,TFCPS_CodeUnitSpecific_Base_CLI
|
|
9
9
|
|
|
@@ -13,32 +13,36 @@ class TFCPS_CodeUnitSpecific_Docker_Functions(TFCPS_CodeUnitSpecific_Base):
|
|
|
13
13
|
def __init__(self,current_file:str,verbosity:LogLevel,targetenvironmenttype:str,use_cache:bool,is_pre_merge:bool):
|
|
14
14
|
super().__init__(current_file, verbosity,targetenvironmenttype,use_cache,is_pre_merge)
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
@GeneralUtilities.check_arguments
|
|
17
|
-
def build(self,custom_arguments:dict[str,str]) -> None:
|
|
18
|
+
def build(self,platforms:list[Platform],custom_arguments:dict[str,str]) -> None:
|
|
18
19
|
codeunitname: str =self.get_codeunit_name()
|
|
19
|
-
codeunit_folder =self.get_codeunit_folder()
|
|
20
|
+
codeunit_folder =self.get_codeunit_folder()
|
|
20
21
|
codeunitname_lower = codeunitname.lower()
|
|
21
22
|
codeunit_file =self.get_codeunit_file()
|
|
22
23
|
codeunitversion = self.tfcps_Tools_General.get_version_of_codeunit(codeunit_file)
|
|
23
|
-
args = ["image", "build", "--pull", "--force-rm", "--progress=plain", "--build-arg", f"TargetEnvironmentType={self.get_target_environment_type()}", "--build-arg", f"CodeUnitName={codeunitname}", "--build-arg", f"CodeUnitVersion={codeunitversion}", "--build-arg", f"CodeUnitOwnerName={self.tfcps_Tools_General.get_codeunit_owner_name(self.get_codeunit_file())}", "--build-arg", f"CodeUnitOwnerEMailAddress={self.tfcps_Tools_General.get_codeunit_owner_emailaddress(self.get_codeunit_file())}"]
|
|
24
24
|
if custom_arguments is None:
|
|
25
25
|
custom_arguments=dict[str,str]()
|
|
26
|
-
for custom_argument_key, custom_argument_value in custom_arguments.items():
|
|
27
|
-
args.append("--build-arg")
|
|
28
|
-
args.append(f"{custom_argument_key}={custom_argument_value}")
|
|
29
|
-
args = args+["--tag", f"{codeunitname_lower}:latest", "--tag", f"{codeunitname_lower}:{codeunitversion}", "--file", f"{codeunitname}/Dockerfile"]
|
|
30
|
-
if not self.use_cache():
|
|
31
|
-
args.append("--no-cache")
|
|
32
|
-
args.append(".")
|
|
33
|
-
codeunit_content_folder = os.path.join(codeunit_folder)
|
|
34
|
-
GeneralUtilities.retry_action(lambda: self._protected_sc.run_program_argsasarray("docker", args, codeunit_content_folder, print_errors_as_information=True), 3)
|
|
35
26
|
artifacts_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts", codeunit_folder)
|
|
36
27
|
app_artifacts_folder = os.path.join(artifacts_folder, "BuildResult_OCIImage")
|
|
37
|
-
GeneralUtilities.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
GeneralUtilities.ensure_folder_exists_and_is_empty(app_artifacts_folder)
|
|
29
|
+
for platform in platforms:
|
|
30
|
+
#builder must be created once before with "docker buildx create --use"
|
|
31
|
+
args = ["buildx","build", "--platform",GeneralUtilities.platform_to_docker_platform_str(platform), "--pull", "--force-rm", "--progress=plain", "--build-arg", f"TargetEnvironmentType={self.get_target_environment_type()}", "--build-arg", f"CodeUnitName={codeunitname}", "--build-arg", f"CodeUnitVersion={codeunitversion}", "--build-arg", f"CodeUnitOwnerName={self.tfcps_Tools_General.get_codeunit_owner_name(self.get_codeunit_file())}", "--build-arg", f"CodeUnitOwnerEMailAddress={self.tfcps_Tools_General.get_codeunit_owner_emailaddress(self.get_codeunit_file())}", "--build-arg", f"Platform={GeneralUtilities.platform_to_dash_str(platform)}", "--build-arg", f"DotNetRuntime={GeneralUtilities.platform_to_dotnet_runtime_identifier(platform)}"]
|
|
32
|
+
for custom_argument_key, custom_argument_value in custom_arguments.items():
|
|
33
|
+
args.append("--build-arg")
|
|
34
|
+
args.append(f"{custom_argument_key}={custom_argument_value}")
|
|
35
|
+
args = args+["--tag", f"{codeunitname_lower}:latest", "--tag", f"{codeunitname_lower}:{codeunitversion}", "--file", f"{codeunitname}/Dockerfile"]
|
|
36
|
+
if not self.use_cache():
|
|
37
|
+
args.append("--no-cache")
|
|
38
|
+
args.append("--load")
|
|
39
|
+
args.append(".")
|
|
40
|
+
codeunit_content_folder = codeunit_folder
|
|
41
|
+
GeneralUtilities.retry_action(lambda a=args, f=codeunit_content_folder: self._protected_sc.run_program_argsasarray("docker", a, f, print_errors_as_information=True), 3)
|
|
42
|
+
|
|
43
|
+
self._protected_sc.run_program_argsasarray("docker", ["save", "--output", f"{codeunitname}_v{codeunitversion}_{GeneralUtilities.platform_to_dash_str(platform)}.tar", f"{codeunitname_lower}:{codeunitversion}"], app_artifacts_folder, print_errors_as_information=True)
|
|
41
44
|
self.__generate_sbom_for_docker_image()
|
|
45
|
+
self.copy_source_files_to_output_directory()
|
|
42
46
|
|
|
43
47
|
|
|
44
48
|
@GeneralUtilities.check_arguments
|
|
@@ -93,10 +97,22 @@ class TFCPS_CodeUnitSpecific_Docker_Functions(TFCPS_CodeUnitSpecific_Base):
|
|
|
93
97
|
timeout=timedelta(seconds=120)
|
|
94
98
|
if environment_variables is None:
|
|
95
99
|
environment_variables={}
|
|
100
|
+
current_platform = GeneralUtilities.get_current_platform()
|
|
101
|
+
platform_for_test:Platform=None
|
|
102
|
+
if current_platform == Platform.Windows_AMD64:
|
|
103
|
+
platform_for_test=Platform.Linux_AMD64
|
|
104
|
+
elif current_platform == Platform.Linux_AMD64:
|
|
105
|
+
platform_for_test=Platform.Linux_AMD64
|
|
106
|
+
elif current_platform == Platform.Linux_ARM64:
|
|
107
|
+
platform_for_test=Platform.Linux_ARM64
|
|
108
|
+
elif current_platform == Platform.MacOS_ARM64:
|
|
109
|
+
platform_for_test=Platform.Linux_ARM64
|
|
110
|
+
else:
|
|
111
|
+
raise ValueError(f"Current platform {current_platform} is not supported for testing.")
|
|
96
112
|
oci_image_artifacts_folder :str= GeneralUtilities.resolve_relative_path("Other/Artifacts/BuildResult_OCIImage", self.get_codeunit_folder())
|
|
97
113
|
container_name:str=f"{self.get_codeunit_name()}finaltest".lower()
|
|
98
114
|
self.tfcps_Tools_General.ensure_containers_are_not_running([container_name])
|
|
99
|
-
self.tfcps_Tools_General.load_docker_image(oci_image_artifacts_folder)
|
|
115
|
+
self.tfcps_Tools_General.load_docker_image(oci_image_artifacts_folder,platform_for_test)
|
|
100
116
|
codeunit_file:str=os.path.join(self.get_codeunit_folder(),f"{self.get_codeunit_name()}.codeunit.xml")
|
|
101
117
|
image=f"{self.get_codeunit_name()}:{self.tfcps_Tools_General.get_version_of_codeunit(codeunit_file)}".lower()
|
|
102
118
|
argument=f"run -d --name {container_name}"
|
|
@@ -23,8 +23,7 @@ class TFCPS_CodeUnitSpecific_Python_Functions(TFCPS_CodeUnitSpecific_Base):
|
|
|
23
23
|
def generate_bom_for_python_project(self) -> None:
|
|
24
24
|
codeunit_folder: str=self.get_codeunit_folder()
|
|
25
25
|
codeunitname: str=self.get_codeunit_name()
|
|
26
|
-
repository_folder = os.path.dirname(codeunit_folder)
|
|
27
|
-
|
|
26
|
+
repository_folder = os.path.dirname(codeunit_folder)
|
|
28
27
|
codeunitversion = self.tfcps_Tools_General.get_version_of_codeunit(self.get_codeunit_file())
|
|
29
28
|
bom_folder = "Other/Artifacts/BOM"
|
|
30
29
|
bom_folder_full = os.path.join(codeunit_folder, bom_folder)
|
|
@@ -40,7 +39,7 @@ class TFCPS_CodeUnitSpecific_Python_Functions(TFCPS_CodeUnitSpecific_Base):
|
|
|
40
39
|
|
|
41
40
|
GeneralUtilities.ensure_file_exists(bom_file_json)
|
|
42
41
|
GeneralUtilities.write_text_to_file(bom_file_json, result[1])
|
|
43
|
-
cyclonedx_exe=self.tfcps_Tools_General.ensure_cyclonedxcli_is_available(
|
|
42
|
+
cyclonedx_exe=self.tfcps_Tools_General.ensure_cyclonedxcli_is_available(not self.use_cache())
|
|
44
43
|
self._protected_sc.run_program(cyclonedx_exe, f"convert --input-file ./{codeunitname}/{bom_file_relative_json} --input-format json --output-file ./{codeunitname}/{bom_file_relative_xml} --output-format xml", repository_folder)
|
|
45
44
|
self._protected_sc.format_xml_file(bom_file_xml)
|
|
46
45
|
GeneralUtilities.ensure_file_does_not_exist(bom_file_json)
|
|
@@ -16,7 +16,7 @@ import urllib.request
|
|
|
16
16
|
from packaging import version
|
|
17
17
|
import requests
|
|
18
18
|
from lxml import etree
|
|
19
|
-
from ..GeneralUtilities import GeneralUtilities
|
|
19
|
+
from ..GeneralUtilities import GeneralUtilities,Platform
|
|
20
20
|
from ..ScriptCollectionCore import ScriptCollectionCore,VSCodeWorkspaceShellTask
|
|
21
21
|
from ..SCLog import LogLevel
|
|
22
22
|
from ..OCIImages.AbstractImageHandler import AbstractImageHandler
|
|
@@ -38,7 +38,7 @@ class TFCPS_Tools_General:
|
|
|
38
38
|
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]))
|
|
39
39
|
|
|
40
40
|
@GeneralUtilities.check_arguments
|
|
41
|
-
def ensure_cyclonedxcli_is_available(self,
|
|
41
|
+
def ensure_cyclonedxcli_is_available(self,enforce_update:bool) -> str:
|
|
42
42
|
local_filename = "cyclonedx-cli"
|
|
43
43
|
filename_on_github: str
|
|
44
44
|
if GeneralUtilities.current_system_is_windows():
|
|
@@ -46,20 +46,20 @@ class TFCPS_Tools_General:
|
|
|
46
46
|
local_filename = local_filename+".exe"
|
|
47
47
|
else:
|
|
48
48
|
filename_on_github = "cyclonedx-linux-x64"
|
|
49
|
-
return self.ensure_file_from_github_assets_is_available_with_retry(
|
|
49
|
+
return self.ensure_file_from_github_assets_is_available_with_retry("CycloneDX", "cyclonedx-cli", "CycloneDXCLI",local_filename,lambda latest_version: filename_on_github,enforce_update=enforce_update)
|
|
50
50
|
|
|
51
51
|
@GeneralUtilities.check_arguments
|
|
52
|
-
def ensure_file_from_github_assets_is_available_with_retry(self,
|
|
53
|
-
return GeneralUtilities.retry_action(lambda: self.ensure_file_from_github_assets_is_available(
|
|
52
|
+
def ensure_file_from_github_assets_is_available_with_retry(self, githubuser: str, githubprojectname: str, local_resource_name: str, local_filename: str, get_filename_on_github, amount_of_attempts: int = 5,enforce_update:bool=False) -> str:
|
|
53
|
+
return GeneralUtilities.retry_action(lambda: self.ensure_file_from_github_assets_is_available(githubuser, githubprojectname, local_resource_name, local_filename, get_filename_on_github,enforce_update), amount_of_attempts)
|
|
54
54
|
|
|
55
55
|
@GeneralUtilities.check_arguments
|
|
56
|
-
def ensure_file_from_github_assets_is_available(self,
|
|
56
|
+
def ensure_file_from_github_assets_is_available(self,githubuser: str, githubprojectname: str, local_resource_name: str, local_filename: str, get_filename_on_github,enforce_update:bool) -> str:
|
|
57
57
|
#TODO use or remove target_folder-parameter
|
|
58
|
-
resource_folder =os.path.join( self.__sc.get_global_cache_folder(),"Tools",
|
|
58
|
+
resource_folder =os.path.join( self.__sc.get_global_cache_folder(),"Tools",local_resource_name)
|
|
59
59
|
file = f"{resource_folder}/{local_filename}"
|
|
60
60
|
file_exists = os.path.isfile(file)
|
|
61
61
|
if not file_exists:
|
|
62
|
-
self.__sc.log.log(f"Download Asset \"{githubuser}/{githubprojectname}: {
|
|
62
|
+
self.__sc.log.log(f"Download Asset \"{githubuser}/{githubprojectname}: {local_resource_name}\" from GitHub to global cache...", LogLevel.Information)
|
|
63
63
|
GeneralUtilities.ensure_folder_exists_and_is_empty(resource_folder)
|
|
64
64
|
headers = { 'User-Agent': 'Mozilla/5.0'}
|
|
65
65
|
self.__add_github_api_key_if_available(headers)
|
|
@@ -459,7 +459,7 @@ class TFCPS_Tools_General:
|
|
|
459
459
|
target_original_sbom_file_relative = os.path.dirname(target_sbom_file_relative)+"/"+os.path.basename(target_sbom_file_relative)+".original.xml"
|
|
460
460
|
os.rename(os.path.join(repository_folder, target_sbom_file_relative), os.path.join(repository_folder, target_original_sbom_file_relative))
|
|
461
461
|
|
|
462
|
-
cyclonedx_exe:str=self.ensure_cyclonedxcli_is_available(
|
|
462
|
+
cyclonedx_exe:str=self.ensure_cyclonedxcli_is_available(not use_cache)
|
|
463
463
|
self.__sc.run_program(cyclonedx_exe, f"merge --input-files {source_sbom_file_relative} {target_original_sbom_file_relative} --output-file {target_sbom_file_relative}", repository_folder)
|
|
464
464
|
GeneralUtilities.ensure_file_does_not_exist(os.path.join(repository_folder, target_original_sbom_file_relative))
|
|
465
465
|
self.__sc.format_xml_file(os.path.join(repository_folder, target_sbom_file_relative))
|
|
@@ -496,21 +496,20 @@ class TFCPS_Tools_General:
|
|
|
496
496
|
def generate_svg_files_from_plantuml_files_for_repository(self, repository_folder: str,use_cache:bool) -> None:
|
|
497
497
|
self.__sc.log.log("Generate svg-files from plantuml-files...")
|
|
498
498
|
self.__sc.assert_is_git_repository(repository_folder)
|
|
499
|
-
plantuml_jar_file=self.ensure_plantuml_is_available(
|
|
499
|
+
plantuml_jar_file=self.ensure_plantuml_is_available(not use_cache)
|
|
500
500
|
target_folder = os.path.join(repository_folder, "Other", "Reference")
|
|
501
501
|
self.__generate_svg_files_from_plantuml(target_folder, plantuml_jar_file)
|
|
502
502
|
|
|
503
503
|
@GeneralUtilities.check_arguments
|
|
504
504
|
def generate_svg_files_from_plantuml_files_for_codeunit(self, codeunit_folder: str,use_cache:bool) -> None:
|
|
505
505
|
self.assert_is_codeunit_folder(codeunit_folder)
|
|
506
|
-
|
|
507
|
-
plantuml_jar_file=self.ensure_plantuml_is_available(repository_folder,not use_cache)
|
|
506
|
+
plantuml_jar_file=self.ensure_plantuml_is_available(not use_cache)
|
|
508
507
|
target_folder = os.path.join(codeunit_folder, "Other", "Reference")
|
|
509
508
|
self.__generate_svg_files_from_plantuml(target_folder, plantuml_jar_file)
|
|
510
509
|
|
|
511
510
|
@GeneralUtilities.check_arguments
|
|
512
|
-
def ensure_plantuml_is_available(self,
|
|
513
|
-
return self.ensure_file_from_github_assets_is_available_with_retry(
|
|
511
|
+
def ensure_plantuml_is_available(self,enforce_update:bool) -> str:
|
|
512
|
+
return self.ensure_file_from_github_assets_is_available_with_retry("plantuml", "plantuml", "PlantUML", "plantuml.jar", lambda latest_version: "plantuml.jar",enforce_update=enforce_update)
|
|
514
513
|
|
|
515
514
|
@GeneralUtilities.check_arguments
|
|
516
515
|
def __generate_svg_files_from_plantuml(self, diagrams_files_folder: str, plantuml_jar_file: str) -> None:
|
|
@@ -578,7 +577,7 @@ class TFCPS_Tools_General:
|
|
|
578
577
|
target_folder_extracted = os.path.join(self.__sc.get_global_cache_folder(),"Tools",resource_name)
|
|
579
578
|
update:bool=not os.path.isdir(target_folder_extracted) or GeneralUtilities.folder_is_empty(target_folder_extracted) or enforce_update
|
|
580
579
|
if update:
|
|
581
|
-
downloaded_file=self.ensure_file_from_github_assets_is_available_with_retry(
|
|
580
|
+
downloaded_file=self.ensure_file_from_github_assets_is_available_with_retry( "trufflesecurity", "trufflehog", resource_name+"_Unextracted", zip_filename, lambda latest_version: f"trufflehog_{latest_version[1:]}_{osname_in_github_asset}_amd64.tar.gz",enforce_update=enforce_update)
|
|
582
581
|
#TODO add option to also download arm-version
|
|
583
582
|
local_zip_file: str = downloaded_file
|
|
584
583
|
GeneralUtilities.ensure_folder_exists_and_is_empty(target_folder_extracted)
|
|
@@ -639,33 +638,50 @@ class TFCPS_Tools_General:
|
|
|
639
638
|
|
|
640
639
|
@GeneralUtilities.check_arguments
|
|
641
640
|
def ensure_androidappbundletool_is_available(self, target_folder: str,enforce_update:bool) -> str:
|
|
642
|
-
return self.ensure_file_from_github_assets_is_available_with_retry(
|
|
641
|
+
return self.ensure_file_from_github_assets_is_available_with_retry( "google", "bundletool", "AndroidAppBundleTool", "bundletool.jar", lambda latest_version: f"bundletool-all-{latest_version}.jar",enforce_update=enforce_update)
|
|
643
642
|
|
|
644
643
|
@GeneralUtilities.check_arguments
|
|
645
644
|
def ensure_mediamtx_is_available(self, target_folder: str,enforce_update:bool) -> None:
|
|
646
|
-
def download_and_extract(osname: str, osname_in_github_asset: str, extension: str):
|
|
647
|
-
resource_name: str = f"MediaMTX_{
|
|
648
|
-
zip_filename: str = f"{resource_name}.{extension}"
|
|
645
|
+
def download_and_extract(osname: str, osname_in_github_asset: str, extension: str,architecture:Platform):
|
|
646
|
+
resource_name: str = f"MediaMTX_{GeneralUtilities.platform_to_dash_str(architecture)}"
|
|
649
647
|
resource_folder: str = os.path.join(target_folder, "Other", "Resources", resource_name)
|
|
650
648
|
target_folder_extracted = os.path.join(resource_folder, "MediaMTX")
|
|
651
649
|
update:bool=not os.path.isdir(target_folder_extracted) or GeneralUtilities.folder_is_empty(target_folder_extracted) or enforce_update
|
|
652
650
|
if update:
|
|
653
|
-
|
|
654
|
-
|
|
651
|
+
platform_str:str=None
|
|
652
|
+
match architecture:
|
|
653
|
+
case Platform.Windows_AMD64:
|
|
654
|
+
platform_str = "windows_amd64"
|
|
655
|
+
case Platform.Linux_ARM64:
|
|
656
|
+
platform_str = "linux_arm64"
|
|
657
|
+
case Platform.Linux_AMD64:
|
|
658
|
+
platform_str = "linux_amd64"
|
|
659
|
+
case Platform.MacOS_ARM64:
|
|
660
|
+
platform_str = "darwin_arm64"
|
|
661
|
+
case _:
|
|
662
|
+
raise ValueError(f"Unknown platform: {str(architecture)}")
|
|
663
|
+
|
|
664
|
+
resource_filename_name_remote:str=f"mediamtx_{platform_str}.{extension}"
|
|
665
|
+
resource_name_local:str=f"MediaMTCX_{platform_str}"
|
|
666
|
+
global_cache_file=os.path.join( self.__sc.get_global_cache_folder(),"Tools",resource_name_local,resource_filename_name_remote)
|
|
667
|
+
if (not os.path.isfile( global_cache_file )) or enforce_update:
|
|
668
|
+
self.ensure_file_from_github_assets_is_available_with_retry( "bluenviron", "mediamtx", resource_name_local, resource_filename_name_remote, lambda latest_version: f"mediamtx_{latest_version}_{platform_str}.{extension}",enforce_update=enforce_update)
|
|
669
|
+
GeneralUtilities.assert_file_exists(global_cache_file)
|
|
670
|
+
GeneralUtilities.assert_file_exists(global_cache_file)
|
|
655
671
|
GeneralUtilities.ensure_folder_exists_and_is_empty(target_folder_extracted)
|
|
656
672
|
if extension == "zip":
|
|
657
|
-
with zipfile.ZipFile(
|
|
673
|
+
with zipfile.ZipFile(global_cache_file, 'r') as zip_ref:
|
|
658
674
|
zip_ref.extractall(target_folder_extracted)
|
|
659
675
|
elif extension == "tar.gz":
|
|
660
|
-
with tarfile.open(
|
|
676
|
+
with tarfile.open(global_cache_file, "r:gz") as tar:
|
|
661
677
|
tar.extractall(path=target_folder_extracted)
|
|
662
678
|
else:
|
|
663
679
|
raise ValueError(f"Unknown extension: \"{extension}\"")
|
|
664
|
-
GeneralUtilities.ensure_file_does_not_exist(local_zip_file)
|
|
665
680
|
|
|
666
|
-
download_and_extract("Windows", "windows", "zip")
|
|
667
|
-
download_and_extract("Linux", "linux", "tar.gz")
|
|
668
|
-
download_and_extract("
|
|
681
|
+
download_and_extract("Windows", "windows", "zip",Platform.Windows_AMD64)
|
|
682
|
+
download_and_extract("Linux", "linux", "tar.gz",Platform.Linux_AMD64)
|
|
683
|
+
download_and_extract("Linux", "linux", "tar.gz",Platform.Linux_ARM64)
|
|
684
|
+
download_and_extract("MacOS", "darwin", "tar.gz",Platform.MacOS_ARM64)
|
|
669
685
|
|
|
670
686
|
@GeneralUtilities.check_arguments
|
|
671
687
|
def clone_repository_as_resource(self, local_repository_folder: str, remote_repository_link: str, resource_name: str, repository_subname: str = None,use_cache:bool=True) -> None:
|
|
@@ -1144,7 +1160,6 @@ class TFCPS_Tools_General:
|
|
|
1144
1160
|
if write_to_file:
|
|
1145
1161
|
GeneralUtilities.write_text_to_file(version_file, latest_version_function)
|
|
1146
1162
|
|
|
1147
|
-
|
|
1148
1163
|
@GeneralUtilities.check_arguments
|
|
1149
1164
|
def push_docker_build_artifact(self, push_artifacts_file: str, registry: str, push_readme: bool, repository_folder_name: str, remote_image_name: str = None) -> None:
|
|
1150
1165
|
folder_of_this_file = os.path.dirname(push_artifacts_file)
|
|
@@ -1154,33 +1169,87 @@ class TFCPS_Tools_General:
|
|
|
1154
1169
|
if match := re.search(filename_regex, filename, re.IGNORECASE):
|
|
1155
1170
|
codeunitname = match.group(1)
|
|
1156
1171
|
else:
|
|
1157
|
-
raise ValueError(f"Expected push-artifacts-file to match the regex \"{filename_regex}\" where \"{codeunitname_regex}\" represents the codeunit-name.")
|
|
1158
|
-
|
|
1172
|
+
raise ValueError(f"Expected push-artifacts-file to match the regex \"{filename_regex}\" where \"{codeunitname_regex}\" represents the codeunit-name.")
|
|
1159
1173
|
repository_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}..{os.path.sep}Submodules{os.path.sep}{repository_folder_name}", folder_of_this_file)
|
|
1160
1174
|
codeunit_folder = os.path.join(repository_folder, codeunitname)
|
|
1161
1175
|
artifacts_folder = os.path.join(repository_folder,codeunitname, "Other", "Artifacts")
|
|
1162
1176
|
applicationimage_folder = os.path.join(artifacts_folder, "BuildResult_OCIImage")
|
|
1163
|
-
image_file = self.__sc.find_file_by_extension(applicationimage_folder, "tar")
|
|
1164
|
-
image_filename = os.path.basename(image_file)
|
|
1165
1177
|
codeunit_version = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml"))
|
|
1166
1178
|
if remote_image_name is None:
|
|
1167
1179
|
remote_image_name = codeunitname
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1180
|
+
tar_files=[f for f in GeneralUtilities.get_direct_files_of_folder(applicationimage_folder) if f.endswith(".tar")]
|
|
1181
|
+
target_image_address=f"{registry}/{remote_image_name}"
|
|
1182
|
+
tar_files_with_platforms: list[tuple[str, str, str]] = []
|
|
1183
|
+
for tar_file in tar_files:
|
|
1184
|
+
filename=os.path.basename(tar_file)#filename looks like "{codeunitname}_v{codeunitversion}_{GeneralUtilities.platform_to_dash_str(platform)}.tar"
|
|
1185
|
+
platform:Platform=self.platform_from_filename(filename)#GeneralUtilities.platform_from_dash_str( filename.split("_")[-1].split(".")[0])
|
|
1186
|
+
platform_os_in_docker_format :str = None
|
|
1187
|
+
platform_arch_in_docker_format :str = None
|
|
1188
|
+
if platform==Platform.Windows_AMD64:
|
|
1189
|
+
raise NotImplementedError("Building docker images for Windows is not implemented yet.")
|
|
1190
|
+
elif platform==Platform.Linux_AMD64:
|
|
1191
|
+
platform_os_in_docker_format = "linux"
|
|
1192
|
+
platform_arch_in_docker_format = "amd64"
|
|
1193
|
+
elif platform==Platform.Linux_ARM64:
|
|
1194
|
+
platform_os_in_docker_format = "linux"
|
|
1195
|
+
platform_arch_in_docker_format = "arm64"
|
|
1196
|
+
elif platform==Platform.MacOS_ARM64:
|
|
1197
|
+
raise NotImplementedError("Building docker images for MacOS is not implemented yet.")
|
|
1198
|
+
else:
|
|
1199
|
+
raise ValueError(f"Unsupported platform {platform} extracted from filename {filename}.")
|
|
1200
|
+
tar_files_with_platforms.append((tar_file, platform_os_in_docker_format, platform_arch_in_docker_format))
|
|
1201
|
+
self.push_docker_build_artifact_as_multi_arch_artifact(tar_files_with_platforms,target_image_address, "v"+codeunit_version)
|
|
1202
|
+
self.push_docker_build_artifact_as_multi_arch_artifact(tar_files_with_platforms,target_image_address, "latest")
|
|
1181
1203
|
if push_readme:
|
|
1182
|
-
self.__sc.run_program_with_retry("docker-pushrm",
|
|
1183
|
-
|
|
1204
|
+
self.__sc.run_program_with_retry("docker-pushrm", target_image_address, codeunit_folder)
|
|
1205
|
+
|
|
1206
|
+
def push_docker_build_artifact_as_multi_arch_artifact(self,tar_files: list[tuple[str, str, str]], image_address: str, tag: str):
|
|
1207
|
+
"""
|
|
1208
|
+
tar_files: list of (tar_path, os, arch) tuples
|
|
1209
|
+
for example [
|
|
1210
|
+
("MyApp.Linux.arm64.tar", "linux", "arm64"),
|
|
1211
|
+
("MyApp.Linux.amd64.tar", "linux", "amd64")
|
|
1212
|
+
]
|
|
1213
|
+
image_address for example: "myregistry.example.com/myapp"
|
|
1214
|
+
tag for example: "1.0.0"
|
|
1215
|
+
"""
|
|
1216
|
+
arch_tags = []
|
|
1217
|
+
|
|
1218
|
+
for tar_path, os_name, arch in tar_files:
|
|
1219
|
+
arch_tag = f"{image_address}:{tag}-{os_name}-{arch}"
|
|
1220
|
+
arch_tags.append(arch_tag)
|
|
1221
|
+
|
|
1222
|
+
# Load tar → local image
|
|
1223
|
+
print(f"Loading {tar_path}...")
|
|
1224
|
+
result = self.__sc.run_program_argsasarray("docker",[ "load", "-i", tar_path], capture_output=True)
|
|
1225
|
+
# docker load outputs: "Loaded image: sha256:abc123..." or "Loaded image ID: ..."
|
|
1226
|
+
# we need the loaded image ID
|
|
1227
|
+
loaded_id = None
|
|
1228
|
+
for line in GeneralUtilities.string_to_lines(result[1]):
|
|
1229
|
+
if "Loaded image" in line:
|
|
1230
|
+
loaded_id = line.split(":", 1)[1].strip()
|
|
1231
|
+
break
|
|
1232
|
+
|
|
1233
|
+
if not loaded_id:
|
|
1234
|
+
raise RuntimeError(f"Could not determine loaded image from output: \"{result[1]}\"")
|
|
1235
|
+
|
|
1236
|
+
# Retag + push
|
|
1237
|
+
self.__sc.run_program_argsasarray("docker",[ "tag", loaded_id, arch_tag])
|
|
1238
|
+
self.__sc.run_program_argsasarray("docker",[ "push", arch_tag])
|
|
1239
|
+
|
|
1240
|
+
# Create multi-arch manifest
|
|
1241
|
+
final_tag = f"{image_address}:{tag}"
|
|
1242
|
+
self.__sc.run_program_argsasarray("docker", [ "buildx", "imagetools", "create", "--tag", final_tag] + arch_tags)
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
@GeneralUtilities.check_arguments
|
|
1246
|
+
def platform_from_filename(self,filename: str) -> Platform:
|
|
1247
|
+
match = re.search(r'_([^_]+)\.tar', filename)
|
|
1248
|
+
if match:
|
|
1249
|
+
return GeneralUtilities.platform_from_dash_str(match.group(1))
|
|
1250
|
+
else:
|
|
1251
|
+
raise ValueError(f"Cannot extract platform from filename: \"{filename}\"")
|
|
1252
|
+
|
|
1184
1253
|
def prepare_building_codeunits(self,repository_folder:str,use_cache:bool,generate_development_certificate:bool):
|
|
1185
1254
|
if generate_development_certificate:
|
|
1186
1255
|
self.ensure_certificate_authority_for_development_purposes_is_generated(repository_folder)
|
|
@@ -1207,10 +1276,14 @@ class TFCPS_Tools_General:
|
|
|
1207
1276
|
self.__sc.run_program("docker", f"container rm -f {container_name}", throw_exception_if_exitcode_is_not_zero=False)
|
|
1208
1277
|
|
|
1209
1278
|
@GeneralUtilities.check_arguments
|
|
1210
|
-
def load_docker_image(self, oci_image_artifacts_folder:str) -> None:
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1279
|
+
def load_docker_image(self, oci_image_artifacts_folder:str,platform:Platform) -> None:
|
|
1280
|
+
for file in GeneralUtilities.get_direct_files_of_folder(oci_image_artifacts_folder):
|
|
1281
|
+
if file.endswith(f"_{GeneralUtilities.platform_to_dash_str(platform)}.tar"):
|
|
1282
|
+
image_filename = file
|
|
1283
|
+
self.__sc.log.log("Load docker-image...")
|
|
1284
|
+
self.__sc.run_program("docker", f"load -i {image_filename}", oci_image_artifacts_folder)
|
|
1285
|
+
return
|
|
1286
|
+
raise ValueError(f"No docker-image found for platform {GeneralUtilities.platform_to_dash_str(platform)} in folder {oci_image_artifacts_folder}.")
|
|
1214
1287
|
|
|
1215
1288
|
@GeneralUtilities.check_arguments
|
|
1216
1289
|
def start_dockerfile_example(self, current_file: str,remove_old_container: bool, remove_volumes_folder: bool, env_file: str) -> None:
|
|
@@ -1225,7 +1298,7 @@ class TFCPS_Tools_General:
|
|
|
1225
1298
|
self.__sc.log.log(f"Ensure container of {docker_compose_file} do not exist...")
|
|
1226
1299
|
oci_image_artifacts_folder = GeneralUtilities.resolve_relative_path("../../../../Artifacts/BuildResult_OCIImage", folder_of_current_file)
|
|
1227
1300
|
self.ensure_containers_are_not_running(container_names_to_remove)
|
|
1228
|
-
self.load_docker_image(oci_image_artifacts_folder)
|
|
1301
|
+
self.load_docker_image(oci_image_artifacts_folder,GeneralUtilities.get_current_platform())
|
|
1229
1302
|
example_name = os.path.basename(folder_of_current_file)
|
|
1230
1303
|
codeunit_name = os.path.basename(GeneralUtilities.resolve_relative_path("../../../../..", folder_of_current_file))
|
|
1231
1304
|
if remove_volumes_folder:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
ScriptCollection/AnionBuildPlatform.py,sha256=K-PHarX802A0PU8uRu0GNcEZiXujFoXHACe-X9YJsAQ,11711
|
|
2
2
|
ScriptCollection/CertificateUpdater.py,sha256=Pa6eyjQSx7IIvj4PQVMI0IwMs01KQrNSB7Qa-7lRfBs,9375
|
|
3
|
-
ScriptCollection/Executables.py,sha256=
|
|
4
|
-
ScriptCollection/GeneralUtilities.py,sha256=
|
|
3
|
+
ScriptCollection/Executables.py,sha256=qpo0g5peWdlK5uLIUCyLDB9c3JCk0ETbtmOJXZwuHh4,44510
|
|
4
|
+
ScriptCollection/GeneralUtilities.py,sha256=FEjJEfA3riVT7G4sL8bzKPb7w0SN8YjONNWMtiBo_j8,64882
|
|
5
5
|
ScriptCollection/HTTPMaintenanceOverheadHelper.py,sha256=TToNtyO1XzsMbBsTBf3o0xgOK0v4Jf03qw2Z0xb2nCk,2007
|
|
6
6
|
ScriptCollection/ProcessesRunner.py,sha256=o5raxIt3lknNPoPrjNzJ2bprRPJ3SnL0rrR7crraD7E,1523
|
|
7
7
|
ScriptCollection/ProgramRunnerBase.py,sha256=4A2eQgSg_rRgQcgSi-LYtUlM-uSQEpS7qFWn0tWt4uo,2171
|
|
@@ -9,7 +9,7 @@ ScriptCollection/ProgramRunnerMock.py,sha256=uTu-aFle1W_oKjeQEmuPsFPQpvo0kRf2FrR
|
|
|
9
9
|
ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
|
|
10
10
|
ScriptCollection/ProgramRunnerSudo.py,sha256=_khC3xuTdrPoLluBJZWfldltmmuKltABJPcbjZSFW-4,4835
|
|
11
11
|
ScriptCollection/SCLog.py,sha256=8TRy1LeYMsPOIuWUcnUNNbO5pd-cNBS-3cn-kdzP8FU,4768
|
|
12
|
-
ScriptCollection/ScriptCollectionCore.py,sha256=
|
|
12
|
+
ScriptCollection/ScriptCollectionCore.py,sha256=k9Jxim56NM5dr6wAZrITk-bxZhKKxeXi1gb1zkfcDUo,176930
|
|
13
13
|
ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
14
|
ScriptCollection/OCIImages/AbstractImageHandler.py,sha256=83qDMILwxhH9DbC0sb358Vu8PXEysmJJyap_6gECZqs,1627
|
|
15
15
|
ScriptCollection/OCIImages/OCIImageManager.py,sha256=aBogkSXNDyi8NO11N-s03nuFJEv7PyJ-wjHuYYeZfvs,6662
|
|
@@ -30,9 +30,9 @@ ScriptCollection/TFCPS/TFCPS_Generic.py,sha256=O-0guM_LJCcZmPZJhMgTvXD2RXUJEBWWv
|
|
|
30
30
|
ScriptCollection/TFCPS/TFCPS_MergeToMain.py,sha256=-Ev9D3bZDlUk2WFQhcmvzQ3FCS97OdsVUd0koAdmpZc,7474
|
|
31
31
|
ScriptCollection/TFCPS/TFCPS_MergeToStable.py,sha256=Ajfy2pLajTuU6UpwItHt4C2a-gLF3gPc4z6BktL3Cio,22163
|
|
32
32
|
ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py,sha256=f0Uq1cA_4LvmL72cal0crrbKF6PcxL13D9wBKuQ1YBw,2328
|
|
33
|
-
ScriptCollection/TFCPS/TFCPS_Tools_General.py,sha256=
|
|
33
|
+
ScriptCollection/TFCPS/TFCPS_Tools_General.py,sha256=RCwtEwuZUds-gyHa2qCitWZUgapM3-IZJKF9e5Zwxck,98000
|
|
34
34
|
ScriptCollection/TFCPS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py,sha256=
|
|
35
|
+
ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py,sha256=xMuj-GoEwDnmUWbmdF536dNQL7jnTaw9aQ-gcDqUkgQ,11743
|
|
36
36
|
ScriptCollection/TFCPS/Docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
37
|
ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py,sha256=bT6Gd5pQpZCw4OQz6HWkPCSn5z__eUUEisABLDSxd0o,200
|
|
38
38
|
ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py,sha256=QyjOfMY22JWCvKjMelHiDWbJiWqotOfebpJpgDUaoO4,237
|
|
@@ -45,10 +45,10 @@ ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py,sha256=kyx26AnT1-LySFA46w
|
|
|
45
45
|
ScriptCollection/TFCPS/Go/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py,sha256=kL37qJNwH6E-CAJqcHbdnc0K0uGjHQ8XD1RXZOdcw1M,12850
|
|
47
47
|
ScriptCollection/TFCPS/NodeJS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py,sha256=
|
|
48
|
+
ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py,sha256=KFKDdfV9DFHE5n7TI6m1Ra1Qw2JwL_JQjneBfqcRQ_w,13467
|
|
49
49
|
ScriptCollection/TFCPS/Python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
-
scriptcollection-4.2.
|
|
51
|
-
scriptcollection-4.2.
|
|
52
|
-
scriptcollection-4.2.
|
|
53
|
-
scriptcollection-4.2.
|
|
54
|
-
scriptcollection-4.2.
|
|
50
|
+
scriptcollection-4.2.60.dist-info/METADATA,sha256=dQYQcvTQgiDemUjezOsxBCyPOkzYt6DkBe7HSGQUxf4,7690
|
|
51
|
+
scriptcollection-4.2.60.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
52
|
+
scriptcollection-4.2.60.dist-info/entry_points.txt,sha256=27XwAJEcaMEc1be0Ec1vKHCbiU4Ziu8jKL-SqsrYOIQ,4680
|
|
53
|
+
scriptcollection-4.2.60.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
|
|
54
|
+
scriptcollection-4.2.60.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|