ScriptCollection 3.5.152__py3-none-any.whl → 3.5.154__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.
@@ -715,11 +715,14 @@ def UpdateImagesInDockerComposeFile() -> int:
715
715
  iu: ImageUpdater = ImageUpdater()
716
716
  parser = argparse.ArgumentParser()
717
717
  parser.add_argument('-f', '--file', required=False, default=None)
718
- # TODO add option to specify ignored services and versionecholon
718
+ parser.add_argument('-v', '--versionecholon', required=False, default=VersionEcholon.Newest.name, dest="Possible values are: " + ", ".join([e.name for e in VersionEcholon]))
719
+ parser.add_argument("-s", "--servicename", required=True, default=None)
720
+ parser.add_argument("-u", "--updatertype", required=True, default=None)
719
721
  args = parser.parse_args()
720
722
  if args.file is None:
721
723
  args.file = os.path.join(os.getcwd(), "docker-compose.yml")
722
- iu.update_all_services_in_docker_compose_file(args.file, VersionEcholon.LatestPatch, [])
724
+ versionecholonTyped = VersionEcholon[args.versionecholon]
725
+ iu.update_services_in_docker_compose_file(args.file, [args.servicename], versionecholonTyped, args.updatertype)
723
726
  return 0
724
727
 
725
728
 
@@ -748,3 +751,12 @@ def GenerateTaskfileFromWorkspacefile() -> int:
748
751
  t = TasksForCommonProjectStructure()
749
752
  t.generate_tasksfile_from_workspace_file(args.repositoryfolder)
750
753
  return 0
754
+
755
+
756
+ def UpdateTimestampInFile() -> int:
757
+ parser = argparse.ArgumentParser(description="Update the timestamp in a comment in a file")
758
+ parser.add_argument('-f', '--file', required=True)
759
+ args = parser.parse_args()
760
+ sc = ScriptCollectionCore()
761
+ sc.update_timestamp_in_file(args.file)
762
+ return 0
@@ -35,6 +35,7 @@ class VersionEcholon(Enum):
35
35
 
36
36
  class GeneralUtilities:
37
37
 
38
+ __datetime_format_with_offset: str = "%Y-%m-%d %H:%M:%S %z"
38
39
  __datetime_format: str = "%Y-%m-%dT%H:%M:%S"
39
40
  __date_format: str = "%Y-%m-%d"
40
41
 
@@ -133,6 +134,11 @@ class GeneralUtilities:
133
134
  value = datetime(year=value.year, month=value.month, day=value.day, hour=value.hour, minute=value.minute, second=value.second)
134
135
  return value.strftime(GeneralUtilities.__datetime_format) # returns "2022-10-06T19:26:01" for example
135
136
 
137
+ @staticmethod
138
+ @check_arguments
139
+ def datetime_to_string_with_timezone(value: datetime) -> str:
140
+ return value.strftime(GeneralUtilities.__datetime_format_with_offset) # returns "2025-08-21 15:30:00 +0200" for example
141
+
136
142
  @staticmethod
137
143
  @check_arguments
138
144
  def string_to_date(value: str) -> date:
@@ -352,13 +358,21 @@ class GeneralUtilities:
352
358
 
353
359
  @staticmethod
354
360
  @check_arguments
355
- def datetime_to_string_for_logfile_name(datetime_object: datetime) -> str:
356
- return datetime_object.strftime('%Y-%m-%d_%H-%M-%S')
361
+ def datetime_to_string_for_logfile_name(datetime_object: datetime, add_timezone_info_to_log: bool = True) -> str:
362
+ base_pattern: str = "%Y-%m-%d_%H-%M-%S"
363
+ if add_timezone_info_to_log:
364
+ return datetime_object.strftime(f'{base_pattern}_%z')
365
+ else:
366
+ return datetime_object.strftime(base_pattern)
357
367
 
358
368
  @staticmethod
359
369
  @check_arguments
360
- def datetime_to_string_for_logfile_entry(datetime_object: datetime) -> str:
361
- return datetime_object.strftime('%Y-%m-%d %H:%M:%S')
370
+ def datetime_to_string_for_logfile_entry(datetime_object: datetime, add_timezone_info_to_log: bool = True) -> str:
371
+ base_pattern: str = "%Y-%m-%d %H:%M:%S"
372
+ if add_timezone_info_to_log:
373
+ return datetime_object.strftime(f'{base_pattern} %z')
374
+ else:
375
+ return datetime_object.strftime(base_pattern)
362
376
 
363
377
  @staticmethod
364
378
  @check_arguments
@@ -132,6 +132,11 @@ class ConcreteImageUpdater:
132
132
  def get_version_from_tag(self, image: str, tag: str) -> Version:
133
133
  raise NotImplementedError
134
134
 
135
+ @abstractmethod
136
+ @GeneralUtilities.check_arguments
137
+ def get_name(self, image: str, tag: str) -> str:
138
+ raise NotImplementedError
139
+
135
140
 
136
141
  class ConcreteImageUpdaterForNginx(ConcreteImageUpdater):
137
142
 
@@ -154,6 +159,11 @@ class ConcreteImageUpdaterForNginx(ConcreteImageUpdater):
154
159
  def get_version_from_tag(self, image: str, tag: str) -> Version:
155
160
  return ve.parse(tag)
156
161
 
162
+ @abstractmethod
163
+ @GeneralUtilities.check_arguments
164
+ def get_name(self, image: str, tag: str) -> str:
165
+ return "Nginx"
166
+
157
167
 
158
168
  class ConcreteImageUpdaterForWordpress(ConcreteImageUpdater):
159
169
 
@@ -176,6 +186,11 @@ class ConcreteImageUpdaterForWordpress(ConcreteImageUpdater):
176
186
  def get_version_from_tag(self, image: str, tag: str) -> Version:
177
187
  return ve.parse(tag)
178
188
 
189
+ @abstractmethod
190
+ @GeneralUtilities.check_arguments
191
+ def get_name(self, image: str, tag: str) -> str:
192
+ return "Wordpress"
193
+
179
194
 
180
195
  class ConcreteImageUpdaterForGitLab(ConcreteImageUpdater):
181
196
 
@@ -195,6 +210,11 @@ class ConcreteImageUpdaterForGitLab(ConcreteImageUpdater):
195
210
  def get_version_from_tag(self, image: str, tag: str) -> Version:
196
211
  raise NotImplementedError
197
212
 
213
+ @abstractmethod
214
+ @GeneralUtilities.check_arguments
215
+ def get_name(self, image: str, tag: str) -> str:
216
+ return "GitLab"
217
+
198
218
 
199
219
  class ConcreteImageUpdaterForRegistry(ConcreteImageUpdater):
200
220
 
@@ -218,6 +238,11 @@ class ConcreteImageUpdaterForRegistry(ConcreteImageUpdater):
218
238
  def get_version_from_tag(self, image: str, tag: str) -> Version:
219
239
  return ve.parse(tag)
220
240
 
241
+ @abstractmethod
242
+ @GeneralUtilities.check_arguments
243
+ def get_name(self, image: str, tag: str) -> str:
244
+ return "Registry"
245
+
221
246
 
222
247
  class ConcreteImageUpdaterForPrometheus(ConcreteImageUpdater):
223
248
 
@@ -240,6 +265,11 @@ class ConcreteImageUpdaterForPrometheus(ConcreteImageUpdater):
240
265
  def get_version_from_tag(self, image: str, tag: str) -> Version:
241
266
  return ve.parse(tag[1:])
242
267
 
268
+ @abstractmethod
269
+ @GeneralUtilities.check_arguments
270
+ def get_name(self, image: str, tag: str) -> str:
271
+ return "Prometheus"
272
+
243
273
 
244
274
  class ConcreteImageUpdaterForPrometheusBlackboxExporter(ConcreteImageUpdater):
245
275
 
@@ -262,6 +292,11 @@ class ConcreteImageUpdaterForPrometheusBlackboxExporter(ConcreteImageUpdater):
262
292
  def get_version_from_tag(self, image: str, tag: str) -> Version:
263
293
  return ve.parse(tag[1:])
264
294
 
295
+ @abstractmethod
296
+ @GeneralUtilities.check_arguments
297
+ def get_name(self, image: str, tag: str) -> str:
298
+ return "PrometheusBlackboxExporter"
299
+
265
300
 
266
301
  class ConcreteImageUpdaterForPrometheusNginxExporter(ConcreteImageUpdater):
267
302
 
@@ -284,6 +319,11 @@ class ConcreteImageUpdaterForPrometheusNginxExporter(ConcreteImageUpdater):
284
319
  def get_version_from_tag(self, image: str, tag: str) -> Version:
285
320
  return ve.parse(tag[1:])
286
321
 
322
+ @abstractmethod
323
+ @GeneralUtilities.check_arguments
324
+ def get_name(self, image: str, tag: str) -> str:
325
+ return "NginxPrometheusExporter"
326
+
287
327
 
288
328
  class ConcreteImageUpdaterForPrometheusNodeExporter(ConcreteImageUpdater):
289
329
 
@@ -306,6 +346,11 @@ class ConcreteImageUpdaterForPrometheusNodeExporter(ConcreteImageUpdater):
306
346
  def get_version_from_tag(self, image: str, tag: str) -> Version:
307
347
  return ve.parse(tag[1:])
308
348
 
349
+ @abstractmethod
350
+ @GeneralUtilities.check_arguments
351
+ def get_name(self, image: str, tag: str) -> str:
352
+ return "PrometheusNodeExporter"
353
+
309
354
 
310
355
  class ConcreteImageUpdaterForKeycloak(ConcreteImageUpdater):
311
356
 
@@ -325,6 +370,11 @@ class ConcreteImageUpdaterForKeycloak(ConcreteImageUpdater):
325
370
  def get_version_from_tag(self, image: str, tag: str) -> Version:
326
371
  raise NotImplementedError
327
372
 
373
+ @abstractmethod
374
+ @GeneralUtilities.check_arguments
375
+ def get_name(self, image: str, tag: str) -> str:
376
+ return "KeyCloak"
377
+
328
378
 
329
379
  class ConcreteImageUpdaterForMariaDB(ConcreteImageUpdater):
330
380
 
@@ -347,6 +397,11 @@ class ConcreteImageUpdaterForMariaDB(ConcreteImageUpdater):
347
397
  def get_version_from_tag(self, image: str, tag: str) -> Version:
348
398
  return ve.parse(tag)
349
399
 
400
+ @abstractmethod
401
+ @GeneralUtilities.check_arguments
402
+ def get_name(self, image: str, tag: str) -> str:
403
+ return "MariaDB"
404
+
350
405
 
351
406
  class ConcreteImageUpdaterForPostgreSQL(ConcreteImageUpdater):
352
407
 
@@ -369,6 +424,11 @@ class ConcreteImageUpdaterForPostgreSQL(ConcreteImageUpdater):
369
424
  def get_version_from_tag(self, image: str, tag: str) -> Version:
370
425
  return ve.parse(tag+".0")
371
426
 
427
+ @abstractmethod
428
+ @GeneralUtilities.check_arguments
429
+ def get_name(self, image: str, tag: str) -> str:
430
+ return "PostgreSQL"
431
+
372
432
 
373
433
  class ConcreteImageUpdaterForAdminer(ConcreteImageUpdater):
374
434
 
@@ -391,6 +451,65 @@ class ConcreteImageUpdaterForAdminer(ConcreteImageUpdater):
391
451
  def get_version_from_tag(self, image: str, tag: str) -> Version:
392
452
  return ve.parse(tag)
393
453
 
454
+ @abstractmethod
455
+ @GeneralUtilities.check_arguments
456
+ def get_name(self, image: str, tag: str) -> str:
457
+ return "Adminer"
458
+
459
+
460
+ class ConcreteImageUpdaterForGeneric(ConcreteImageUpdater):
461
+
462
+ @GeneralUtilities.check_arguments
463
+ def version_to_tag(self, version: Version) -> str:
464
+ return f"{version.major}.{version.minor}.{version.micro}"
465
+
466
+ @GeneralUtilities.check_arguments
467
+ def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
468
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
469
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
470
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
471
+ return result
472
+
473
+ @GeneralUtilities.check_arguments
474
+ def get_supported_images(self) -> list[str]:
475
+ return [".*"]
476
+
477
+ @GeneralUtilities.check_arguments
478
+ def get_version_from_tag(self, image: str, tag: str) -> Version:
479
+ return ve.parse(tag)
480
+
481
+ @abstractmethod
482
+ @GeneralUtilities.check_arguments
483
+ def get_name(self, image: str, tag: str) -> str:
484
+ return "Generic"
485
+
486
+
487
+ class ConcreteImageUpdaterForGenericV(ConcreteImageUpdater):
488
+
489
+ @GeneralUtilities.check_arguments
490
+ def version_to_tag(self, version: Version) -> str:
491
+ return f"v{version.major}.{version.minor}.{version.micro}"
492
+
493
+ @GeneralUtilities.check_arguments
494
+ def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
495
+ versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^v\\d+\\.\\d+\\.\\d+$", 999)
496
+ newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
497
+ result = ImageUpdaterHelper.filter_considering_echolon(newer_versions, current_version, version_echolon)
498
+ return result
499
+
500
+ @GeneralUtilities.check_arguments
501
+ def get_supported_images(self) -> list[str]:
502
+ return [".*"]
503
+
504
+ @GeneralUtilities.check_arguments
505
+ def get_version_from_tag(self, image: str, tag: str) -> Version:
506
+ return ve.parse(tag[1:])
507
+
508
+ @abstractmethod
509
+ @GeneralUtilities.check_arguments
510
+ def get_name(self, image: str, tag: str) -> str:
511
+ return "GenericV"
512
+
394
513
 
395
514
  class ImageUpdater:
396
515
 
@@ -434,16 +553,16 @@ class ImageUpdater:
434
553
  return newer_version_available
435
554
 
436
555
  @GeneralUtilities.check_arguments
437
- def update_all_services_in_docker_compose_file(self, dockercompose_file: str, version_echolon: VersionEcholon, except_services: list[str] = []):
556
+ def update_all_services_in_docker_compose_file(self, dockercompose_file: str, version_echolon: VersionEcholon, except_services: list[str] = [], updatertype: str = None):
438
557
  all_services = self.get_services_from_docker_compose_file(dockercompose_file)
439
558
  services_to_update = [service for service in all_services if service not in except_services]
440
- self.update_services_in_docker_compose_file(dockercompose_file, services_to_update, version_echolon)
559
+ self.update_services_in_docker_compose_file(dockercompose_file, services_to_update, version_echolon, updatertype)
441
560
 
442
561
  @GeneralUtilities.check_arguments
443
- def update_services_in_docker_compose_file(self, dockercompose_file: str, service_names: list[str], version_echolon: VersionEcholon):
562
+ def update_services_in_docker_compose_file(self, dockercompose_file: str, service_names: list[str], version_echolon: VersionEcholon, updatertype: str = None):
444
563
  for service_name in service_names:
445
564
  if self.service_has_image_information(dockercompose_file, service_name):
446
- self.update_service_in_docker_compose_file(dockercompose_file, service_name, version_echolon)
565
+ self.update_service_in_docker_compose_file(dockercompose_file, service_name, version_echolon, updatertype)
447
566
 
448
567
  @GeneralUtilities.check_arguments
449
568
  def service_has_image_information(self, dockercompose_file: str, service_name: str) -> bool:
@@ -454,9 +573,9 @@ class ImageUpdater:
454
573
  return image is not None
455
574
 
456
575
  @GeneralUtilities.check_arguments
457
- def update_service_in_docker_compose_file(self, dockercompose_file: str, service_name: str, version_echolon: VersionEcholon):
576
+ def update_service_in_docker_compose_file(self, dockercompose_file: str, service_name: str, version_echolon: VersionEcholon, updatertype: str = None):
458
577
  imagename, existing_tag, existing_version = self.get_current_version_of_service_from_docker_compose_file(dockercompose_file, service_name) # pylint:disable=unused-variable
459
- result = self.get_latest_version_of_image(imagename, version_echolon, existing_version)
578
+ result = self.get_latest_version_of_image(imagename, version_echolon, existing_version, updatertype)
460
579
  newest_version = result[0]
461
580
  newest_tag = result[1]
462
581
  # TODO write info to console if there is a newwer version available if versionecoholon==latest would have been chosen
@@ -495,20 +614,35 @@ class ImageUpdater:
495
614
  raise ValueError(f"Service '{service_name}' in '{dockercompose_file}'")
496
615
 
497
616
  @GeneralUtilities.check_arguments
498
- def __get_updater_for_image(self, image: str) -> str:
617
+ def __get_updater_for_image(self, image: str) -> ConcreteImageUpdater:
499
618
  for updater in self.updater:
500
- if image in updater.get_supported_images():
501
- return updater
619
+ for supported_image_regex in updater.get_supported_images():
620
+ r = re.compile("^"+supported_image_regex+"$")
621
+ if r.match(supported_image_regex):
622
+ return updater
502
623
  raise ValueError(f"No updater available for image '{image}'")
503
624
 
625
+ @GeneralUtilities.check_arguments
626
+ def __get_updater_by_name(self, updater_name: str) -> ConcreteImageUpdater:
627
+ for updater in self.updater:
628
+ if updater.get_name() == updater_name:
629
+ return updater
630
+ raise ValueError(f"No updater available with name '{updater_name}'")
631
+
504
632
  @GeneralUtilities.check_arguments
505
633
  def get_docker_version_from_tag(self, image: str, tag: str) -> Version:
506
- updater: ImageUpdater = self.__get_updater_for_image(image)
634
+ updater: ConcreteImageUpdater = self.__get_updater_for_image(image)
507
635
  return updater.get_version_from_tag(image, tag)
508
636
 
509
637
  @GeneralUtilities.check_arguments
510
- def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> tuple[Version, str]:
511
- updater: ImageUpdater = self.__get_updater_for_image(image)
638
+ def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version, updatertype: str = None) -> tuple[Version, str]:
639
+
640
+ updater: ConcreteImageUpdater = None
641
+ if updatertype is None:
642
+ self.__get_updater_for_image(image)
643
+ else:
644
+ self.__get_updater_by_name(updatertype)
645
+
512
646
  newest_version: Version = updater.get_latest_version_of_image(image, version_echolon, current_version)
513
647
  newest_tag: str = updater.version_to_tag(newest_version)
514
648
  return (newest_version, newest_tag)
ScriptCollection/SCLog.py CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  from enum import Enum
3
- from datetime import datetime, timezone
3
+ from datetime import datetime
4
4
  from .GeneralUtilities import GeneralUtilities
5
5
 
6
6
 
@@ -22,7 +22,6 @@ class SCLog:
22
22
  add_overhead_to_console: bool
23
23
  add_overhead_to_logfile: bool
24
24
  print_as_color: bool
25
- zone_of_time: timezone = None
26
25
 
27
26
  def __init__(self, log_file: str = None, loglevel: LogLevel = None, print_as_color: bool = True):
28
27
  self.log_file = log_file
@@ -69,13 +68,9 @@ class SCLog:
69
68
  if loglevel == LogLevel.Diagnostic:
70
69
  part3 = f"Diagnostic: {message}"
71
70
 
72
- moment: datetime = None
73
- if self.zone_of_time is None:
74
- moment = datetime.now()
75
- else:
76
- moment = datetime.now(self.zone_of_time)
71
+ moment: datetime = datetime.now(datetime.now().astimezone().tzinfo)
77
72
 
78
- part1 = f"[{GeneralUtilities.datetime_to_string_for_logfile_entry(moment)}] ["
73
+ part1 = f"[{GeneralUtilities.datetime_to_string_for_logfile_entry(moment, True)}] ["
79
74
  if loglevel == LogLevel.Information:
80
75
  part2 = f"Information"
81
76
  elif loglevel == LogLevel.Error:
@@ -1,3 +1,4 @@
1
+ import traceback
1
2
  from datetime import timedelta, datetime
2
3
  import json
3
4
  import binascii
@@ -34,7 +35,7 @@ from .ProgramRunnerPopen import ProgramRunnerPopen
34
35
  from .ProgramRunnerEpew import ProgramRunnerEpew, CustomEpewArgument
35
36
  from .SCLog import SCLog, LogLevel
36
37
 
37
- version = "3.5.152"
38
+ version = "3.5.154"
38
39
  __version__ = version
39
40
 
40
41
 
@@ -2355,8 +2356,10 @@ TXDX
2355
2356
  GeneralUtilities.write_text_to_file(file, ET.tostring(element, encoding="unicode"), encoding)
2356
2357
 
2357
2358
  @GeneralUtilities.check_arguments
2358
- def install_requirementstxt_file(self, requirements_txt_file: str, folder: str, verbosity: int):
2359
- self.run_program_argsasarray("pip", ["install", "-r", requirements_txt_file], folder, verbosity=verbosity)
2359
+ def install_requirementstxt_file(self, requirements_txt_file: str, verbosity: int):
2360
+ folder:str=os.path.dirname(requirements_txt_file)
2361
+ filename:str=os.path.basename(requirements_txt_file)
2362
+ self.run_program_argsasarray("pip", ["install", "-r", filename], folder, verbosity=verbosity)
2360
2363
 
2361
2364
  @GeneralUtilities.check_arguments
2362
2365
  def ocr_analysis_of_folder(self, folder: str, serviceaddress: str, extensions: list[str], languages: list[str]) -> list[str]: # Returns a list of changed files due to ocr-analysis.
@@ -2436,3 +2439,28 @@ OCR-content:
2436
2439
  if has_staged_changes:
2437
2440
  changed_ocr_file_relative_path = os.path.relpath(changed_ocr_file, folder)
2438
2441
  self.run_program_argsasarray("git", ["add", changed_ocr_file_relative_path], folder)
2442
+
2443
+ @GeneralUtilities.check_arguments
2444
+ def update_timestamp_in_file(self, target_file: str) -> None:
2445
+ lines = GeneralUtilities.read_lines_from_file(target_file)
2446
+ new_lines = []
2447
+ prefix: str = "# last update: "
2448
+ for line in lines:
2449
+ if line.startswith(prefix):
2450
+ new_lines.append(prefix+GeneralUtilities.datetime_to_string_with_timezone(datetime.now()))
2451
+ else:
2452
+ new_lines.append(line)
2453
+ GeneralUtilities.write_lines_to_file(target_file, new_lines)
2454
+
2455
+ def do_and_log_task(self, name_of_task: str, task):
2456
+ try:
2457
+ self.log.log(f"Start action \"{name_of_task}\".", LogLevel.Information)
2458
+ result = task()
2459
+ if result is None:
2460
+ result = 0
2461
+ return result
2462
+ except Exception as e:
2463
+ self.log.log_exception(f"Error while running action \"{name_of_task}\".", e, traceback)
2464
+ return 1
2465
+ finally:
2466
+ self.log.log(f"Finished action \"{name_of_task}\".", LogLevel.Information)
@@ -1376,8 +1376,8 @@ class TasksForCommonProjectStructure:
1376
1376
  return project_version
1377
1377
 
1378
1378
  @GeneralUtilities.check_arguments
1379
- def standardized_tasks_build_for_docker_project(self, build_script_file: str, target_environment_type: str, verbosity: int, commandline_arguments: list[str]) -> None:
1380
- self.standardized_tasks_build_for_docker_project_with_additional_build_arguments(build_script_file, target_environment_type, verbosity, commandline_arguments, dict[str, str]())
1379
+ def standardized_tasks_build_for_docker_project(self, build_script_file: str, target_environment_type: str, verbosity: int, commandline_arguments: list[str], custom_arguments: dict[str, str] = None) -> None:
1380
+ self.standardized_tasks_build_for_docker_project_with_additional_build_arguments(build_script_file, target_environment_type, verbosity, commandline_arguments, custom_arguments)
1381
1381
  self.generate_sbom_for_docker_image(build_script_file, verbosity, commandline_arguments)
1382
1382
 
1383
1383
  @GeneralUtilities.check_arguments
@@ -1416,9 +1416,10 @@ class TasksForCommonProjectStructure:
1416
1416
  codeunit_file = os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml")
1417
1417
  codeunitversion = self.get_version_of_codeunit(codeunit_file)
1418
1418
  args = ["image", "build", "--pull", "--force-rm", "--progress=plain", "--build-arg", f"TargetEnvironmentType={target_environment_type}", "--build-arg", f"CodeUnitName={codeunitname}", "--build-arg", f"CodeUnitVersion={codeunitversion}", "--build-arg", f"CodeUnitOwnerName={self.get_codeunit_owner_name(codeunit_file)}", "--build-arg", f"CodeUnitOwnerEMailAddress={self.get_codeunit_owner_emailaddress(codeunit_file)}"]
1419
- for custom_argument_key, custom_argument_value in custom_arguments.items():
1420
- args.append("--build-arg")
1421
- args.append(f"{custom_argument_key}={custom_argument_value}")
1419
+ if custom_arguments is not None:
1420
+ for custom_argument_key, custom_argument_value in custom_arguments.items():
1421
+ args.append("--build-arg")
1422
+ args.append(f"{custom_argument_key}={custom_argument_value}")
1422
1423
  args = args+["--tag", f"{codeunitname_lower}:latest", "--tag", f"{codeunitname_lower}:{codeunitversion}", "--file", f"{codeunitname}/Dockerfile"]
1423
1424
  if not use_cache:
1424
1425
  args.append("--no-cache")
@@ -2428,11 +2429,13 @@ class TasksForCommonProjectStructure:
2428
2429
  relative_script_file = task["command"]
2429
2430
 
2430
2431
  relative_script_file = "."
2432
+ cwd: str = None
2431
2433
  if "options" in task:
2432
2434
  options = task["options"]
2433
2435
  if "cwd" in options:
2434
- cwd: str = options["cwd"]
2436
+ cwd = options["cwd"]
2435
2437
  cwd = cwd.replace("${workspaceFolder}", ".")
2438
+ cwd = cwd.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2436
2439
  relative_script_file = cwd
2437
2440
  if len(relative_script_file) == 0:
2438
2441
  relative_script_file = "."
@@ -2450,15 +2453,16 @@ class TasksForCommonProjectStructure:
2450
2453
  if append_cli_args_at_end:
2451
2454
  command_with_args = f"{command_with_args} {{{{.CLI_ARGS}}}}"
2452
2455
 
2453
- cwd_literal = cwd.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2454
2456
  description_literal = description.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2457
+ command_with_args = command_with_args.replace("\\", "\\\\").replace('"', '\\"') # escape backslashes and double quotes for YAML
2455
2458
 
2456
2459
  lines.append(f" {name}:")
2457
2460
  lines.append(f' desc: "{description_literal}"')
2458
2461
  lines.append(' silent: true')
2459
- lines.append(f' dir: "{cwd_literal}"')
2462
+ if cwd is not None:
2463
+ lines.append(f' dir: "{cwd}"')
2460
2464
  lines.append(" cmds:")
2461
- lines.append(f" - {command_with_args}")
2465
+ lines.append(f' - "{command_with_args}"')
2462
2466
  lines.append(' aliases:')
2463
2467
  lines.append(f' - {name.lower()}')
2464
2468
  if "aliases" in task:
@@ -2649,6 +2653,7 @@ class TasksForCommonProjectStructure:
2649
2653
 
2650
2654
  @GeneralUtilities.check_arguments
2651
2655
  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] = [], do_git_clean_when_no_changes: bool = False, note: str = None, check_for_new_files: bool = True) -> None:
2656
+ now_begin: datetime = datetime.now()
2652
2657
  codeunits_list = "{"+", ".join(["a", "b"])+"}"
2653
2658
  if verbosity > 2:
2654
2659
  GeneralUtilities.write_message_to_stdout(f"Start building codeunits ({codeunits_list}) in repository '{repository_folder}'...")
@@ -2672,7 +2677,7 @@ class TasksForCommonProjectStructure:
2672
2677
  sorted_codeunits = [codeunit for codeunit in sorted_codeunits if codeunit in codeunits]
2673
2678
  project_version = self.get_version_of_project(repository_folder)
2674
2679
 
2675
- message = f"Build codeunits in product {repository_name}..."
2680
+ message = f"Build codeunits in product {repository_name}... (Started: {GeneralUtilities.datetime_to_string(now_begin)})"
2676
2681
  if note is not None:
2677
2682
  message = f"{message} ({note})"
2678
2683
  GeneralUtilities.write_message_to_stdout(message)
@@ -2714,7 +2719,8 @@ class TasksForCommonProjectStructure:
2714
2719
  archive_file = os.path.join(os.getcwd(), f"{filename_without_extension}.zip")
2715
2720
  shutil.move(archive_file, target_folder)
2716
2721
 
2717
- message2 = f"Finished build codeunits in product {repository_name}."
2722
+ now_end: datetime = datetime.now()
2723
+ message2 = f"Finished build codeunits in product {repository_name}. (Finished: {GeneralUtilities.datetime_to_string(now_end)})"
2718
2724
  if note is not None:
2719
2725
  message2 = f"{message2} ({note})"
2720
2726
  GeneralUtilities.write_message_to_stdout(message2)
@@ -3466,9 +3472,9 @@ class TasksForCommonProjectStructure:
3466
3472
  iu.update_all_services_in_docker_compose_file(dockercomposefile, VersionEcholon.LatestPatchOrLatestMinor, excluded)
3467
3473
  iu.check_for_newest_version(dockercomposefile, excluded)
3468
3474
 
3469
-
3470
3475
  @GeneralUtilities.check_arguments
3471
3476
  def clone_repository_as_resource(self, local_repository_folder: str, remote_repository_link: str, resource_name: str, repository_subname: str = None) -> None:
3477
+ GeneralUtilities.write_message_to_stdout(f'Clone resource {resource_name}...')
3472
3478
  resrepo_commit_id_folder: str = os.path.join(local_repository_folder, "Other", "Resources", f"{resource_name}Version")
3473
3479
  resrepo_commit_id_file: str = os.path.join(resrepo_commit_id_folder, f"{resource_name}Version.txt")
3474
3480
  latest_version: str = GeneralUtilities.read_text_from_file(resrepo_commit_id_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ScriptCollection
3
- Version: 3.5.152
3
+ Version: 3.5.154
4
4
  Summary: The ScriptCollection is the place for reusable scripts.
5
5
  Home-page: https://github.com/anionDev/ScriptCollection
6
6
  Author: Marius Göcke
@@ -23,12 +23,12 @@ Classifier: Topic :: Utilities
23
23
  Requires-Python: >=3.10
24
24
  Description-Content-Type: text/markdown
25
25
  Requires-Dist: build>=1.3.0
26
- Requires-Dist: coverage>=7.10.2
26
+ Requires-Dist: coverage>=7.10.5
27
27
  Requires-Dist: cyclonedx-bom>=7.0.0
28
28
  Requires-Dist: defusedxml>=0.7.1
29
29
  Requires-Dist: keyboard>=0.13.5
30
30
  Requires-Dist: lcov-cobertura>=2.1.1
31
- Requires-Dist: lxml>=6.0.0
31
+ Requires-Dist: lxml>=6.0.1
32
32
  Requires-Dist: ntplib>=0.4.0
33
33
  Requires-Dist: Pillow>=11.3.0
34
34
  Requires-Dist: psutil>=7.0.0
@@ -36,7 +36,7 @@ Requires-Dist: pycdlib>=1.14.0
36
36
  Requires-Dist: Pygments>=2.19.2
37
37
  Requires-Dist: pylint>=3.3.8
38
38
  Requires-Dist: pyOpenSSL>=25.1.0
39
- Requires-Dist: PyPDF>=5.9.0
39
+ Requires-Dist: PyPDF>=6.0.0
40
40
  Requires-Dist: pytest>=8.4.1
41
41
  Requires-Dist: PyYAML>=6.0.2
42
42
  Requires-Dist: qrcode>=8.2
@@ -0,0 +1,17 @@
1
+ ScriptCollection/CertificateUpdater.py,sha256=OAxrG21k_o3W3niOOGNSZzUPJlvolOWc1lRB2dMhc3g,9212
2
+ ScriptCollection/Executables.py,sha256=BojgHGBYn5QqpsdIgn8r8raxb8bE_BUyb-fZ_rCEN0A,37050
3
+ ScriptCollection/GeneralUtilities.py,sha256=t8xp0s10V-MrWYQQiMx0ae3LLTM8G2wPyyWXqeIteXQ,48023
4
+ ScriptCollection/ImageUpdater.py,sha256=qTe3yoqzQJY7LZdXBbjbWvrsSQaeHy1VwmOxaRzU2ig,29305
5
+ ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
6
+ ScriptCollection/ProgramRunnerBase.py,sha256=2kMIAqdc65UjBAddOZkzy_aFx9h5roZ5a4bQNM6RV6Y,2480
7
+ ScriptCollection/ProgramRunnerEpew.py,sha256=4pjEd0r9Fcz3TTDv0MdTSd5KkigYXcWUVI1X43regfU,6477
8
+ ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
9
+ ScriptCollection/SCLog.py,sha256=49NlLEmK_f-icw_uEPq9U3qv-oBTMRcIqSg3jaO8VsA,4360
10
+ ScriptCollection/ScriptCollectionCore.py,sha256=vzMtn001WIBWKPgJak1J42iZjpTDrKIgw4Q3ab50AcY,139260
11
+ ScriptCollection/TasksForCommonProjectStructure.py,sha256=6m8Mu_G_ydkFn2twYrbf9Kt7YoWZjJnuUztJbl4bZPU,247230
12
+ ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ scriptcollection-3.5.154.dist-info/METADATA,sha256=heYQXlbf-hXoNwkR2mPredjwCtwMg1Ybs18Ji-8_JxE,7689
14
+ scriptcollection-3.5.154.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ scriptcollection-3.5.154.dist-info/entry_points.txt,sha256=yZlEK0r5Ie7xrSLdlWZgFqzLZiIctsIAUJvDCgrYBRo,4193
16
+ scriptcollection-3.5.154.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
17
+ scriptcollection-3.5.154.dist-info/RECORD,,
@@ -56,4 +56,5 @@ scshowmissingfiles = ScriptCollection.Executables:ShowMissingFiles
56
56
  scsigncertificate = ScriptCollection.Executables:SignCertificate
57
57
  scupdateimagesindockercomposefile = ScriptCollection.Executables:UpdateImagesInDockerComposeFile
58
58
  scupdatenugetpackagesincsharpproject = ScriptCollection.Executables:UpdateNugetpackagesInCsharpProject
59
+ scupdatetimestampinfile = ScriptCollection.Executables:UpdateTimestampInFile
59
60
  scuploadfile = ScriptCollection.Executables:UploadFile
@@ -1,17 +0,0 @@
1
- ScriptCollection/CertificateUpdater.py,sha256=OAxrG21k_o3W3niOOGNSZzUPJlvolOWc1lRB2dMhc3g,9212
2
- ScriptCollection/Executables.py,sha256=eX94RLNBarpWoZKeDo02gcP_qzmJDZcIx8XBZFVCFNc,36396
3
- ScriptCollection/GeneralUtilities.py,sha256=PKdzq382RYVSWeSG_6z6FiHu-SiTOi2BavJKvP-0slU,47308
4
- ScriptCollection/ImageUpdater.py,sha256=3B5pgAMnyne3ZogWz-suqglZGIB9FAKyWn31P9E1ST0,24491
5
- ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
6
- ScriptCollection/ProgramRunnerBase.py,sha256=2kMIAqdc65UjBAddOZkzy_aFx9h5roZ5a4bQNM6RV6Y,2480
7
- ScriptCollection/ProgramRunnerEpew.py,sha256=4pjEd0r9Fcz3TTDv0MdTSd5KkigYXcWUVI1X43regfU,6477
8
- ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
9
- ScriptCollection/SCLog.py,sha256=GJ44S6VaBVwX5Dd6MIrdZn6I0dpaaYKVq9w-N0nMXlo,4496
10
- ScriptCollection/ScriptCollectionCore.py,sha256=cOswEDwCQ4ndqIz-X0M2lGQD1WBtKLXvjd5aXtyoxUo,138093
11
- ScriptCollection/TasksForCommonProjectStructure.py,sha256=_QNBZo_48y6UIGKpeepkrCHNvkCpO6MHB2kakgLBe4k,246625
12
- ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- scriptcollection-3.5.152.dist-info/METADATA,sha256=W5nkU3X26V-6tgI3aFnpzsaaVPOFHYcHQ1iQW2U_ILg,7689
14
- scriptcollection-3.5.152.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- scriptcollection-3.5.152.dist-info/entry_points.txt,sha256=AvmVO9iyWImExpvzL3YYQ9AOEiUIN9guPRRG_W_VNWY,4116
16
- scriptcollection-3.5.152.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
17
- scriptcollection-3.5.152.dist-info/RECORD,,