ScriptCollection 3.5.92__py3-none-any.whl → 3.5.94__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.
@@ -619,3 +619,12 @@ def NpmI() -> int:
619
619
  t = TasksForCommonProjectStructure()
620
620
  t.do_npm_install(folder, args.force, 3 if args.verbose else 0)
621
621
  return 0
622
+
623
+
624
+ def CurrentUserHasElevatedPrivileges() -> int:
625
+ parser = argparse.ArgumentParser(description="Returns 1 if the current user has elevated privileges. Otherwise this function returns 0.")
626
+ parser.parse_args()
627
+ if GeneralUtilities.current_user_has_elevated_privileges():
628
+ return 1
629
+ else:
630
+ return 0
@@ -348,29 +348,60 @@ class GeneralUtilities:
348
348
  @staticmethod
349
349
  @check_arguments
350
350
  def ends_with_newline_character(content: bytes) -> bool:
351
- return content.endswith(b'\x0a')
351
+ result = content.endswith(GeneralUtilities.string_to_bytes("\n"))
352
+ return result
352
353
 
353
354
  @staticmethod
354
355
  @check_arguments
355
- def __get_new_line_character_if_required(file: str) -> bool:
356
+ def file_ends_with_content(file: str) -> bool:
356
357
  content = GeneralUtilities.read_binary_from_file(file)
357
358
  if len(content) == 0:
358
- return ""
359
+ return False
359
360
  else:
360
361
  if GeneralUtilities.ends_with_newline_character(content):
361
- return ""
362
+ return False
362
363
  else:
363
- return "\n"
364
+ return True
365
+
366
+ @staticmethod
367
+ @check_arguments
368
+ def get_new_line_character_for_textfile_if_required(file: str) -> bool:
369
+ if GeneralUtilities.file_ends_with_content(file):
370
+ return "\n"
371
+ else:
372
+ return ""
364
373
 
365
374
  @staticmethod
366
375
  @check_arguments
367
376
  def append_line_to_file(file: str, line: str, encoding: str = "utf-8") -> None:
368
- line = GeneralUtilities.__get_new_line_character_if_required(file)+line
369
- GeneralUtilities.append_to_file(file, line, encoding)
377
+ GeneralUtilities.append_lines_to_file(file, [line], encoding)
378
+
379
+ @staticmethod
380
+ @check_arguments
381
+ def append_lines_to_file(file: str, lines: list[str], encoding: str = "utf-8") -> None:
382
+ if len(lines) == 0:
383
+ return
384
+ is_first_line = True
385
+ for line in lines:
386
+ insert_linebreak: bool
387
+ if is_first_line:
388
+ insert_linebreak = GeneralUtilities.file_ends_with_content(file)
389
+ else:
390
+ insert_linebreak = True
391
+ line_to_write: str = None
392
+ if insert_linebreak:
393
+ line_to_write = "\n"+line
394
+ else:
395
+ line_to_write = line
396
+ with open(file, "r+b") as fileObject:
397
+ fileObject.seek(0, os.SEEK_END)
398
+ fileObject.write(GeneralUtilities.string_to_bytes(line_to_write, encoding))
399
+ is_first_line = False
370
400
 
371
401
  @staticmethod
372
402
  @check_arguments
373
403
  def append_to_file(file: str, content: str, encoding: str = "utf-8") -> None:
404
+ GeneralUtilities.assert_condition(not "\n" in content, "Appending multiple lines is not allowed. Use append_lines_to_file instead.")
374
405
  with open(file, "a", encoding=encoding) as fileObject:
375
406
  fileObject.write(content)
376
407
 
@@ -508,6 +539,11 @@ class GeneralUtilities:
508
539
  def read_lines_from_file(file: str, encoding="utf-8") -> list[str]:
509
540
  return [GeneralUtilities.strip_new_line_character(line) for line in GeneralUtilities.read_text_from_file(file, encoding).split('\n')]
510
541
 
542
+ @staticmethod
543
+ @check_arguments
544
+ def read_nonempty_lines_from_file(file: str, encoding="utf-8") -> list[str]:
545
+ return [line for line in GeneralUtilities.read_lines_from_file(file, encoding) if GeneralUtilities.string_has_content(line)]
546
+
511
547
  @staticmethod
512
548
  @check_arguments
513
549
  def read_text_from_file(file: str, encoding="utf-8") -> str:
@@ -960,20 +996,20 @@ class GeneralUtilities:
960
996
 
961
997
  @staticmethod
962
998
  @check_arguments
963
- def retry_action(action, amount_of_attempts: int,action_name:str=None) -> None:
999
+ def retry_action(action, amount_of_attempts: int, action_name: str = None) -> None:
964
1000
  amount_of_fails = 0
965
1001
  enabled = True
966
1002
  while enabled:
967
1003
  try:
968
- result=action()
1004
+ result = action()
969
1005
  return result
970
1006
  except Exception:
971
1007
  amount_of_fails = amount_of_fails+1
972
1008
  GeneralUtilities.assert_condition(not (amount_of_attempts < amount_of_fails))
973
1009
  if amount_of_fails == amount_of_attempts:
974
- message=f"Action failed {amount_of_attempts} times."
1010
+ message = f"Action failed {amount_of_attempts} times."
975
1011
  if action_name is not None:
976
- message=f"{message} Name of action: {action_name}"
1012
+ message = f"{message} Name of action: {action_name}"
977
1013
  GeneralUtilities.write_message_to_stderr(message)
978
1014
  raise
979
1015
  return None
@@ -0,0 +1,2 @@
1
+ class SCLog:
2
+ pass # TODO
@@ -12,6 +12,7 @@ import math
12
12
  import os
13
13
  from queue import Queue, Empty
14
14
  from concurrent.futures import ThreadPoolExecutor
15
+ import xml.etree.ElementTree as ET
15
16
  from pathlib import Path
16
17
  from subprocess import Popen
17
18
  import re
@@ -32,7 +33,7 @@ from .ProgramRunnerBase import ProgramRunnerBase
32
33
  from .ProgramRunnerPopen import ProgramRunnerPopen
33
34
  from .ProgramRunnerEpew import ProgramRunnerEpew, CustomEpewArgument
34
35
 
35
- version = "3.5.92"
36
+ version = "3.5.94"
36
37
  __version__ = version
37
38
 
38
39
 
@@ -2052,7 +2053,7 @@ chmod {permission} {link_file}
2052
2053
  if productname is None:
2053
2054
  productname = os.path.basename(repository)
2054
2055
  if subfolder is None:
2055
- subfolder = "Other/Resources/Reference"
2056
+ subfolder = "Other/Reference"
2056
2057
  reference_root_folder = f"{repository}/{subfolder}"
2057
2058
  reference_content_folder = reference_root_folder + "/Technical"
2058
2059
  if os.path.isdir(reference_root_folder):
@@ -2109,8 +2110,7 @@ TXDX
2109
2110
 
2110
2111
  ## Deployment-proecsses
2111
2112
 
2112
- TXDX
2113
- """)
2113
+ TXDX""")
2114
2114
  self.__add_chapter(main_reference_file, reference_content_folder, 8, 'Crosscutting Concepts', """TXDX""")
2115
2115
  self.__add_chapter(main_reference_file, reference_content_folder, 9, 'Architectural Decisions', """## Decision-board
2116
2116
 
@@ -2153,7 +2153,6 @@ TXDX
2153
2153
  - [Repository](TXDX)
2154
2154
  - [Productive-System](TXDX)
2155
2155
  - [QualityCheck-system](TXDX)
2156
-
2157
2156
  """.replace("XDX", "ODO"))
2158
2157
 
2159
2158
  @GeneralUtilities.check_arguments
@@ -2243,3 +2242,10 @@ TXDX
2243
2242
  raise ValueError(f"Folder '{folder}' does not exist.")
2244
2243
 
2245
2244
  GeneralUtilities.ensure_directory_exists(path)
2245
+
2246
+ @GeneralUtilities.check_arguments
2247
+ def format_xml_file(self, file: str) -> None:
2248
+ encoding = "utf-8"
2249
+ element = ET.XML(GeneralUtilities.read_text_from_file(file, encoding))
2250
+ ET.indent(element)
2251
+ GeneralUtilities.write_text_to_file(file, ET.tostring(element, encoding="unicode"), encoding)
@@ -307,6 +307,7 @@ class TasksForCommonProjectStructure:
307
307
  @GeneralUtilities.check_arguments
308
308
  def generate_bom_for_python_project(self, verbosity: int, codeunit_folder: str, codeunitname: str, commandline_arguments: list[str]) -> None:
309
309
  self.assert_is_codeunit_folder(codeunit_folder)
310
+ repository_folder = os.path.dirname(codeunit_folder)
310
311
  verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
311
312
  codeunitversion = self.get_version_of_codeunit_folder(codeunit_folder)
312
313
  bom_folder = "Other/Artifacts/BOM"
@@ -316,9 +317,20 @@ class TasksForCommonProjectStructure:
316
317
  raise ValueError(f"Codeunit {codeunitname} does not have a 'requirements.txt'-file.")
317
318
  # TODO check that all values from setup.cfg are contained in requirements.txt
318
319
  result = self.__sc.run_program("cyclonedx-py", "requirements", codeunit_folder, verbosity=verbosity)
319
- bom_file = os.path.join(codeunit_folder, f"{bom_folder}/{codeunitname}.{codeunitversion}.bom.json")
320
- GeneralUtilities.ensure_file_exists(bom_file)
321
- GeneralUtilities.write_text_to_file(bom_file, result[1])
320
+ bom_file_relative_json = f"{bom_folder}/{codeunitname}.{codeunitversion}.bom.json"
321
+ bom_file_relative_xml = f"{bom_folder}/{codeunitname}.{codeunitversion}.bom.xml"
322
+ bom_file_json = os.path.join(codeunit_folder, bom_file_relative_json)
323
+ bom_file_xml = os.path.join(codeunit_folder, bom_file_relative_xml)
324
+
325
+ GeneralUtilities.ensure_file_exists(bom_file_json)
326
+ GeneralUtilities.write_text_to_file(bom_file_json, result[1])
327
+ self.ensure_cyclonedxcli_is_available(repository_folder)
328
+ cyclonedx_exe = os.path.join(repository_folder, "Other/Resources/CycloneDXCLI/cyclonedx-cli")
329
+ if GeneralUtilities.current_system_is_windows():
330
+ cyclonedx_exe = cyclonedx_exe+".exe"
331
+ self.__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)
332
+ self.__sc.format_xml_file(bom_file_xml)
333
+ GeneralUtilities.ensure_file_does_not_exist(bom_file_json)
322
334
 
323
335
  @GeneralUtilities.check_arguments
324
336
  def standardized_tasks_push_wheel_file_to_registry(self, wheel_file: str, api_key: str, repository: str, gpg_identity: str, verbosity: int) -> None:
@@ -759,6 +771,7 @@ class TasksForCommonProjectStructure:
759
771
  target = f"{codeunit_folder}\\{bomfile_folder}\\{codeunit_name}.{codeunitversion}.sbom.xml"
760
772
  GeneralUtilities.ensure_file_does_not_exist(target)
761
773
  os.rename(f"{codeunit_folder}\\{bomfile_folder}\\bom.xml", target)
774
+ self.__sc.format_xml_file(target)
762
775
 
763
776
  @GeneralUtilities.check_arguments
764
777
  def standardized_tasks_run_linting_for_flutter_project_in_common_project_structure(self, script_file: str, default_verbosity: int, args: list[str]):
@@ -1338,12 +1351,38 @@ class TasksForCommonProjectStructure:
1338
1351
  @GeneralUtilities.check_arguments
1339
1352
  def standardized_tasks_build_for_docker_project(self, build_script_file: str, target_environment_type: str, verbosity: int, commandline_arguments: list[str]) -> None:
1340
1353
  self.standardized_tasks_build_for_docker_project_with_additional_build_arguments(build_script_file, target_environment_type, verbosity, commandline_arguments, dict[str, str]())
1354
+ self.generate_sbom_for_docker_image(build_script_file, verbosity, commandline_arguments)
1355
+
1356
+ @GeneralUtilities.check_arguments
1357
+ def merge_sbom_file_from_dependent_codeunit_into_this(self, build_script_file: str, dependent_codeunit_name: str) -> None:
1358
+ codeunitname: str = Path(os.path.dirname(build_script_file)).parent.parent.name
1359
+ codeunit_folder = GeneralUtilities.resolve_relative_path("../..", str(os.path.dirname(build_script_file)))
1360
+ repository_folder = GeneralUtilities.resolve_relative_path("..", codeunit_folder)
1361
+ dependent_codeunit_folder = os.path.join(repository_folder, dependent_codeunit_name).replace("\\", "/")
1362
+ t = TasksForCommonProjectStructure()
1363
+ sbom_file = f"{codeunitname}/Other/Artifacts/BOM/{codeunitname}.{t.get_version_of_codeunit_folder(codeunit_folder)}.sbom.xml"
1364
+ dependent_sbom_file = f"{dependent_codeunit_name}/Other/Artifacts/BOM/{dependent_codeunit_name}.{t.get_version_of_codeunit_folder(dependent_codeunit_folder)}.sbom.xml"
1365
+ self.merge_sbom_file(repository_folder, dependent_sbom_file, sbom_file)
1366
+
1367
+ @GeneralUtilities.check_arguments
1368
+ def merge_sbom_file(self, repository_folder: str, source_sbom_file: str, target_sbom_file: str) -> None:
1369
+ GeneralUtilities.assert_file_exists(os.path.join(repository_folder, source_sbom_file))
1370
+ GeneralUtilities.assert_file_exists(os.path.join(repository_folder, target_sbom_file))
1371
+ target_original_sbom_file = os.path.dirname(target_sbom_file)+"/"+os.path.basename(target_sbom_file)+".original.xml"
1372
+ os.rename(os.path.join(repository_folder, target_sbom_file), os.path.join(repository_folder, target_original_sbom_file))
1373
+
1374
+ self.ensure_cyclonedxcli_is_available(repository_folder)
1375
+ cyclonedx_exe = os.path.join(repository_folder, "Other/Resources/CycloneDXCLI/cyclonedx-cli")
1376
+ if GeneralUtilities.current_system_is_windows():
1377
+ cyclonedx_exe = cyclonedx_exe+".exe"
1378
+ self.__sc.run_program("cyclonedx_exe", f"merge --input-files {source_sbom_file} {target_original_sbom_file} --output-file {target_sbom_file}")
1379
+ GeneralUtilities.ensure_file_does_not_exist(os.path.join(repository_folder, target_original_sbom_file))
1380
+ self.__sc.format_xml_file(target_original_sbom_file)
1341
1381
 
1342
1382
  @GeneralUtilities.check_arguments
1343
1383
  def standardized_tasks_build_for_docker_project_with_additional_build_arguments(self, build_script_file: str, target_environment_type: str, verbosity: int, commandline_arguments: list[str], custom_arguments: dict[str, str]) -> None:
1344
1384
  use_cache: bool = False
1345
1385
  verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
1346
- sc: ScriptCollectionCore = ScriptCollectionCore()
1347
1386
  codeunitname: str = Path(os.path.dirname(build_script_file)).parent.parent.name
1348
1387
  codeunit_folder = GeneralUtilities.resolve_relative_path("../..", str(os.path.dirname(build_script_file)))
1349
1388
  codeunitname_lower = codeunitname.lower()
@@ -1358,7 +1397,7 @@ class TasksForCommonProjectStructure:
1358
1397
  args.append("--no-cache")
1359
1398
  args.append(".")
1360
1399
  codeunit_content_folder = os.path.join(codeunit_folder)
1361
- sc.run_program_argsasarray("docker", args, codeunit_content_folder, verbosity=verbosity, print_errors_as_information=True)
1400
+ self.__sc.run_program_argsasarray("docker", args, codeunit_content_folder, verbosity=verbosity, print_errors_as_information=True)
1362
1401
  artifacts_folder = GeneralUtilities.resolve_relative_path("Other/Artifacts", codeunit_folder)
1363
1402
  app_artifacts_folder = os.path.join(artifacts_folder, "BuildResult_OCIImage")
1364
1403
  GeneralUtilities.ensure_directory_does_not_exist(app_artifacts_folder)
@@ -1377,6 +1416,7 @@ class TasksForCommonProjectStructure:
1377
1416
  codeunitversion = self.get_version_of_codeunit(os.path.join(codeunit_folder, f"{codeunitname}.codeunit.xml"))
1378
1417
  GeneralUtilities.ensure_directory_exists(sbom_folder)
1379
1418
  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)
1419
+ self.__sc.format_xml_file(sbom_folder+f"/{codeunitname}.{codeunitversion}.sbom.xml")
1380
1420
 
1381
1421
  @GeneralUtilities.check_arguments
1382
1422
  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:
@@ -1527,6 +1567,8 @@ class TasksForCommonProjectStructure:
1527
1567
  author_emailaddress = expected_author.xpath('./cps:developeremailaddress/text()', namespaces=namespaces)[0]
1528
1568
  expected_authors.append((author_name, author_emailaddress))
1529
1569
  actual_authors: list[tuple[str, str]] = self.__sc.get_all_authors_and_committers_of_repository(repository_folder, codeunit_name, verbosity)
1570
+ # TODO refactor this check to only check commits which are behind this but which are not already on main
1571
+ # TODO verify also if the commit is signed by a valid key of the author
1530
1572
  for actual_author in actual_authors:
1531
1573
  if not (actual_author) in expected_authors:
1532
1574
  actual_author_formatted = f"{actual_author[0]} <{actual_author[1]}>"
@@ -1704,7 +1746,9 @@ class TasksForCommonProjectStructure:
1704
1746
  def standardized_tasks_build_bom_for_node_project(self, codeunit_folder: str, verbosity: int, commandline_arguments: list[str]) -> None:
1705
1747
  self.assert_is_codeunit_folder(codeunit_folder)
1706
1748
  verbosity = TasksForCommonProjectStructure.get_verbosity_from_commandline_arguments(commandline_arguments, verbosity)
1707
- # TODO
1749
+ relative_path_to_bom_file = f"Other/Artifacts/BOM/{os.path.basename(codeunit_folder)}.{self.get_version_of_codeunit_folder(codeunit_folder)}.sbom.xml"
1750
+ self.run_with_epew("cyclonedx-npm", f"--output-format xml --output-file {relative_path_to_bom_file}", codeunit_folder, verbosity=verbosity)
1751
+ self.__sc.format_xml_file(codeunit_folder+"/"+relative_path_to_bom_file)
1708
1752
 
1709
1753
  @GeneralUtilities.check_arguments
1710
1754
  def standardized_tasks_linting_for_angular_codeunit(self, linting_script_file: str, verbosity: int, build_environment_target_type: str, commandline_arguments: list[str]) -> None:
@@ -2509,11 +2553,10 @@ class TasksForCommonProjectStructure:
2509
2553
  i = i+1
2510
2554
  GeneralUtilities.write_message_to_stdout(f"{i}.: {codeunit}")
2511
2555
  self.__do_repository_checks(repository_folder, project_version)
2512
- line = "----------"
2513
2556
  for codeunit in sorted_codeunits:
2514
- GeneralUtilities.write_message_to_stdout(line)
2557
+ GeneralUtilities.write_message_to_stdout(GeneralUtilities.get_line())
2515
2558
  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)
2516
- GeneralUtilities.write_message_to_stdout(line)
2559
+ GeneralUtilities.write_message_to_stdout(GeneralUtilities.get_line())
2517
2560
  contains_uncommitted_changes_at_end = self.__sc.git_repository_has_uncommitted_changes(repository_folder)
2518
2561
  if contains_uncommitted_changes_at_end and not is_pre_merge:
2519
2562
  if contains_uncommitted_changes_at_begin:
@@ -2524,6 +2567,7 @@ class TasksForCommonProjectStructure:
2524
2567
  GeneralUtilities.write_message_to_stderr(f"Warning: {message}")
2525
2568
  else:
2526
2569
  raise ValueError(message)
2570
+
2527
2571
  if export_target_directory is not None:
2528
2572
  project_name = self.get_project_name(repository_folder)
2529
2573
  for codeunit in sorted_codeunits:
@@ -2547,9 +2591,14 @@ class TasksForCommonProjectStructure:
2547
2591
  self.__sc.assert_is_git_repository(repository_folder)
2548
2592
  self.__check_if_changelog_exists(repository_folder, project_version)
2549
2593
  self.__check_whether_security_txt_exists(repository_folder)
2594
+ self.__check_whether_general_reference_exists(repository_folder)
2550
2595
  self.__check_whether_workspace_file_exists(repository_folder)
2551
2596
  self.__check_for_staged_or_committed_ignored_files(repository_folder)
2552
2597
 
2598
+ @GeneralUtilities.check_arguments
2599
+ def __check_whether_general_reference_exists(self, repository_folder: str) -> None:
2600
+ GeneralUtilities.assert_file_exists(os.path.join(repository_folder, "Other", "Reference", "Reference.md"))
2601
+
2553
2602
  @GeneralUtilities.check_arguments
2554
2603
  def __check_if_changelog_exists(self, repository_folder: str, project_version: str) -> None:
2555
2604
  self.__sc.assert_is_git_repository(repository_folder)
@@ -2564,6 +2613,8 @@ class TasksForCommonProjectStructure:
2564
2613
  security_txt_file = GeneralUtilities.resolve_relative_path(security_txt_file_relative, repository_folder)
2565
2614
  if not os.path.isfile(security_txt_file):
2566
2615
  raise ValueError(f"The repository does not contain a '{security_txt_file_relative}'-file. See https://securitytxt.org/ for more information.")
2616
+ # TODO throw error if the date set in the file is expired
2617
+ # TODO write wartning if the date set in the file expires soon
2567
2618
 
2568
2619
  @GeneralUtilities.check_arguments
2569
2620
  def __check_for_staged_or_committed_ignored_files(self, repository_folder: str) -> None:
@@ -2664,6 +2715,17 @@ class TasksForCommonProjectStructure:
2664
2715
  def ensure_androidappbundletool_is_available(self, target_folder: str) -> None:
2665
2716
  self.ensure_file_from_github_assets_is_available(target_folder, "google", "bundletool", "AndroidAppBundleTool", "bundletool.jar", lambda latest_version: f"bundletool-all-{latest_version}.jar")
2666
2717
 
2718
+ @GeneralUtilities.check_arguments
2719
+ def ensure_cyclonedxcli_is_available(self, target_folder: str) -> None:
2720
+ local_filename = "cyclonedx-cli"
2721
+ filename_on_github: str
2722
+ if GeneralUtilities.current_system_is_windows():
2723
+ filename_on_github = "cyclonedx-win-x64.exe"
2724
+ local_filename = local_filename+".exe"
2725
+ else:
2726
+ filename_on_github = "cyclonedx-linux-x64"
2727
+ self.ensure_file_from_github_assets_is_available(target_folder, "CycloneDX", "cyclonedx-cli", "CycloneDXCLI", local_filename, lambda latest_version: filename_on_github)
2728
+
2667
2729
  @GeneralUtilities.check_arguments
2668
2730
  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:
2669
2731
  resource_folder = os.path.join(target_folder, "Other", "Resources", resource_name)
@@ -2674,11 +2736,11 @@ class TasksForCommonProjectStructure:
2674
2736
  GeneralUtilities.ensure_directory_does_not_exist(resource_folder)
2675
2737
  GeneralUtilities.ensure_directory_exists(resource_folder)
2676
2738
  headers = {'Cache-Control': 'no-cache'}
2677
- response = requests.get(f"https://api.github.com/repos/{githubuser}/{githubprojectname}/releases/latest", timeout=5, headers=headers)
2678
- latest_version = response.json()["name"]
2739
+ response = requests.get(f"https://api.github.com/repos/{githubuser}/{githubprojectname}/releases/latest", timeout=10, headers=headers)
2740
+ latest_version = response.json()["tag_name"]
2679
2741
  filename_on_github = get_filename_on_github(latest_version)
2680
- jar_link = f"https://github.com/{githubuser}/{githubprojectname}/releases/download/{latest_version}/{filename_on_github}"
2681
- urllib.request.urlretrieve(jar_link, file)
2742
+ link = f"https://github.com/{githubuser}/{githubprojectname}/releases/download/{latest_version}/{filename_on_github}"
2743
+ urllib.request.urlretrieve(link, file)
2682
2744
  else:
2683
2745
  if file_exists:
2684
2746
  GeneralUtilities.write_message_to_stdout(f"Warning: Can not check for updates of {resource_name} due to missing internet-connection.")
@@ -2686,15 +2748,78 @@ class TasksForCommonProjectStructure:
2686
2748
  raise ValueError(f"Can not download {resource_name}.")
2687
2749
 
2688
2750
  @GeneralUtilities.check_arguments
2689
- def generate_svg_files_from_plantuml_files(self, target_folder: str) -> None:
2690
- self.ensure_plantuml_is_available(target_folder)
2691
- plant_uml_folder = os.path.join(target_folder, "Other", "Resources", "PlantUML")
2692
- files_folder = os.path.join(target_folder, "Other", "Reference")
2751
+ def generate_svg_files_from_plantuml_files_for_repository(self, repository_folder: str) -> None:
2752
+ self.__sc.assert_is_git_repository(repository_folder)
2753
+ self.ensure_plantuml_is_available(repository_folder)
2754
+ plant_uml_folder = os.path.join(repository_folder, "Other", "Resources", "PlantUML")
2755
+ target_folder = os.path.join(repository_folder, "Other", "Reference")
2756
+ self.__generate_svg_files_from_plantuml(target_folder, plant_uml_folder)
2757
+
2758
+ @GeneralUtilities.check_arguments
2759
+ def generate_svg_files_from_plantuml_files_for_codeunit(self, codeunit_folder: str) -> None:
2760
+ self.assert_is_codeunit_folder(codeunit_folder)
2761
+ repository_folder = os.path.dirname(codeunit_folder)
2762
+ self.ensure_plantuml_is_available(repository_folder)
2763
+ plant_uml_folder = os.path.join(repository_folder, "Other", "Resources", "PlantUML")
2764
+ target_folder = os.path.join(codeunit_folder, "Other", "Reference")
2765
+ self.__generate_svg_files_from_plantuml(target_folder, plant_uml_folder)
2766
+
2767
+ @GeneralUtilities.check_arguments
2768
+ def __generate_svg_files_from_plantuml(self, diagrams_files_folder: str, plant_uml_folder: str) -> None:
2693
2769
  sc = ScriptCollectionCore()
2694
- for file in GeneralUtilities.get_all_files_of_folder(files_folder):
2770
+ for file in GeneralUtilities.get_all_files_of_folder(diagrams_files_folder):
2695
2771
  if file.endswith(".plantuml"):
2696
- argument = ['-jar', f'{plant_uml_folder}/plantuml.jar', os.path.basename(file).replace("\\", "/"), '-tsvg']
2697
- sc.run_program_argsasarray("java", argument, os.path.dirname(file), verbosity=0)
2772
+ output_filename = self.get_output_filename_for_plantuml_filename(file)
2773
+ argument = ['-jar', f'{plant_uml_folder}/plantuml.jar', '-tsvg', os.path.basename(file)]
2774
+ folder = os.path.dirname(file)
2775
+ sc.run_program_argsasarray("java", argument, folder, verbosity=0)
2776
+ result_file = folder+"/" + output_filename
2777
+ GeneralUtilities.assert_file_exists(result_file)
2778
+ self.__sc.format_xml_file(result_file)
2779
+
2780
+ @GeneralUtilities.check_arguments
2781
+ def get_output_filename_for_plantuml_filename(self, plantuml_file: str) -> str:
2782
+ for line in GeneralUtilities.read_lines_from_file(plantuml_file):
2783
+ prefix = "@startuml "
2784
+ if line.startswith(prefix):
2785
+ title = line[len(prefix):]
2786
+ return title+".svg"
2787
+ return Path(plantuml_file).stem+".svg"
2788
+
2789
+ @GeneralUtilities.check_arguments
2790
+ def generate_codeunits_overview_diagram(self, repository_folder: str) -> None:
2791
+ self.__sc.assert_is_git_repository(repository_folder)
2792
+ project_name: str = os.path.basename(repository_folder)
2793
+ target_folder = os.path.join(repository_folder, "Other", "Reference", "Technical", "Diagrams")
2794
+ GeneralUtilities.ensure_directory_exists(target_folder)
2795
+ target_file = os.path.join(target_folder, "CodeUnits-Overview.plantuml")
2796
+ lines = ["@startuml CodeUnits-Overview"]
2797
+ lines.append(f"title CodeUnits of {project_name}")
2798
+
2799
+ codeunits = self.get_codeunits(repository_folder)
2800
+ for codeunitname in codeunits:
2801
+ codeunit_file: str = os.path.join(repository_folder, codeunitname, f"{codeunitname}.codeunit.xml")
2802
+
2803
+ description = self.get_codeunit_description(codeunit_file)
2804
+
2805
+ lines.append(f"")
2806
+ lines.append(f"[{codeunitname}]")
2807
+ lines.append(f"note as {codeunitname}Note")
2808
+ lines.append(f" {description}")
2809
+ lines.append(f"end note")
2810
+ lines.append(f"{codeunitname} .. {codeunitname}Note")
2811
+
2812
+ lines.append(f"")
2813
+ for codeunitname in codeunits:
2814
+ codeunit_file: str = os.path.join(repository_folder, codeunitname, f"{codeunitname}.codeunit.xml")
2815
+ dependent_codeunits = self.get_dependent_code_units(codeunit_file)
2816
+ for dependent_codeunit in dependent_codeunits:
2817
+ lines.append(f"{codeunitname} --> {dependent_codeunit}")
2818
+
2819
+ lines.append(f"")
2820
+ lines.append("@enduml")
2821
+
2822
+ GeneralUtilities.write_lines_to_file(target_file, lines)
2698
2823
 
2699
2824
  @GeneralUtilities.check_arguments
2700
2825
  def load_deb_control_file_content(self, file: str, codeunitname: str, codeunitversion: str, installedsize: int, maintainername: str, maintaineremail: str, description: str,) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ScriptCollection
3
- Version: 3.5.92
3
+ Version: 3.5.94
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
@@ -33,7 +33,7 @@ Requires-Dist: ntplib>=0.4.0
33
33
  Requires-Dist: Pillow>=11.1.0
34
34
  Requires-Dist: pycdlib>=1.14.0
35
35
  Requires-Dist: Pygments>=2.19.1
36
- Requires-Dist: pylint>=3.3.4
36
+ Requires-Dist: pylint>=3.3.5
37
37
  Requires-Dist: pyOpenSSL>=25.0.0
38
38
  Requires-Dist: PyPDF>=5.3.1
39
39
  Requires-Dist: pytest>=8.3.5
@@ -0,0 +1,17 @@
1
+ ScriptCollection/Executables.py,sha256=HI9Pxs5Z9QxPGyqeJU2lWslEggFyGYANCqYVQZp6eJ0,30490
2
+ ScriptCollection/GeneralUtilities.py,sha256=q0ikIwmhklCFsaaj9YGPGdrNonibuRmWofvbwOtlxdM,42246
3
+ ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
4
+ ScriptCollection/ProgramRunnerBase.py,sha256=2kMIAqdc65UjBAddOZkzy_aFx9h5roZ5a4bQNM6RV6Y,2480
5
+ ScriptCollection/ProgramRunnerEpew.py,sha256=4pjEd0r9Fcz3TTDv0MdTSd5KkigYXcWUVI1X43regfU,6477
6
+ ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
7
+ ScriptCollection/RPStream.py,sha256=NRRHL3YSP3D9MuAV2jB_--0KUKCsvJGxeKnxgrRZ9kY,1545
8
+ ScriptCollection/SCLog.py,sha256=l4aekBiGoNkKGtvO_Er3NY_K7ts4ZWtIGlhq07I-4LY,30
9
+ ScriptCollection/ScriptCollectionCore.py,sha256=kuiMlekPcD_BTbCjqKgsEQjeMWL7consYcplP1lTByU,123973
10
+ ScriptCollection/TasksForCommonProjectStructure.py,sha256=QqpfA-9sLtUogsOqg4uHHS2kucwZkTuzhDs24OdFIrE,225316
11
+ ScriptCollection/UpdateCertificates.py,sha256=Eynbgu7k9jLxApP2D_8Il77B6BFjJap6K7oTeEAZYbk,7790
12
+ ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ scriptcollection-3.5.94.dist-info/METADATA,sha256=UrFm_ztejp_WXvuM7uom8cka14YxGvMC_wPzpf6rdjw,7664
14
+ scriptcollection-3.5.94.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
15
+ scriptcollection-3.5.94.dist-info/entry_points.txt,sha256=fYCGWGNGijBQHhFe6UAO-BEpfEOxLyNJemukt5ElSzs,3644
16
+ scriptcollection-3.5.94.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
17
+ scriptcollection-3.5.94.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (76.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -14,6 +14,7 @@ sccreatefolder = ScriptCollection.Executables:CreateFolder
14
14
  sccreatehashofallfiles = ScriptCollection.Executables:CreateHashOfAllFiles
15
15
  sccreateisofilewithobfuscatedfiles = ScriptCollection.Executables:CreateISOFileWithObfuscatedFiles
16
16
  sccreatesimplemergewithoutrelease = ScriptCollection.Executables:CreateSimpleMergeWithoutRelease
17
+ sccurrentuserhaselevatedprivileges = ScriptCollection.Executables:CurrentUserHasElevatedPrivileges
17
18
  scextractpdfpages = ScriptCollection.Executables:ExtractPDFPages
18
19
  scfilecontainscontent = ScriptCollection.Executables:FileContainsContent
19
20
  scfileexists = ScriptCollection.Executables:FileExists
@@ -1,16 +0,0 @@
1
- ScriptCollection/Executables.py,sha256=ApfwgRftLeG1NBpv7Jad98Yb-wI8sKjhtoIsxMmfGVY,30167
2
- ScriptCollection/GeneralUtilities.py,sha256=VVbbGFD4qLwWV211-E-n3faP2fZGc8mOBlKLd0oKHl4,40765
3
- ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
4
- ScriptCollection/ProgramRunnerBase.py,sha256=2kMIAqdc65UjBAddOZkzy_aFx9h5roZ5a4bQNM6RV6Y,2480
5
- ScriptCollection/ProgramRunnerEpew.py,sha256=4pjEd0r9Fcz3TTDv0MdTSd5KkigYXcWUVI1X43regfU,6477
6
- ScriptCollection/ProgramRunnerPopen.py,sha256=BPY7-ZMIlqT7JOKz8qlB5c0laF2Js-ijzqk09GxZC48,3821
7
- ScriptCollection/RPStream.py,sha256=NRRHL3YSP3D9MuAV2jB_--0KUKCsvJGxeKnxgrRZ9kY,1545
8
- ScriptCollection/ScriptCollectionCore.py,sha256=Qyo5uuSsVQIbwWxqGgyfetyVrZ2KO1yg_PlICIavTto,123626
9
- ScriptCollection/TasksForCommonProjectStructure.py,sha256=YWHvLc91WuCrmay9qqf43onRhYy94nbd1sKLiBfNrZ4,217137
10
- ScriptCollection/UpdateCertificates.py,sha256=Eynbgu7k9jLxApP2D_8Il77B6BFjJap6K7oTeEAZYbk,7790
11
- ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- scriptcollection-3.5.92.dist-info/METADATA,sha256=lsBwVdHT7ePQ2yjnuj-kq7uX7NVvVniSWodbiPdMJzs,7664
13
- scriptcollection-3.5.92.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
14
- scriptcollection-3.5.92.dist-info/entry_points.txt,sha256=1jAL5AuB8mvdw2v-6E7wCZFThurQxchiQynL8DCi-Yg,3545
15
- scriptcollection-3.5.92.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
16
- scriptcollection-3.5.92.dist-info/RECORD,,