ScriptCollection 3.5.16__py3-none-any.whl → 4.0.78__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. ScriptCollection/AnionBuildPlatform.py +206 -0
  2. ScriptCollection/{UpdateCertificates.py → CertificateUpdater.py} +69 -46
  3. ScriptCollection/Executables.py +515 -18
  4. ScriptCollection/GeneralUtilities.py +1272 -873
  5. ScriptCollection/ImageUpdater.py +648 -0
  6. ScriptCollection/ProgramRunnerBase.py +10 -10
  7. ScriptCollection/ProgramRunnerMock.py +2 -0
  8. ScriptCollection/ProgramRunnerPopen.py +7 -1
  9. ScriptCollection/ProgramRunnerSudo.py +108 -0
  10. ScriptCollection/SCLog.py +115 -0
  11. ScriptCollection/ScriptCollectionCore.py +942 -266
  12. ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +95 -0
  13. ScriptCollection/TFCPS/Docker/__init__.py +0 -0
  14. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py +8 -0
  15. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py +6 -0
  16. ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationNoGenerate.py +7 -0
  17. ScriptCollection/TFCPS/DotNet/TFCPS_CodeUnitSpecific_DotNet.py +485 -0
  18. ScriptCollection/TFCPS/DotNet/__init__.py +0 -0
  19. ScriptCollection/TFCPS/Flutter/TFCPS_CodeUnitSpecific_Flutter.py +130 -0
  20. ScriptCollection/TFCPS/Flutter/__init__.py +0 -0
  21. ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py +74 -0
  22. ScriptCollection/TFCPS/Go/__init__.py +0 -0
  23. ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py +131 -0
  24. ScriptCollection/TFCPS/NodeJS/__init__.py +0 -0
  25. ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +227 -0
  26. ScriptCollection/TFCPS/Python/__init__.py +0 -0
  27. ScriptCollection/TFCPS/TFCPS_CodeUnitSpecific_Base.py +418 -0
  28. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnit.py +128 -0
  29. ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnits.py +136 -0
  30. ScriptCollection/TFCPS/TFCPS_CreateRelease.py +95 -0
  31. ScriptCollection/TFCPS/TFCPS_Generic.py +43 -0
  32. ScriptCollection/TFCPS/TFCPS_MergeToMain.py +122 -0
  33. ScriptCollection/TFCPS/TFCPS_MergeToStable.py +350 -0
  34. ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py +47 -0
  35. ScriptCollection/TFCPS/TFCPS_Tools_General.py +1356 -0
  36. ScriptCollection/TFCPS/__init__.py +0 -0
  37. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/METADATA +23 -22
  38. scriptcollection-4.0.78.dist-info/RECORD +43 -0
  39. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/WHEEL +1 -1
  40. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/entry_points.txt +32 -0
  41. ScriptCollection/ProgramRunnerEpew.py +0 -122
  42. ScriptCollection/RPStream.py +0 -42
  43. ScriptCollection/TasksForCommonProjectStructure.py +0 -2625
  44. ScriptCollection-3.5.16.dist-info/RECORD +0 -16
  45. {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,206 @@
1
+ import argparse
2
+ import os
3
+ from .TFCPS.TFCPS_CodeUnit_BuildCodeUnit import TFCPS_CodeUnit_BuildCodeUnit
4
+ from .TFCPS.TFCPS_CodeUnit_BuildCodeUnits import TFCPS_CodeUnit_BuildCodeUnits
5
+ from .TFCPS.TFCPS_Tools_General import TFCPS_Tools_General
6
+ from .SCLog import LogLevel
7
+ from .GeneralUtilities import GeneralUtilities
8
+ from .ScriptCollectionCore import ScriptCollectionCore
9
+
10
+ class AnionBuildPlatformConfiguration:
11
+ build_repositories_folder:str
12
+ additional_arguments_file:str
13
+ verbosity:LogLevel
14
+ source_branch:str
15
+ common_remote_name:str
16
+ update_dependencies:bool
17
+
18
+ def __init__(self,
19
+ build_repositories_folder:str,
20
+ additional_arguments_file:str,
21
+ verbosity:LogLevel,
22
+ source_branch:str,
23
+ common_remote_name:str,
24
+ update_dependencies: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
+
32
+ class AnionBuildPlatform:
33
+
34
+ __configuration: AnionBuildPlatformConfiguration
35
+ __sc:ScriptCollectionCore
36
+ __tFCPS_Tools_General:TFCPS_Tools_General
37
+
38
+ def __init__(self, configuration: AnionBuildPlatformConfiguration):
39
+ self.__configuration = configuration
40
+ self.__sc = ScriptCollectionCore()
41
+ self.__sc.log.loglevel=configuration.verbosity
42
+ self.__tFCPS_Tools_General=TFCPS_Tools_General(self.__sc)
43
+
44
+ def run(self) -> None:
45
+ # Checkout source branch
46
+ build_repo_folder:str=self.__configuration.build_repositories_folder
47
+ GeneralUtilities.assert_condition(build_repo_folder.endswith("Build"),f"buildrepositoriesfolder {build_repo_folder} must end with 'Build'")
48
+ self.__sc.assert_is_git_repository(build_repo_folder)
49
+ product_name=os.path.basename(build_repo_folder)[:-len("Build")]
50
+ repository:str=os.path.join(build_repo_folder,"Submodules",product_name)
51
+ self.__sc.assert_is_git_repository(repository)
52
+ self.__sc.git_commit(build_repo_folder,"Updated changes")
53
+ self.__sc.git_checkout(repository,self.__configuration.source_branch)
54
+
55
+ # Pull changes from remote
56
+ self.__sc.git_fetch(repository)
57
+ 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
58
+ self.__sc.git_commit(build_repo_folder,"Updated changes")
59
+
60
+ #Update dependencies
61
+ if self.__configuration.update_dependencies:
62
+ self.__update_dependencies(product_name)
63
+
64
+ #Do release
65
+ scripts_folder:str=os.path.join(build_repo_folder,"Scripts","CreateRelease")
66
+
67
+ merge_to_main_arguments=""
68
+ #if self.__configuration.project_to_build is not None:
69
+ # merge_to_main_arguments+=f" --productname {self.__configuration.project_to_build}"
70
+ if self.__configuration.source_branch is not None:
71
+ merge_to_main_arguments+=f" --mergesourcebranch {self.__configuration.source_branch}"
72
+ #if self.__configuration.additional_arguments_file is not None:
73
+ # merge_to_main_arguments+=f" --additionalargumentsfile {self.__configuration.additional_arguments_file}"
74
+ #if self.__configuration.main_branch is not None:
75
+ # merge_to_main_arguments+=f" --mainbranch {self.__configuration.main_branch}"
76
+ #if self.__configuration.common_remote_name is not None:
77
+ # merge_to_main_arguments+=f" --commonremotename {self.__configuration.common_remote_name}"
78
+ if self.__configuration.verbosity is not None:
79
+ merge_to_main_arguments+=f" --verbosity {self.__configuration.verbosity.value}"
80
+ self.__sc.run_program("python",f"MergeToMain.py{merge_to_main_arguments}",scripts_folder,print_live_output=True)
81
+
82
+ merge_to_stable_arguments=""
83
+ #if self.__configuration.project_to_build is not None:
84
+ # merge_to_stable_arguments+=f" --productname {self.__configuration.project_to_build}"
85
+ #if self.__configuration.additional_arguments_file is not None:
86
+ # merge_to_stable_arguments+=f" --additionalargumentsfile {self.__configuration.additional_arguments_file}"
87
+ #if self.__configuration.source_branch is not None:
88
+ # merge_to_stable_arguments+=f" --sourcebranch {self.__configuration.source_branch}"
89
+ #if self.__configuration.main_branch is not None:
90
+ # merge_to_stable_arguments+=f" --targetbranch {self.__configuration.main_branch}"
91
+ #if self.__configuration.reference_repo is not None:
92
+ # merge_to_stable_arguments+=f" --referencerepo {self.__configuration.referencerepo}"
93
+ #if self.__configuration.common_remote_name is not None:
94
+ # merge_to_stable_arguments+=f" --commonremotename {self.__configuration.common_remote_name}"
95
+ #if self.__configuration.build_repo_main_branch_name is not None:
96
+ # merge_to_stable_arguments+=f" --buildrepomainbranchname {self.__configuration.build_repo_main_branch_name}"
97
+ #if self.__configuration.reference_repo_main_branch_name is not None:
98
+ # merge_to_stable_arguments+=f" --referencerepomainbranchname {self.__configuration.reference_repo_main_branch_name}"
99
+ #if self.__configuration.reference_remote_name is not None:
100
+ # merge_to_stable_arguments+=f" --referenceremotename {self.__configuration.reference_remote_name}"
101
+ #if self.__configuration.build_repo_remote_name is not None:
102
+ # merge_to_stable_arguments+=f" --buildreporemotename {self.__configuration.build_repo_remote_name}"
103
+ #if self.__configuration.artifacts_target_folder is not None:
104
+ # merge_to_stable_arguments+=f" --artifactstargetfolder {self.__configuration.artifacts_target_folder}"
105
+ #if self.__configuration.common_remote_url is not None:
106
+ # merge_to_stable_arguments+=f" --commonremoteurl {self.__configuration.common_remote_url}"
107
+ if self.__configuration.verbosity is not None:
108
+ merge_to_stable_arguments+=f" --verbosity {self.__configuration.verbosity.value}"
109
+ self.__sc.run_program("python",f"MergeToStable.py{merge_to_stable_arguments}",scripts_folder,print_live_output=True)
110
+
111
+ #prepare for next-release
112
+ self.__sc.git_checkout(repository,self.__configuration.source_branch)
113
+
114
+ def __update_dependencies(self,product_name:str) -> None:
115
+ self.__sc.log.log("Update dependencies...")
116
+ repository:str=os.path.join(self.__configuration.build_repositories_folder,"Submodules",product_name)
117
+ self.__sc.assert_is_git_repository(repository)
118
+ self.__sc.assert_no_uncommitted_changes(repository)
119
+ if os.path.isfile(os.path.join(repository,"Other","Scripts","UpdateDependencies.py")):
120
+ self.__sc.run_program("python","UpdateDependencies.py",os.path.join(repository,"Other","Scripts"))
121
+ codeunits:list[str]=self.__tFCPS_Tools_General.get_codeunits(repository)
122
+ for codeunit_name in codeunits:
123
+ self.__sc.log.log(f"Update dependencies of codeunit {codeunit_name}...")
124
+ codeunit_folder=os.path.join(repository,codeunit_name)
125
+ tFCPS_CodeUnit_BuildCodeUnit:TFCPS_CodeUnit_BuildCodeUnit = TFCPS_CodeUnit_BuildCodeUnit(codeunit_folder,self.__sc.log.loglevel,"QualityCheck",None,True,False)
126
+ tFCPS_CodeUnit_BuildCodeUnit.build_codeunit()#ensure requirements for updating are there (some programming types needs this)
127
+ if self.__tFCPS_Tools_General.codeunit_has_updatable_dependencies(os.path.join(codeunit_folder,f"{codeunit_name}.codeunit.xml")):
128
+ self.__sc.run_program("python","UpdateDependencies.py",os.path.join(codeunit_folder,"Other"))
129
+ tFCPS_CodeUnit_BuildCodeUnit.build_codeunit()#check if codeunit is still buildable
130
+
131
+ if self.__sc.git_repository_has_uncommitted_changes(repository):
132
+ changelog_folder = os.path.join(repository, "Other", "Resources", "Changelog")
133
+ project_version:str=self.__tFCPS_Tools_General.get_version_of_project(repository)
134
+ changelog_file = os.path.join(changelog_folder, f"v{project_version}.md")
135
+ if not os.path.isfile(changelog_file):
136
+ self.__ensure_changelog_file_is_added(repository, project_version)
137
+ t=TFCPS_CodeUnit_BuildCodeUnits(repository,self.__sc.log.loglevel,"QualityCheck",None,True,False)
138
+ t.build_codeunits()#check codeunits are buildable at all
139
+ self.__sc.git_commit(repository, "Updated dependencies", stage_all_changes=True)
140
+
141
+
142
+ def __ensure_changelog_file_is_added(self, repository_folder: str, version_of_project: str):
143
+ changelog_file = os.path.join(repository_folder, "Other", "Resources", "Changelog", f"v{version_of_project}.md")
144
+ if not os.path.isfile(changelog_file):
145
+ GeneralUtilities.ensure_file_exists(changelog_file)
146
+ GeneralUtilities.write_text_to_file(changelog_file, """# Release notes
147
+
148
+ ## Changes
149
+
150
+ - Updated dependencies.
151
+ """)
152
+
153
+ class TFCPS_AnionBuildPlatform_CLI:
154
+
155
+ @staticmethod
156
+ 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:
157
+ parser = argparse.ArgumentParser()
158
+ verbosity_values = ", ".join(f"{lvl.value}={lvl.name}" for lvl in LogLevel)
159
+ parser.add_argument('-b', '--buildrepositoriesfolder', required=False,default=None)
160
+ parser.add_argument('-p', '--projecttobuild', required=False, default=None)
161
+ parser.add_argument('-a', '--additionalargumentsfile', required=False, default=None)
162
+ parser.add_argument('-v', '--verbosity', required=False, help=f"Sets the loglevel. Possible values: {verbosity_values}")
163
+ parser.add_argument('-s', '--sourcebranch', required=False)#other/next-release
164
+ parser.add_argument('-m', '--mainbranch', required=False)#main
165
+ parser.add_argument('-r', '--defaultremotename', required=False)#origin
166
+ parser.add_argument('-u', '--updatedependencies', required=False, action='store_true', default=False)
167
+ args=parser.parse_args()
168
+
169
+ if args.projecttobuild is not None:
170
+ default_project_to_build=args.projecttobuild
171
+
172
+ if args.buildrepositoriesfolder is not None:
173
+ default_build_repositories_folder=args.buildrepositoriesfolder
174
+
175
+ scripts_folder=os.getcwd()
176
+
177
+ if default_build_repositories_folder is None:
178
+ parent_parent_folder=GeneralUtilities.resolve_relative_path("../..",scripts_folder)
179
+ if os.path.basename(parent_parent_folder).endswith("Build"):
180
+ default_build_repositories_folder=os.path.dirname(parent_parent_folder)
181
+ GeneralUtilities.assert_not_null(default_build_repositories_folder,"buildrepositoriesfolder is not set")
182
+
183
+ if default_project_to_build is None:
184
+ parent_parent_folder=GeneralUtilities.resolve_relative_path("../..",scripts_folder)
185
+ if os.path.basename(parent_parent_folder).endswith("Build"):
186
+ default_project_to_build=os.path.basename(parent_parent_folder)[:-len("Build")]
187
+ GeneralUtilities.assert_not_null(default_project_to_build,"projecttobuild is not set")
188
+
189
+ if args.verbosity is not None:
190
+ default_loglevel=LogLevel(int( args.verbosity))
191
+ GeneralUtilities.assert_not_null(default_loglevel,"verbosity is not set")
192
+
193
+ if args.additionalargumentsfile is not None:
194
+ default_additionalargumentsfile=args.additionalargumentsfile
195
+
196
+ if args.sourcebranch is not None:
197
+ default_source_branch=args.sourcebranch
198
+ GeneralUtilities.assert_not_null(default_source_branch,"sourcebranch is not set")
199
+
200
+ if args.defaultremotename is not None:
201
+ default_remote_name=args.defaultremotename
202
+ GeneralUtilities.assert_not_null(default_remote_name,"defaultremotename is not set")
203
+
204
+ config:AnionBuildPlatformConfiguration=AnionBuildPlatformConfiguration(default_build_repositories_folder,default_additionalargumentsfile,default_loglevel,default_source_branch,default_remote_name,args.updatedependencies)
205
+ tFCPS_MergeToMain:AnionBuildPlatform=AnionBuildPlatform(config)
206
+ return tFCPS_MergeToMain
@@ -1,28 +1,41 @@
1
1
  import os
2
2
  from pathlib import Path
3
+ from datetime import datetime, timedelta
3
4
  import traceback
4
5
  from shutil import copyfile
5
- from .ScriptCollectionCore import ScriptCollectionCore
6
+ import argparse
6
7
  from .GeneralUtilities import GeneralUtilities
8
+ from .ScriptCollectionCore import ScriptCollectionCore
7
9
 
8
10
 
9
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
10
24
 
11
- __domains: list[str]
12
- __email: str
13
-
14
- __current_folder = os.path.dirname(os.path.abspath(__file__))
15
- __repository_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}..{os.path.sep}..{os.path.sep}", __current_folder)
16
- __letsencrypt_folder = GeneralUtilities.resolve_relative_path(f"..{os.path.sep}..{os.path.sep}Volumes{os.path.sep}letsencrypt", __current_folder)
17
- __letsencrypt_live_folder = os.path.join(__letsencrypt_folder, "live")
18
- __letsencrypt_archive_folder = os.path.join(__letsencrypt_folder, "archive")
19
- __log_folder = GeneralUtilities.resolve_relative_path(f"Logs{os.path.sep}Overhead", __repository_folder)
20
- __sc = ScriptCollectionCore()
21
- __line = "___________________________________________________________________"
22
-
23
- def __init__(self, domains: list[str], email: str):
25
+ def __init__(self, domains: list[str], email: str, current_file: str, arguments: list[str]):
26
+ self.__sc = ScriptCollectionCore()
27
+ self.maximal_age_of_certificates_in_days = 15
24
28
  self.__domains = domains
25
29
  self.__email = email
30
+ self.__current_folder = current_file
31
+ self.__arguments = arguments
32
+ self.__last_update_timestamp_file = GeneralUtilities.resolve_relative_path("./LastCertificateUpdate.csv", self.__current_folder)
33
+ self.__repository_folder = GeneralUtilities.resolve_relative_path("../..", self.__current_folder)
34
+ self.__sc.assert_is_git_repository(self.__repository_folder)
35
+ self.__letsencrypt_folder = f"{ self.__repository_folder}/Configuration/Volumes/letsencrypt"
36
+ self.__letsencrypt_live_folder = os.path.join(self.__letsencrypt_folder, "live")
37
+ self.__letsencrypt_archive_folder = os.path.join(self.__letsencrypt_folder, "archive")
38
+ self.__log_folder = GeneralUtilities.resolve_relative_path("Logs/Overhead", self.__repository_folder)
26
39
 
27
40
  @GeneralUtilities.check_arguments
28
41
  def __get_latest_index_by_domain(self, domain: str) -> int:
@@ -31,16 +44,10 @@ class CertificateUpdater:
31
44
  return result
32
45
 
33
46
  @GeneralUtilities.check_arguments
34
- def __get_latest_index_by_filelist(self, filenames: list) -> int:
35
- print("files:")
36
- print(filenames)
47
+ def __get_latest_index_by_filelist(self, filenames: list[str]) -> int:
37
48
  filenames = [Path(os.path.basename(file)).stem for file in filenames]
38
- print(filenames)
39
49
  filenames = [file for file in filenames if file.startswith("privkey")]
40
- print(filenames)
41
50
  numbers = [int(file[len("privkey"):]) for file in filenames]
42
- # numbers=[]
43
- # print([os.path.basename(file) for file in filenames])
44
51
  result = max(numbers)
45
52
  return result
46
53
 
@@ -51,7 +58,7 @@ class CertificateUpdater:
51
58
  live_folder = os.path.join(self.__letsencrypt_live_folder, domain)
52
59
  live_filename = filename+".pem"
53
60
  live_file = os.path.join(live_folder, live_filename)
54
- self.__sc.run_program("rm", live_filename, live_folder)
61
+ self.__sc.run_program("rm", live_filename, live_folder, throw_exception_if_exitcode_is_not_zero=True)
55
62
  copyfile(archive_file, live_file)
56
63
 
57
64
  @GeneralUtilities.check_arguments
@@ -59,8 +66,8 @@ class CertificateUpdater:
59
66
  # new ".../live/example.com/cert.pem" is a file but should replaced by a symlink which points to ".../archive/example.com/cert42.pem"
60
67
  live_folder = os.path.join(self.__letsencrypt_live_folder, domain)
61
68
  live_filename = filename+".pem"
62
- self.__sc.run_program("rm", live_filename, live_folder)
63
- self.__sc.run_program("ln", f"-s ../../archive/{domain}/{filename+str(index)}.pem {live_filename}", live_folder)
69
+ self.__sc.run_program("rm", live_filename, live_folder, throw_exception_if_exitcode_is_not_zero=True)
70
+ 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)
64
71
 
65
72
  @GeneralUtilities.check_arguments
66
73
  def __replace_symlinks_by_files(self, domain):
@@ -79,48 +86,64 @@ class CertificateUpdater:
79
86
  self.__replace_file_by_symlink(domain, "privkey", index)
80
87
 
81
88
  @GeneralUtilities.check_arguments
82
- def update_certificate_managed_by_docker_and_letsencrypt(self) -> None:
83
- GeneralUtilities.write_message_to_stdout("current_folder:")
84
- GeneralUtilities.write_message_to_stdout(self.__current_folder)
85
- GeneralUtilities.write_message_to_stdout("letsencrypt_folder:")
86
- GeneralUtilities.write_message_to_stdout(self.__letsencrypt_folder)
87
- GeneralUtilities.write_message_to_stdout("letsencrypt_live_folder:")
88
- GeneralUtilities.write_message_to_stdout(self.__letsencrypt_live_folder)
89
- GeneralUtilities.write_message_to_stdout("letsencrypt_archive_folder:")
90
- GeneralUtilities.write_message_to_stdout(self.__letsencrypt_archive_folder)
91
- GeneralUtilities.write_message_to_stdout("log_folder:")
92
- GeneralUtilities.write_message_to_stdout(self.__log_folder)
93
-
94
- GeneralUtilities.write_message_to_stdout(self.__line+self.__line)
95
- GeneralUtilities.write_message_to_stdout("Updating certificates")
96
- self.__sc.git_commit(self.__current_folder, "Saved current changes")
89
+ def __update_certificates(self) -> None:
90
+ self.__sc.git_commit(self.__repository_folder, "Saved current changes")
91
+ error_occurred = False
97
92
  for domain in self.__domains:
93
+ certbot_container_name = "certificate_updater"
98
94
  try:
99
- GeneralUtilities.write_message_to_stdout(self.__line)
95
+ GeneralUtilities.write_message_to_stdout(GeneralUtilities.get_line())
100
96
  GeneralUtilities.write_message_to_stdout(f"Process domain {domain}")
97
+ self.__sc.run_program("docker", f"container rm {certbot_container_name}", self.__current_folder, throw_exception_if_exitcode_is_not_zero=False)
101
98
  certificate_for_domain_already_exists = os.path.isfile(f"{self.__letsencrypt_folder}/renewal/{domain}.conf")
102
99
  if certificate_for_domain_already_exists:
103
100
  GeneralUtilities.write_message_to_stdout(f"Update certificate for domain {domain}")
104
101
  self.__replace_files_by_symlinks(domain)
105
102
  else:
106
103
  GeneralUtilities.write_message_to_stdout(f"Create certificate for domain {domain}")
107
- certbot_container_name = "r2_updatecertificates_certbot"
108
104
  dockerargument = f"run --name {certbot_container_name} --volume {self.__letsencrypt_folder}:/etc/letsencrypt"
109
- dockerargument = dockerargument+f" --volume {self.__log_folder}:/var/log/letsencrypt -p 80:80 certbot/certbot:latest"
105
+ dockerargument = dockerargument + f" --volume {self.__log_folder}:/var/log/letsencrypt -p 80:80 certbot/certbot:latest"
110
106
  certbotargument = f"--standalone --email {self.__email} --agree-tos --force-renewal --rsa-key-size 4096 --non-interactive --no-eff-email --domain {domain}"
111
107
  if (certificate_for_domain_already_exists):
112
- self.__sc.run_program("docker", f"{dockerargument} certonly --no-random-sleep-on-renew {certbotargument}", self.__current_folder, throw_exception_if_exitcode_is_not_zero=True)
108
+ self.__sc.run_program("docker", f"{dockerargument} certonly --no-random-sleep-on-renew {certbotargument}", self.__current_folder)
113
109
  self.__replace_symlinks_by_files(domain)
114
110
  else:
115
- self.__sc.run_program("docker", f"{dockerargument} certonly --cert-name {domain} {certbotargument}", self.__current_folder, throw_exception_if_exitcode_is_not_zero=True)
111
+ self.__sc.run_program("docker", f"{dockerargument} certonly --cert-name {domain} {certbotargument}", self.__current_folder)
116
112
  except Exception as exception:
113
+ error_occurred = True
117
114
  GeneralUtilities.write_exception_to_stderr_with_traceback(exception, traceback, "Error while updating certificate")
118
115
  finally:
119
116
  try:
120
117
  self.__sc.run_program("docker", f"container rm {certbot_container_name}", self.__current_folder, throw_exception_if_exitcode_is_not_zero=True)
121
118
  except Exception as exception:
122
119
  GeneralUtilities.write_exception_to_stderr_with_traceback(exception, traceback, "Error while removing container")
123
-
124
- GeneralUtilities.write_message_to_stdout("Commit changes...")
125
120
  self.__sc.git_commit(self.__repository_folder, "Executed certificate-update-process")
126
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.")