checkmk-dev-tools 2.1.4__tar.gz → 2.3.0__tar.gz

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 (19) hide show
  1. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/PKG-INFO +2 -2
  2. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/ci_artifacts.py +103 -46
  3. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/version.py +1 -1
  4. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/pyproject.toml +2 -2
  5. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/README.md +0 -0
  6. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/__init__.py +0 -0
  7. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/activity_from_fs.py +0 -0
  8. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/binreplace.py +0 -0
  9. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/check_rpath.py +0 -0
  10. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/cli.py +0 -0
  11. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/cpumon.py +0 -0
  12. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/decent_output.py +0 -0
  13. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/jenkins_utils/__init__.py +0 -0
  14. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/job_resource_usage.py +0 -0
  15. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/last_access.py +0 -0
  16. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/lockable_resources.py +0 -0
  17. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/procmon.py +0 -0
  18. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/pycinfo.py +0 -0
  19. {checkmk_dev_tools-2.1.4 → checkmk_dev_tools-2.3.0}/cmk_dev/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: checkmk-dev-tools
3
- Version: 2.1.4
3
+ Version: 2.3.0
4
4
  Summary: Checkmk DevOps tools
5
5
  Author: Frans Fürst
6
6
  Author-email: frans.fuerst@checkmk.com
@@ -14,7 +14,7 @@ Requires-Dist: pydantic (>=2,<3)
14
14
  Requires-Dist: python-jenkins (>=1.8.3,<1.9.0)
15
15
  Requires-Dist: rich
16
16
  Requires-Dist: trickkiste (>=0.3.4,<0.4.0)
17
- Project-URL: Changelog, https://github.com/Checkmk/checkmk-dev-tools/blob/release/2.1.4/changelog.md
17
+ Project-URL: Changelog, https://github.com/Checkmk/checkmk-dev-tools/blob/release/2.3.0/changelog.md
18
18
  Project-URL: Repository, https://github.com/Checkmk/checkmk-dev-tools
19
19
  Description-Content-Type: text/markdown
20
20
 
@@ -24,7 +24,7 @@ from datetime import datetime
24
24
  from itertools import chain
25
25
  from pathlib import Path
26
26
  from subprocess import check_output
27
- from typing import Any, List, Literal, cast
27
+ from typing import Any, List, Literal, TypedDict, cast
28
28
 
29
29
  import requests
30
30
  from influxdb_client import InfluxDBClient # type: ignore[attr-defined]
@@ -56,6 +56,12 @@ from .version import __version__
56
56
 
57
57
  PathHashes = Mapping[str, str]
58
58
  shared_build_info: asyncio.Queue[str] = asyncio.Queue(maxsize=1)
59
+
60
+
61
+ class FetchResult(TypedDict):
62
+ result: None | JobResult
63
+ artifacts: list[str]
64
+
59
65
  MAX_RETRY_ATTEMPTS = 3
60
66
 
61
67
 
@@ -311,7 +317,7 @@ def download_artifacts(
311
317
  total_download_timeout: int = 240,
312
318
  no_remove_others: bool = False,
313
319
  no_raise: bool = False,
314
- ) -> tuple[Sequence[str | None], Sequence[str | None]]:
320
+ ) -> tuple[Sequence[str], Sequence[str]]:
315
321
  """Downloads all artifacts listed for given job/build to @out_dir"""
316
322
  # pylint: disable=protected-access
317
323
  # pylint: disable=too-many-locals
@@ -733,6 +739,7 @@ async def _fn_request_build(args: Args) -> None:
733
739
  path_hashes=compose_path_hashes(args.base_dir, args.dependency_paths),
734
740
  time_constraints=args.time_constraints,
735
741
  next_check_sleep=args.poll_queue_sleep,
742
+ ignore_build_queue=args.ignore_build_queue,
736
743
  args=args,
737
744
  )
738
745
  ):
@@ -901,54 +908,72 @@ def query_matching_builds(
901
908
  return []
902
909
 
903
910
 
904
- async def _fn_fetch(args: Args) -> None:
905
- """Entry point for fetching (request and download combined) artifacts"""
906
- out_dir = compose_out_dir(args.base_dir, args.out_dir)
907
- path_hashes = compose_path_hashes(args.base_dir, args.dependency_paths)
911
+ async def fetch_artifacts(
912
+ job: str,
913
+ params: None | JobParams = None,
914
+ *,
915
+ out_dir: Path = Path("out"),
916
+ base_dir: Path = Path("."),
917
+ params_no_check: None | JobParams = None,
918
+ dependency_paths: None | Sequence[str] = None,
919
+ time_constraints: None | str = None,
920
+ force_new_build: bool = False,
921
+ omit_new_build: bool = False,
922
+ download: bool = True,
923
+ no_remove_others: bool = False,
924
+ no_raise: bool = False,
925
+ poll_queue_sleep: int = 30,
926
+ poll_sleep: int = 60,
927
+ timeout: int = 120,
928
+ total_download_timeout: int = 240,
929
+ credentials: None | Mapping[str, str] = None,
930
+ credentials_file: str = "~/.config/jenkins_jobs/jenkins_jobs.ini",
931
+ ) -> FetchResult:
932
+ """Find or trigger a matching build, wait for it, download artifacts, return result dict."""
933
+ resolved_out_dir = compose_out_dir(base_dir, out_dir)
934
+ path_hashes = compose_path_hashes(base_dir, dependency_paths or [])
908
935
  async with AugmentedJenkinsClient(
909
- **extract_credentials(args.credentials), timeout=args.timeout
936
+ **extract_credentials(credentials, credentials_file=credentials_file), timeout=timeout
910
937
  ) as jenkins_client:
911
- if not (job := await jenkins_client.job_info(args.job)).type == "WorkflowJob":
912
- raise Fatal(f"{args.job} is not a WorkflowJob")
913
- # In case we force a new build anyway we don't have to look for an existing one
938
+ if not (job_info := await jenkins_client.job_info(job)).type == "WorkflowJob":
939
+ raise Fatal(f"{job} is not a WorkflowJob")
914
940
  matching_build = (
915
941
  None
916
- if args.force_new_build
942
+ if force_new_build
917
943
  else await identify_matching_build(
918
- job,
944
+ job_info,
919
945
  jenkins_client=jenkins_client,
920
- params=flatten(args.params),
921
- path_hashes=compose_path_hashes(args.base_dir, args.dependency_paths),
922
- time_constraints=args.time_constraints,
923
- next_check_sleep=args.poll_queue_sleep,
924
- args=args,
946
+ params=params,
947
+ path_hashes=path_hashes,
948
+ time_constraints=time_constraints,
949
+ next_check_sleep=poll_queue_sleep,
925
950
  )
926
951
  )
927
- if args.omit_new_build and not matching_build:
928
- raise Fatal(f"No matching build found for job '{job.name}' but new builds are omitted.")
929
-
952
+ if omit_new_build and not matching_build:
953
+ raise Fatal(
954
+ f"No matching build found for job '{job_info.name}' but new builds are omitted."
955
+ )
930
956
  build_candidate = matching_build or await trigger_build(
931
957
  jenkins_client=jenkins_client,
932
- job=job,
958
+ job=job_info,
933
959
  params=compose_build_params(
934
- params=flatten(args.params),
935
- params_no_check=flatten(args.params_no_check),
936
- path_hashes=compose_path_hashes(args.base_dir, args.dependency_paths),
960
+ params=params,
961
+ params_no_check=params_no_check,
962
+ path_hashes=path_hashes,
937
963
  ),
938
- next_check_sleep=args.poll_queue_sleep,
964
+ next_check_sleep=poll_queue_sleep,
939
965
  )
940
-
941
966
  for key, value in build_candidate.__dict__.items():
942
967
  log().debug(" %s: %s", key, value)
943
-
944
968
  completed_build = await await_build(
945
- job.path,
969
+ job_info.path,
946
970
  build_candidate.number,
947
971
  jenkins_client=jenkins_client,
948
972
  check_result=True,
949
973
  path_hashes=path_hashes,
950
- next_check_sleep=args.poll_sleep,
951
- no_raise=args.no_raise,
974
+ next_check_sleep=poll_sleep,
975
+ no_raise=no_raise,
976
+ download=download,
952
977
  )
953
978
  downloaded_artifacts = (
954
979
  list(
@@ -956,25 +981,44 @@ async def _fn_fetch(args: Args) -> None:
956
981
  *download_artifacts(
957
982
  jenkins_client.client,
958
983
  completed_build,
959
- out_dir,
960
- args.total_download_timeout,
961
- args.no_remove_others,
962
- args.no_raise,
984
+ resolved_out_dir,
985
+ total_download_timeout,
986
+ no_remove_others,
987
+ no_raise,
963
988
  )
964
989
  )
965
990
  )
966
- if args.download
991
+ if download
967
992
  else []
968
993
  )
994
+ return {"result": completed_build.result, "artifacts": downloaded_artifacts}
969
995
 
970
- print(
971
- json.dumps(
972
- {
973
- "result": completed_build.result,
974
- "artifacts": downloaded_artifacts,
975
- }
996
+
997
+ async def _fn_fetch(args: Args) -> None:
998
+ """Entry point for fetching (request and download combined) artifacts"""
999
+ print(
1000
+ json.dumps(
1001
+ await fetch_artifacts(
1002
+ job=args.job,
1003
+ params=flatten(args.params),
1004
+ out_dir=args.out_dir,
1005
+ base_dir=args.base_dir,
1006
+ params_no_check=flatten(args.params_no_check),
1007
+ dependency_paths=args.dependency_paths,
1008
+ time_constraints=args.time_constraints,
1009
+ force_new_build=args.force_new_build,
1010
+ omit_new_build=args.omit_new_build,
1011
+ download=args.download,
1012
+ no_remove_others=args.no_remove_others,
1013
+ no_raise=args.no_raise,
1014
+ poll_queue_sleep=args.poll_queue_sleep,
1015
+ poll_sleep=args.poll_sleep,
1016
+ timeout=args.timeout,
1017
+ total_download_timeout=args.total_download_timeout,
1018
+ credentials=args.credentials,
976
1019
  )
977
1020
  )
1021
+ )
978
1022
 
979
1023
 
980
1024
  async def identify_matching_build(
@@ -985,6 +1029,7 @@ async def identify_matching_build(
985
1029
  path_hashes: PathHashes,
986
1030
  time_constraints: None | str,
987
1031
  next_check_sleep: int = 30,
1032
+ ignore_build_queue: bool = False,
988
1033
  args: None | Args = None,
989
1034
  ) -> None | Build:
990
1035
  """Find an existing build (finished, still running or queued) which matches our
@@ -1093,13 +1138,16 @@ async def identify_matching_build(
1093
1138
  f"Got {len(matching_builds)} InfluxDB job history entries of today, generated {len(builds)} builds to check"
1094
1139
  )
1095
1140
 
1141
+ log().info("Check for matching existing builds")
1096
1142
  # ugly code duplication incomming, rework this to a dedicated function
1097
1143
  for build in list(builds.values()):
1098
1144
  if meets_constraints(build, params, time_constraints, path_hashes):
1099
1145
  log().info("found matching (may finished) build: %s (%s)", build.number, build.url)
1100
1146
  return build
1147
+ log().info("No matching builds found in the InfluxDB")
1101
1148
 
1102
- if hasattr(args, "ignore_build_queue") and not args.ignore_build_queue:
1149
+ log().info("Check for matching queue items via Jenkins API")
1150
+ if not ignore_build_queue:
1103
1151
  if matching_item := await find_matching_queue_item(
1104
1152
  jenkins_client=jenkins_client,
1105
1153
  job=job,
@@ -1107,33 +1155,40 @@ async def identify_matching_build(
1107
1155
  path_hashes=path_hashes,
1108
1156
  next_check_sleep=next_check_sleep,
1109
1157
  ):
1110
- log().debug("Found matching queued item %s", matching_item)
1158
+ log().info("Found matching queued item %s", matching_item)
1111
1159
  return await jenkins_client.build_info(job.path, matching_item)
1160
+ log().info("No matching queue items found via Jenkins API")
1112
1161
 
1113
1162
  # exit here with no matching result if
1114
1163
  # - the InfluxDB connection was a success
1115
1164
  # - and some data was found by the query
1116
1165
  # otherwise fall back to the old Jenkins job history crawling
1117
1166
  if influx_client.health().status == "pass" and matching_builds:
1167
+ log().info("Return None and schedule a new build")
1118
1168
  return None
1119
1169
 
1120
- log().debug("Start finding matching build via Jenkins API")
1170
+ log().info("Start finding matching build via Jenkins API")
1121
1171
  # fetch a job's build history first
1122
1172
  await job.expand(jenkins_client)
1123
1173
 
1174
+ log().info("Check for matching existing builds")
1124
1175
  # Look for finished builds
1125
1176
  for build in filter(lambda b: b.completed, job.build_infos.values()):
1126
1177
  if meets_constraints(build, params, time_constraints, path_hashes):
1127
1178
  log().info("found matching finished build: %s (%s)", build.number, build.url)
1128
1179
  return build
1180
+ log().info("No matching builds found via the Jenkins API")
1129
1181
 
1182
+ log().info("Check for matching non finished builds")
1130
1183
  # Look for still unfinished builds
1131
1184
  for build in filter(lambda b: not b.completed, job.build_infos.values()):
1132
1185
  if meets_constraints(build, params, time_constraints, path_hashes):
1133
1186
  log().info("found matching unfinished build: %s (%s)", build.number, build.url)
1134
1187
  return build
1188
+ log().info("No matching non finished builds found via the Jenkins API")
1135
1189
 
1136
- if args and not args.ignore_build_queue:
1190
+ log().info("Check for matching queue items via Jenkins API")
1191
+ if not ignore_build_queue:
1137
1192
  if matching_item := await find_matching_queue_item(
1138
1193
  jenkins_client=jenkins_client,
1139
1194
  job=job,
@@ -1141,9 +1196,11 @@ async def identify_matching_build(
1141
1196
  path_hashes=path_hashes,
1142
1197
  next_check_sleep=next_check_sleep,
1143
1198
  ):
1144
- log().debug("Found matching queued item %s", matching_item)
1199
+ log().info("Found matching queued item %s", matching_item)
1145
1200
  return await jenkins_client.build_info(job.path, matching_item)
1201
+ log().info("No matching queue items found via Jenkins API")
1146
1202
 
1203
+ log().info("Return None and schedule a new build")
1147
1204
  return None
1148
1205
 
1149
1206
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: UTF-8 -*-
3
3
 
4
- __version_info__ = ("2", "1", "4")
4
+ __version_info__ = ("2", "3", "0")
5
5
  __version__ = '.'.join(__version_info__)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "checkmk-dev-tools"
3
- version = "2.1.4" # will-be-updated-automatically
3
+ version = "2.3.0" # will-be-updated-automatically
4
4
  description = "Checkmk DevOps tools"
5
5
  authors = [
6
6
  "Frans Fürst <frans.fuerst@checkmk.com>",
@@ -14,7 +14,7 @@ exclude = ["cmk_dev/out"]
14
14
 
15
15
  [tool.poetry.urls]
16
16
  Repository = "https://github.com/Checkmk/checkmk-dev-tools"
17
- Changelog = "https://github.com/Checkmk/checkmk-dev-tools/blob/release/2.1.4/changelog.md"
17
+ Changelog = "https://github.com/Checkmk/checkmk-dev-tools/blob/release/2.3.0/changelog.md"
18
18
 
19
19
  [tool.poetry.scripts]
20
20
  activity-from-fs = 'cmk_dev.activity_from_fs:main'