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.
- ScriptCollection/AnionBuildPlatform.py +206 -0
- ScriptCollection/{UpdateCertificates.py → CertificateUpdater.py} +69 -46
- ScriptCollection/Executables.py +515 -18
- ScriptCollection/GeneralUtilities.py +1272 -873
- ScriptCollection/ImageUpdater.py +648 -0
- ScriptCollection/ProgramRunnerBase.py +10 -10
- ScriptCollection/ProgramRunnerMock.py +2 -0
- ScriptCollection/ProgramRunnerPopen.py +7 -1
- ScriptCollection/ProgramRunnerSudo.py +108 -0
- ScriptCollection/SCLog.py +115 -0
- ScriptCollection/ScriptCollectionCore.py +942 -266
- ScriptCollection/TFCPS/Docker/TFCPS_CodeUnitSpecific_Docker.py +95 -0
- ScriptCollection/TFCPS/Docker/__init__.py +0 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationBase.py +8 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationGenerate.py +6 -0
- ScriptCollection/TFCPS/DotNet/CertificateGeneratorInformationNoGenerate.py +7 -0
- ScriptCollection/TFCPS/DotNet/TFCPS_CodeUnitSpecific_DotNet.py +485 -0
- ScriptCollection/TFCPS/DotNet/__init__.py +0 -0
- ScriptCollection/TFCPS/Flutter/TFCPS_CodeUnitSpecific_Flutter.py +130 -0
- ScriptCollection/TFCPS/Flutter/__init__.py +0 -0
- ScriptCollection/TFCPS/Go/TFCPS_CodeUnitSpecific_Go.py +74 -0
- ScriptCollection/TFCPS/Go/__init__.py +0 -0
- ScriptCollection/TFCPS/NodeJS/TFCPS_CodeUnitSpecific_NodeJS.py +131 -0
- ScriptCollection/TFCPS/NodeJS/__init__.py +0 -0
- ScriptCollection/TFCPS/Python/TFCPS_CodeUnitSpecific_Python.py +227 -0
- ScriptCollection/TFCPS/Python/__init__.py +0 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnitSpecific_Base.py +418 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnit.py +128 -0
- ScriptCollection/TFCPS/TFCPS_CodeUnit_BuildCodeUnits.py +136 -0
- ScriptCollection/TFCPS/TFCPS_CreateRelease.py +95 -0
- ScriptCollection/TFCPS/TFCPS_Generic.py +43 -0
- ScriptCollection/TFCPS/TFCPS_MergeToMain.py +122 -0
- ScriptCollection/TFCPS/TFCPS_MergeToStable.py +350 -0
- ScriptCollection/TFCPS/TFCPS_PreBuildCodeunitsScript.py +47 -0
- ScriptCollection/TFCPS/TFCPS_Tools_General.py +1356 -0
- ScriptCollection/TFCPS/__init__.py +0 -0
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/METADATA +23 -22
- scriptcollection-4.0.78.dist-info/RECORD +43 -0
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/WHEEL +1 -1
- {ScriptCollection-3.5.16.dist-info → scriptcollection-4.0.78.dist-info}/entry_points.txt +32 -0
- ScriptCollection/ProgramRunnerEpew.py +0 -122
- ScriptCollection/RPStream.py +0 -42
- ScriptCollection/TasksForCommonProjectStructure.py +0 -2625
- ScriptCollection-3.5.16.dist-info/RECORD +0 -16
- {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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
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(
|
|
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}",
|
|
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}",
|
|
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.")
|