scriptcollection 4.2.81__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. ScriptCollection/AnionBuildPlatform.py +199 -0
  2. ScriptCollection/CertificateUpdater.py +149 -0
  3. ScriptCollection/Executables.py +921 -0
  4. ScriptCollection/GeneralUtilities.py +1589 -0
  5. ScriptCollection/HTTPMaintenanceOverheadHelper.py +36 -0
  6. ScriptCollection/OCIImages/AbstractImageHandler.py +38 -0
  7. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerDebian.py +20 -0
  8. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerDebianSlim.py +20 -0
  9. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerGeneric.py +20 -0
  10. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerGenericV.py +20 -0
  11. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerGitlabCE.py +20 -0
  12. ScriptCollection/OCIImages/ConcreteImageHandlers/ImageHandlerGitlabEE.py +20 -0
  13. ScriptCollection/OCIImages/ConcreteImageHandlers/__init__.py +0 -0
  14. ScriptCollection/OCIImages/OCIImageManager.py +190 -0
  15. ScriptCollection/OCIImages/__init__.py +0 -0
  16. ScriptCollection/ProcessesRunner.py +43 -0
  17. ScriptCollection/ProgramRunnerBase.py +47 -0
  18. ScriptCollection/ProgramRunnerMock.py +2 -0
  19. ScriptCollection/ProgramRunnerPopen.py +57 -0
  20. ScriptCollection/ProgramRunnerSudo.py +108 -0
  21. ScriptCollection/Resources/CultureChooser/CultureChooser.js +29 -0
  22. ScriptCollection/Resources/CultureChooser/index.html +15 -0
  23. ScriptCollection/Resources/MaintenanceSite/MaintenanceSite.html +15 -0
  24. ScriptCollection/SCLog.py +115 -0
  25. ScriptCollection/ScriptCollectionCore.py +3485 -0
  26. ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +192 -0
  27. ScriptCollection/TFCPS/Docker/__init__.py +0 -0
  28. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py +8 -0
  29. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py +6 -0
  30. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationNoGenerate.py +7 -0
  31. ScriptCollection/TFCPS/DotNet/TFCPS_CodeUnitSpecific_DotNet.py +547 -0
  32. ScriptCollection/TFCPS/DotNet/__init__.py +0 -0
  33. ScriptCollection/TFCPS/Flutter/TFCPS_CodeUnitSpecific_Flutter.py +137 -0
  34. ScriptCollection/TFCPS/Flutter/__init__.py +0 -0
  35. ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py +72 -0
  36. ScriptCollection/TFCPS/Go/__init__.py +0 -0
  37. ScriptCollection/TFCPS/Maven/TFCPS_CodeUnitSpecific_Maven.py +42 -0
  38. ScriptCollection/TFCPS/Maven/__init__.py +0 -0
  39. ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py +232 -0
  40. ScriptCollection/TFCPS/NodeJS/__init__.py +0 -0
  41. ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +239 -0
  42. ScriptCollection/TFCPS/Python/__init__.py +0 -0
  43. ScriptCollection/TFCPS/Rust/TFCPS_CodeUnitSpecific_Rust.py +42 -0
  44. ScriptCollection/TFCPS/Rust/__init__.py +0 -0
  45. ScriptCollection/TFCPS/TFCPS_CodeUnitSpecific_Base.py +433 -0
  46. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnit.py +135 -0
  47. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnits.py +301 -0
  48. ScriptCollection/TFCPS/TFCPS_CreateRelease.py +98 -0
  49. ScriptCollection/TFCPS/TFCPS_Generic.py +44 -0
  50. ScriptCollection/TFCPS/TFCPS_MergeToMain.py +128 -0
  51. ScriptCollection/TFCPS/TFCPS_MergeToStable.py +356 -0
  52. ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py +48 -0
  53. ScriptCollection/TFCPS/TFCPS_Tools_General.py +1565 -0
  54. ScriptCollection/TFCPS/__init__.py +0 -0
  55. ScriptCollection/__init__.py +0 -0
  56. ScriptCollection/__pycache__/GeneralUtilities.cpython-311.pyc +0 -0
  57. ScriptCollection/__pycache__/__init__.cpython-311.pyc +0 -0
  58. scriptcollection-4.2.81.dist-info/METADATA +169 -0
  59. scriptcollection-4.2.81.dist-info/RECORD +62 -0
  60. scriptcollection-4.2.81.dist-info/WHEEL +5 -0
  61. scriptcollection-4.2.81.dist-info/entry_points.txt +67 -0
  62. scriptcollection-4.2.81.dist-info/top_level.txt +1 -0
@@ -0,0 +1,199 @@
1
+ import argparse
2
+ import os
3
+ from .TFCPS.TFCPS_CodeUnit_BuildCodeUnits import TFCPS_CodeUnit_BuildCodeUnits
4
+ from .SCLog import LogLevel
5
+ from .GeneralUtilities import GeneralUtilities
6
+ from .ScriptCollectionCore import ScriptCollectionCore
7
+
8
+ class AnionBuildPlatformConfiguration:
9
+ build_repositories_folder:str
10
+ additional_arguments_file:str
11
+ verbosity:LogLevel
12
+ source_branch:str#other/next-release
13
+ common_remote_name:str
14
+ update_dependencies:bool
15
+ lazy_mode:bool
16
+
17
+ def __init__(self,
18
+ build_repositories_folder:str,
19
+ additional_arguments_file:str,
20
+ verbosity:LogLevel,
21
+ source_branch:str,
22
+ common_remote_name:str,
23
+ update_dependencies:bool,
24
+ lazy_mode:bool):
25
+ self.build_repositories_folder=build_repositories_folder
26
+ self.additional_arguments_file=additional_arguments_file
27
+ self.verbosity=verbosity
28
+ self.source_branch=source_branch
29
+ self.common_remote_name=common_remote_name
30
+ self.update_dependencies=update_dependencies
31
+ self.lazy_mode=lazy_mode
32
+
33
+ class AnionBuildPlatform:
34
+
35
+ __configuration: AnionBuildPlatformConfiguration
36
+ __sc:ScriptCollectionCore
37
+
38
+ def __init__(self, configuration: AnionBuildPlatformConfiguration):
39
+ self.__configuration = configuration
40
+ self.__sc = ScriptCollectionCore()
41
+ self.__sc.log.loglevel=configuration.verbosity
42
+
43
+ @GeneralUtilities.check_arguments
44
+ def run(self) -> None:
45
+ #TODO refactor this
46
+ # ensure that if
47
+ # - main is up to date and
48
+ # - all dependencies are up to date and
49
+ # - other/next-release==main and
50
+ # - main is buildable and
51
+ # - the latest main is already merged in stable
52
+ # then this function does nothing
53
+
54
+ # Checkout source branch
55
+ build_repo_folder:str=self.__configuration.build_repositories_folder
56
+ GeneralUtilities.assert_condition(build_repo_folder.endswith("Build"),f"buildrepositoriesfolder {build_repo_folder} must end with 'Build'")
57
+ self.__sc.assert_is_git_repository(build_repo_folder)
58
+ product_name=os.path.basename(build_repo_folder)[:-len("Build")]
59
+ repository:str=os.path.join(build_repo_folder,"Submodules",product_name)
60
+ self.__sc.assert_is_git_repository(repository)
61
+ reference_repo:str=os.path.join(build_repo_folder,"Submodules",product_name+"Reference")
62
+ self.__sc.assert_is_git_repository(reference_repo)
63
+ self.__sc.git_commit(reference_repo,"Updated changes")
64
+ self.__sc.git_checkout(repository,self.__configuration.source_branch)
65
+ self.__sc.git_commit(build_repo_folder,"Updated changes")
66
+
67
+ # Pull changes from remote
68
+ self.__sc.git_fetch(repository)
69
+ self.__sc.git_merge(repository,self.__configuration.common_remote_name+"/"+self.__configuration.source_branch,self.__configuration.source_branch,fastforward=True)#TODO check if is anchestor and throw exception if nor
70
+ self.__sc.git_commit(build_repo_folder,"Updated changes")
71
+
72
+ # Added changelog entry and build to verify buildability and to update versions etc.
73
+ if self.__configuration.lazy_mode:
74
+ self.__sc.run_program("sccreatechangelogentry","-m Update.",repository)
75
+ arguments:str="bb --"
76
+ if self.__configuration.verbosity == LogLevel.Debug:
77
+ arguments+=f" --verbosity {self.__configuration.verbosity.value}"
78
+ self.__sc.run_program("task",arguments,repository)
79
+ self.__sc.git_commit(repository,"update")
80
+
81
+ # Update dependencies
82
+ if self.__configuration.update_dependencies:
83
+ self.__update_dependencies(product_name)
84
+
85
+ # Do release
86
+ scripts_folder:str=os.path.join(build_repo_folder,"Scripts","CreateRelease")
87
+
88
+ merge_to_main_arguments=""
89
+ #if self.__configuration.project_to_build is not None:
90
+ # merge_to_main_arguments+=f" --productname {self.__configuration.project_to_build}"
91
+ if self.__configuration.source_branch is not None:
92
+ merge_to_main_arguments+=f" --mergesourcebranch {self.__configuration.source_branch}"
93
+ #if self.__configuration.additional_arguments_file is not None:
94
+ # merge_to_main_arguments+=f" --additionalargumentsfile {self.__configuration.additional_arguments_file}"
95
+ #if self.__configuration.main_branch is not None:
96
+ # merge_to_main_arguments+=f" --mainbranch {self.__configuration.main_branch}"
97
+ #if self.__configuration.common_remote_name is not None:
98
+ # merge_to_main_arguments+=f" --commonremotename {self.__configuration.common_remote_name}"
99
+ if self.__configuration.verbosity is not None:
100
+ merge_to_main_arguments+=f" --verbosity {self.__configuration.verbosity.value}"
101
+ self.__sc.run_program(GeneralUtilities.get_python_executable(),f"MergeToMain.py{merge_to_main_arguments}",scripts_folder,print_live_output=True)
102
+
103
+ merge_to_stable_arguments=""
104
+ #if self.__configuration.project_to_build is not None:
105
+ # merge_to_stable_arguments+=f" --productname {self.__configuration.project_to_build}"
106
+ #if self.__configuration.additional_arguments_file is not None:
107
+ # merge_to_stable_arguments+=f" --additionalargumentsfile {self.__configuration.additional_arguments_file}"
108
+ #if self.__configuration.source_branch is not None:
109
+ # merge_to_stable_arguments+=f" --sourcebranch {self.__configuration.source_branch}"
110
+ #if self.__configuration.main_branch is not None:
111
+ # merge_to_stable_arguments+=f" --targetbranch {self.__configuration.main_branch}"
112
+ #if self.__configuration.reference_repo is not None:
113
+ # merge_to_stable_arguments+=f" --referencerepo {self.__configuration.referencerepo}"
114
+ #if self.__configuration.common_remote_name is not None:
115
+ # merge_to_stable_arguments+=f" --commonremotename {self.__configuration.common_remote_name}"
116
+ #if self.__configuration.build_repo_main_branch_name is not None:
117
+ # merge_to_stable_arguments+=f" --buildrepomainbranchname {self.__configuration.build_repo_main_branch_name}"
118
+ #if self.__configuration.reference_repo_main_branch_name is not None:
119
+ # merge_to_stable_arguments+=f" --referencerepomainbranchname {self.__configuration.reference_repo_main_branch_name}"
120
+ #if self.__configuration.reference_remote_name is not None:
121
+ # merge_to_stable_arguments+=f" --referenceremotename {self.__configuration.reference_remote_name}"
122
+ #if self.__configuration.build_repo_remote_name is not None:
123
+ # merge_to_stable_arguments+=f" --buildreporemotename {self.__configuration.build_repo_remote_name}"
124
+ #if self.__configuration.artifacts_target_folder is not None:
125
+ # merge_to_stable_arguments+=f" --artifactstargetfolder {self.__configuration.artifacts_target_folder}"
126
+ #if self.__configuration.common_remote_url is not None:
127
+ # merge_to_stable_arguments+=f" --commonremoteurl {self.__configuration.common_remote_url}"
128
+ if self.__configuration.verbosity == LogLevel.Debug:
129
+ merge_to_stable_arguments+=f" --verbosity {self.__configuration.verbosity.value}"
130
+ self.__sc.run_program(GeneralUtilities.get_python_executable(),f"MergeToStable.py{merge_to_stable_arguments}",scripts_folder,print_live_output=True)
131
+
132
+ #prepare for next-release
133
+ self.__sc.git_checkout(repository,self.__configuration.source_branch)
134
+
135
+ @GeneralUtilities.check_arguments
136
+ def __update_dependencies(self,product_name:str) -> None:
137
+ self.__sc.log.log("Update dependencies...")
138
+ repository:str=os.path.join(self.__configuration.build_repositories_folder,"Submodules",product_name)
139
+ t:TFCPS_CodeUnit_BuildCodeUnits=TFCPS_CodeUnit_BuildCodeUnits(repository,self.__sc.log.loglevel,"QualityCheck",None,True,False)
140
+ t.update_dependencies()
141
+
142
+
143
+
144
+ class TFCPS_AnionBuildPlatform_CLI:
145
+
146
+ @staticmethod
147
+ @GeneralUtilities.check_arguments
148
+ def get_with_overwritable_defaults(default_project_to_build:str=None,default_loglevel:LogLevel=None,default_additionalargumentsfile:str=None,default_build_repositories_folder:str=None,default_source_branch:str=None,default_main_branch:str=None,default_remote_name:str=None)->AnionBuildPlatform:
149
+ parser = argparse.ArgumentParser()
150
+ verbosity_values = ", ".join(f"{lvl.value}={lvl.name}" for lvl in LogLevel)
151
+ parser.add_argument('-b', '--buildrepositoriesfolder', required=False,default=None)
152
+ parser.add_argument('-p', '--projecttobuild', required=False, default=None)
153
+ parser.add_argument('-a', '--additionalargumentsfile', required=False, default=None)
154
+ parser.add_argument('-v', '--verbosity', required=False, help=f"Sets the loglevel. Possible values: {verbosity_values}")
155
+ parser.add_argument('-s', '--sourcebranch', required=False)#other/next-release
156
+ parser.add_argument('-m', '--mainbranch', required=False)#main
157
+ parser.add_argument('-r', '--defaultremotename', required=False)#origin
158
+ parser.add_argument('-u', '--updatedependencies', required=False, action='store_true', default=False)
159
+ parser.add_argument('-l', '--lazymode', required=False, action='store_true', default=False)
160
+ args=parser.parse_args()
161
+
162
+ if args.projecttobuild is not None:
163
+ default_project_to_build=args.projecttobuild
164
+
165
+ if args.buildrepositoriesfolder is not None:
166
+ default_build_repositories_folder=args.buildrepositoriesfolder
167
+
168
+ scripts_folder=os.getcwd()
169
+
170
+ if default_build_repositories_folder is None:
171
+ parent_parent_folder=GeneralUtilities.resolve_relative_path("../..",scripts_folder)
172
+ if os.path.basename(parent_parent_folder).endswith("Build"):
173
+ default_build_repositories_folder=os.path.dirname(parent_parent_folder)
174
+ GeneralUtilities.assert_not_null(default_build_repositories_folder,"buildrepositoriesfolder is not set")
175
+
176
+ if default_project_to_build is None:
177
+ parent_parent_folder=GeneralUtilities.resolve_relative_path("../..",scripts_folder)
178
+ if os.path.basename(parent_parent_folder).endswith("Build"):
179
+ default_project_to_build=os.path.basename(parent_parent_folder)[:-len("Build")]
180
+ GeneralUtilities.assert_not_null(default_project_to_build,"projecttobuild is not set")
181
+
182
+ if args.verbosity is not None:
183
+ default_loglevel=LogLevel(int( args.verbosity))
184
+ GeneralUtilities.assert_not_null(default_loglevel,"verbosity is not set")
185
+
186
+ if args.additionalargumentsfile is not None:
187
+ default_additionalargumentsfile=args.additionalargumentsfile
188
+
189
+ if args.sourcebranch is not None:
190
+ default_source_branch=args.sourcebranch
191
+ GeneralUtilities.assert_not_null(default_source_branch,"sourcebranch is not set")
192
+
193
+ if args.defaultremotename is not None:
194
+ default_remote_name=args.defaultremotename
195
+ GeneralUtilities.assert_not_null(default_remote_name,"defaultremotename is not set")
196
+
197
+ config:AnionBuildPlatformConfiguration=AnionBuildPlatformConfiguration(default_build_repositories_folder,default_additionalargumentsfile,default_loglevel,default_source_branch,default_remote_name,args.updatedependencies,args.lazymode)
198
+ tFCPS_MergeToMain:AnionBuildPlatform=AnionBuildPlatform(config)
199
+ return tFCPS_MergeToMain
@@ -0,0 +1,149 @@
1
+ import os
2
+ from pathlib import Path
3
+ from datetime import datetime, timedelta
4
+ import traceback
5
+ from shutil import copyfile
6
+ import argparse
7
+ from .GeneralUtilities import GeneralUtilities
8
+ from .ScriptCollectionCore import ScriptCollectionCore
9
+
10
+
11
+ class CertificateUpdater:
12
+ maximal_age_of_certificates_in_days: int = None
13
+ __domains: list[str] = None
14
+ __email: str = None
15
+ __current_folder: str = None
16
+ __last_update_timestamp_file: str = None
17
+ __repository_folder: str = None
18
+ __letsencrypt_folder: str = None
19
+ __letsencrypt_live_folder: str = None
20
+ __letsencrypt_archive_folder: str = None
21
+ __log_folder: str = None
22
+ __sc: ScriptCollectionCore = None
23
+ __arguments: ScriptCollectionCore = None
24
+ __certbot_image_address_with_tag:str=None
25
+
26
+ def __init__(self, domains: list[str], email: str, current_file: str, arguments: list[str],certbot_image_address_with_tag:str=None):
27
+ self.__sc = ScriptCollectionCore()
28
+ self.maximal_age_of_certificates_in_days = 15
29
+ self.__domains = domains
30
+ self.__email = email
31
+ self.__current_folder = current_file
32
+ self.__arguments = arguments
33
+ self.__last_update_timestamp_file = GeneralUtilities.resolve_relative_path("./LastCertificateUpdate.csv", self.__current_folder)
34
+ self.__repository_folder = GeneralUtilities.resolve_relative_path("../..", self.__current_folder)
35
+ self.__sc.assert_is_git_repository(self.__repository_folder)
36
+ self.__letsencrypt_folder = f"{ self.__repository_folder}/Configuration/Volumes/letsencrypt"
37
+ self.__letsencrypt_live_folder = os.path.join(self.__letsencrypt_folder, "live")
38
+ self.__letsencrypt_archive_folder = os.path.join(self.__letsencrypt_folder, "archive")
39
+ self.__log_folder = GeneralUtilities.resolve_relative_path("Logs/Overhead", self.__repository_folder)
40
+ if certbot_image_address_with_tag is None:
41
+ certbot_image_address_with_tag="docker.io/certbot/certbot:latest"
42
+ self.__certbot_image_address_with_tag=certbot_image_address_with_tag
43
+
44
+ @GeneralUtilities.check_arguments
45
+ def __get_latest_index_by_domain(self, domain: str) -> int:
46
+ result = self.__get_latest_index_by_filelist(GeneralUtilities.get_all_files_of_folder(os.path.join(self.__letsencrypt_archive_folder, domain)))
47
+ GeneralUtilities.write_message_to_stdout(f"Debug: Latest found existing number for domain {domain}: {result}")
48
+ return result
49
+
50
+ @GeneralUtilities.check_arguments
51
+ def __get_latest_index_by_filelist(self, filenames: list[str]) -> int:
52
+ filenames = [Path(os.path.basename(file)).stem for file in filenames]
53
+ filenames = [file for file in filenames if file.startswith("privkey")]
54
+ numbers = [int(file[len("privkey"):]) for file in filenames]
55
+ result = max(numbers)
56
+ return result
57
+
58
+ @GeneralUtilities.check_arguments
59
+ def __replace_symlink_by_file(self, domain: str, filename: str, index: int) -> None:
60
+ # ".../live/example.com/cert.pem" is a symlink but should replaced by a copy of ".../archive/example.com/cert.42pem"
61
+ archive_file = os.path.join(self.__letsencrypt_archive_folder, domain, filename+str(index)+".pem")
62
+ live_folder = os.path.join(self.__letsencrypt_live_folder, domain)
63
+ live_filename = filename+".pem"
64
+ live_file = os.path.join(live_folder, live_filename)
65
+ self.__sc.run_program("rm", live_filename, live_folder, throw_exception_if_exitcode_is_not_zero=True)
66
+ copyfile(archive_file, live_file)
67
+
68
+ @GeneralUtilities.check_arguments
69
+ def __replace_file_by_symlink(self, domain: str, filename: str, index: int) -> None:
70
+ # new ".../live/example.com/cert.pem" is a file but should replaced by a symlink which points to ".../archive/example.com/cert42.pem"
71
+ live_folder = os.path.join(self.__letsencrypt_live_folder, domain)
72
+ live_filename = filename+".pem"
73
+ self.__sc.run_program("rm", live_filename, live_folder, throw_exception_if_exitcode_is_not_zero=True)
74
+ self.__sc.run_program("ln", f"-s ../../archive/{domain}/{filename+str(index)}.pem {live_filename}", live_folder, throw_exception_if_exitcode_is_not_zero=True)
75
+
76
+ @GeneralUtilities.check_arguments
77
+ def __replace_symlinks_by_files(self, domain):
78
+ index = self.__get_latest_index_by_domain(domain)
79
+ self.__replace_symlink_by_file(domain, "cert", index)
80
+ self.__replace_symlink_by_file(domain, "chain", index)
81
+ self.__replace_symlink_by_file(domain, "fullchain", index)
82
+ self.__replace_symlink_by_file(domain, "privkey", index)
83
+
84
+ @GeneralUtilities.check_arguments
85
+ def __replace_files_by_symlinks(self, domain):
86
+ index = self.__get_latest_index_by_domain(domain)
87
+ self.__replace_file_by_symlink(domain, "cert", index)
88
+ self.__replace_file_by_symlink(domain, "chain", index)
89
+ self.__replace_file_by_symlink(domain, "fullchain", index)
90
+ self.__replace_file_by_symlink(domain, "privkey", index)
91
+
92
+ @GeneralUtilities.check_arguments
93
+ def __update_certificates(self) -> None:
94
+ self.__sc.git_commit(self.__repository_folder, "Saved current changes")
95
+ error_occurred = False
96
+ for domain in self.__domains:
97
+ certbot_container_name = "certificate_updater"
98
+ try:
99
+ GeneralUtilities.write_message_to_stdout(GeneralUtilities.get_line())
100
+ GeneralUtilities.write_message_to_stdout(f"Process domain {domain}")
101
+ self.__sc.run_program("docker", f"container rm {certbot_container_name}", self.__current_folder, throw_exception_if_exitcode_is_not_zero=False)
102
+ certificate_for_domain_already_exists = os.path.isfile(f"{self.__letsencrypt_folder}/renewal/{domain}.conf")
103
+ if certificate_for_domain_already_exists:
104
+ GeneralUtilities.write_message_to_stdout(f"Update certificate for domain {domain}")
105
+ self.__replace_files_by_symlinks(domain)
106
+ else:
107
+ GeneralUtilities.write_message_to_stdout(f"Create certificate for domain {domain}")
108
+ dockerargument = f"run --rm --name {certbot_container_name} --volume {self.__letsencrypt_folder}:/etc/letsencrypt"
109
+ dockerargument = dockerargument + f" --volume {self.__log_folder}:/var/log/letsencrypt -p 80:80 "+self.__certbot_image_address_with_tag
110
+ certbotargument = f"--standalone --email {self.__email} --agree-tos --force-renewal --rsa-key-size 4096 --non-interactive --no-eff-email --domain {domain}"
111
+ if (certificate_for_domain_already_exists):
112
+ self.__sc.run_program("docker", f"{dockerargument} certonly --no-random-sleep-on-renew {certbotargument}", self.__current_folder)
113
+ self.__replace_symlinks_by_files(domain)
114
+ GeneralUtilities.write_message_to_stdout(f"Certificates updated for {domain}.")
115
+ else:
116
+ self.__sc.run_program("docker", f"{dockerargument} certonly --cert-name {domain} {certbotargument}", self.__current_folder)
117
+ except Exception as exception:
118
+ error_occurred = True
119
+ GeneralUtilities.write_exception_to_stderr_with_traceback(exception, traceback, "Error while updating certificate")
120
+ self.__sc.git_commit(self.__repository_folder, "Executed certificate-update-process")
121
+ GeneralUtilities.write_message_to_stdout("Finished certificate-update-process")
122
+ if error_occurred:
123
+ raise ValueError("Certificates for at least one domain could not be added/updated.")
124
+
125
+ @GeneralUtilities.check_arguments
126
+ def __get_last_certificate_update_date(self) -> datetime:
127
+ if os.path.exists(self.__last_update_timestamp_file):
128
+ filecontent = GeneralUtilities.read_text_from_file(self.__last_update_timestamp_file)
129
+ return GeneralUtilities.string_to_datetime(filecontent.replace("\r", GeneralUtilities.empty_string).replace("\n", GeneralUtilities.empty_string))
130
+ else:
131
+ return datetime(year=1970, month=1, day=1)
132
+
133
+ @GeneralUtilities.check_arguments
134
+ def __set_last_certificate_update_date(self, moment: datetime) -> datetime:
135
+ GeneralUtilities.ensure_file_exists(self.__last_update_timestamp_file)
136
+ GeneralUtilities.write_text_to_file(self.__last_update_timestamp_file, GeneralUtilities.datetime_to_string(moment))
137
+
138
+ @GeneralUtilities.check_arguments
139
+ def update_certificates_if_required(self) -> None:
140
+ parser = argparse.ArgumentParser(description="Updated lets-encrypt-certificates")
141
+ parser.add_argument('-f', '--force', action='store_true', required=False, default=False)
142
+ args = parser.parse_args(self.__arguments)
143
+ now = datetime.now()
144
+ if (self.__get_last_certificate_update_date()+timedelta(days=self.maximal_age_of_certificates_in_days)) < now or args.force:
145
+ GeneralUtilities.write_message_to_stdout(f"Update certificates...")
146
+ self.__update_certificates()
147
+ self.__set_last_certificate_update_date(now)
148
+ else:
149
+ GeneralUtilities.write_message_to_stdout(f"Certificates are already up to date.")