Skip to content

Commit 20fee64

Browse files
committed
Merge remote-tracking branch 'origin/master' into Ticket8402_Jenkins_job_to_check_the_status_of_Instrument_config_dir
2 parents dc1e4dd + 22af7df commit 20fee64

File tree

10 files changed

+143
-262
lines changed

10 files changed

+143
-262
lines changed

.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ UPSTREAM_BRANCH_CONFIG=epics
33

44
WORKSPACE=temporary_workspace
55

6-
SSH_CREDENTIALS_USR=***REMOVED***
7-
SSH_CREDENTIALS_PSW=***REMOVED***
6+
SSH_CREDENTIALS_USR=username
7+
SSH_CREDENTIALS_PSW=password
88

99
USE_TEST_INSTRUMENT_LIST=false
1010
TEST_INSTRUMENT_LIST=NDXSCIDEMO

Jenkinsfile_epics_dir

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pipeline {
1919
DEBUG_MODE = "${DEBUG_MODE}"
2020
REPO_DIR = "C:\\Instrument\\Apps\\EPICS\\"
2121
UPSTREAM_BRANCH_CONFIG = "epics"
22+
SHOW_UNCOMMITTED_CHANGES_MESSAGES="false"
2223
}
2324

2425
stages {

hotfix_checker.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Creates a RepoChecker object and calls the check_instruments method to check for changes in the instruments repository."""
2+
23
import os
34

45
from dotenv import find_dotenv, load_dotenv
@@ -14,12 +15,14 @@
1415
if __name__ == "__main__":
1516
if os.environ["DEBUG_MODE"] == "true":
1617
print("INFO: Running in debug mode")
17-
print(f"DEBUG: REPO_DIR: {os.environ['REPO_DIR']}")
18-
print(f"DEBUG: UPSTREAM_BRANCH: {os.environ['UPSTREAM_BRANCH_CONFIG']}")
19-
print(f"DEBUG: ARTEFACT_DIR: {os.environ['WORKSPACE']}")
20-
print(f"DEBUG: USE_TEST_INSTRUMENT_LIST: {os.environ['USE_TEST_INSTRUMENT_LIST']}")
21-
print(f"DEBUG: TEST_INSTRUMENT_LIST: {os.environ['TEST_INSTRUMENT_LIST']}")
22-
print(f"DEBUG: DEBUG_MODE: {os.environ['DEBUG_MODE']}")
18+
print(f"INFO: REPO_DIR: {os.environ['REPO_DIR']}")
19+
print(f"INFO: UPSTREAM_BRANCH: {os.environ['UPSTREAM_BRANCH_CONFIG']}")
20+
print(f"INFO: ARTEFACT_DIR: {os.environ['WORKSPACE']}")
21+
print(
22+
f"INFO: USE_TEST_INSTRUMENT_LIST: {os.environ['USE_TEST_INSTRUMENT_LIST']}"
23+
)
24+
print(f"INFO: TEST_INSTRUMENT_LIST: {os.environ['TEST_INSTRUMENT_LIST']}")
25+
print(f"INFO: DEBUG_MODE: {os.environ['DEBUG_MODE']}")
2326

2427
repo_checker = RepoChecker()
2528
repo_checker.check_instruments()

utils/communication_utils/channel_access.py

Lines changed: 5 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Module containing utility methods for interacting with a PV."""
2+
23
import binascii
34
import json
45
import zlib
@@ -8,6 +9,7 @@
89
from enum import (
910
Enum,
1011
)
12+
from typing import Dict, Any
1113

1214
from genie_python.channel_access_exceptions import (
1315
ReadAccessException,
@@ -34,7 +36,7 @@ def __init__(
3436
def get_value(
3537
self,
3638
pv,
37-
):
39+
) -> str | dict | None:
3840
"""Gets the value of the PV. Returns None if PV is unavailable.
3941
:return: The PV value as a string, or None if there was an error.
4042
"""
@@ -55,7 +57,7 @@ def get_value(
5557
@staticmethod
5658
def _dehex_and_decompress(
5759
data,
58-
):
60+
) -> bytes:
5961
"""Converts the raw data from a PV to a decompressed string.
6062
:param data: The raw data from the PV. It is a string of numbers representing the bytes of the raw data of the
6163
PV.
@@ -65,7 +67,7 @@ def _dehex_and_decompress(
6567

6668
def get_inst_list(
6769
self,
68-
):
70+
) -> Dict | Any:
6971
"""Gets a list with all instruments running on IBEX from CS:INSTLIST.
7072
:return: a list of strings of instrument names.
7173
"""
@@ -74,80 +76,6 @@ def get_inst_list(
7476
{} if pv_value is None else json.loads(self._dehex_and_decompress(pv_value))
7577
)
7678

77-
def get_interesting_pvs(
78-
self,
79-
):
80-
"""Returns all PVs with high or medium interest status from the corresponding instrument PV. The
81-
instrument for which it returns the list depends on the prefix assigned to this class, which needs to be in the
82-
format IN:NAME_OF_INSTRUMENT.
83-
:return: A python set with the names of all the PVs with a high or medium interest status.
84-
"""
85-
all_interesting_pvs = (
86-
self._get_pvs_by_interesting_level(PvInterestingLevel.HIGH)
87-
+ self._get_pvs_by_interesting_level(PvInterestingLevel.MEDIUM)
88-
+ self._get_pvs_by_interesting_level(PvInterestingLevel.LOW)
89-
+ self._get_pvs_by_interesting_level(PvInterestingLevel.FACILITY)
90-
)
91-
interesting_pvs = {pv for pv in all_interesting_pvs}
92-
93-
return interesting_pvs
94-
95-
def _get_pvs_by_interesting_level(
96-
self,
97-
interesting_level,
98-
):
99-
"""Returns the list of all PVs with the specified interesting level from the corresponding instrument PV. The
100-
instrument for which it returns the list depends on the prefix assigned to this class, which needs to be in the
101-
format IN:NAME_OF_INSTRUMENT.
102-
:param interesting_level An enum type representing the interesting level of a PV.
103-
:return: A python list with the names of all the PVs with the specified interesting level.
104-
"""
105-
pv_value = self.get_value(
106-
"CS:BLOCKSERVER:PVS:INTEREST:" + interesting_level.value
107-
)
108-
if pv_value is None:
109-
return []
110-
else:
111-
interesting_pvs = json.loads(self._dehex_and_decompress(pv_value))
112-
pv_names = [pv[0] for pv in interesting_pvs]
113-
114-
return pv_names
115-
116-
def get_valid_iocs(
117-
self,
118-
):
119-
"""Gets the names of all valid IOCS from the PV of IOCs of the instrument.
120-
:return: a list of strings representing IOC names.
121-
"""
122-
pv_value = self.get_value("CS:BLOCKSERVER:IOCS")
123-
return (
124-
None
125-
if pv_value is None
126-
else json.loads(self._dehex_and_decompress(pv_value)).keys()
127-
)
128-
129-
def get_protected_iocs(
130-
self,
131-
):
132-
"""Gets the names of all protected IOCS from the PV of IOCs of the instrument. Protected IOCs are IOCs that a user
133-
is not allowed to stop.
134-
:return: a list of strings representing IOC names.
135-
"""
136-
pv_value = self.get_value("CS:BLOCKSERVER:IOCS_NOT_TO_STOP")
137-
return (
138-
None
139-
if pv_value is None
140-
else json.loads(self._dehex_and_decompress(pv_value))
141-
)
142-
143-
def get_version_string(
144-
self,
145-
):
146-
"""Gets the version of IBEX server running.
147-
:return: a compressed and hex encoded string representing the version.
148-
"""
149-
return self.get_value("CS:VERSION:SVN:REV")
150-
15179

15280
class PvInterestingLevel(Enum):
15381
"""Enumerated type representing the possible interesting levels a PV can have."""

utils/communication_utils/ssh_access.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""This module provides utilities for SSH access."""
2+
from typing import Dict
3+
24
import paramiko
35

46
SSH_PORT = 22
@@ -8,12 +10,12 @@ class SSHAccessUtils(object):
810
"""Class containing utility methods for SSH access."""
911

1012
@staticmethod
11-
def run_ssh_commandd(
12-
host : str,
13-
username : str,
14-
password : str,
15-
command : str,
16-
):
13+
def run_ssh_command(
14+
host: str,
15+
username: str,
16+
password: str,
17+
command: str,
18+
) -> Dict[str, bool | str]:
1719
"""Run a command on a remote host using SSH.
1820
1921
Args:

utils/hotfix_utils/InstrumentChecker.py

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""A module for checking the status of an instrument in relation to it's repo."""
2+
23
import os
3-
from typing import Union
4+
from typing import List, Tuple, Union
45

56
from ..communication_utils.ssh_access import (
67
SSHAccessUtils,
@@ -30,6 +31,7 @@ def __init__(self, hostname: str) -> None:
3031
self._commits_upstream_not_on_local_enum_messages = None
3132

3233
self._uncommitted_changes_enum = None
34+
self._uncommitted_changes_messages = None
3335

3436
@property
3537
def hostname(self) -> str:
@@ -41,7 +43,7 @@ def hostname(self) -> str:
4143
"""
4244
return self._hostname
4345

44-
def check_for_uncommitted_changes(self) -> CHECK:
46+
def check_for_uncommitted_changes(self) -> Tuple[CHECK, List[any]]:
4547
"""Check if there are any uncommitted changes on the instrument via SSH.
4648
4749
Args:
@@ -52,7 +54,7 @@ def check_for_uncommitted_changes(self) -> CHECK:
5254
5355
"""
5456
command = f"cd {self.repo_dir} && git status --porcelain"
55-
ssh_process = SSHAccessUtils.run_ssh_commandd(
57+
ssh_process = SSHAccessUtils.run_ssh_command(
5658
self.hostname,
5759
os.environ["SSH_CREDENTIALS_USR"],
5860
os.environ["SSH_CREDENTIALS_PSW"],
@@ -62,20 +64,20 @@ def check_for_uncommitted_changes(self) -> CHECK:
6264
if os.environ["DEBUG_MODE"] == "true":
6365
print(f"DEBUG: Running command {command}")
6466

65-
# if os.environ["DEBUG_MODE"] == "true":
66-
# print(f"DEBUG: {ssh_process}")
67-
6867
if ssh_process["success"]:
6968
status = ssh_process["output"]
7069

7170
JenkinsUtils.save_git_status(self.hostname, status, os.environ["WORKSPACE"])
7271

73-
if status.strip() != "":
74-
return CHECK.TRUE
72+
status_stripped = status.strip()
73+
if status_stripped != "" and os.environ["SHOW_UNCOMMITTED_CHANGES_MESSAGES"] == "true":
74+
return CHECK.TRUE, status_stripped.split("\n")
75+
elif status_stripped != "":
76+
return CHECK.TRUE, []
7577
else:
76-
return CHECK.FALSE
78+
return CHECK.FALSE, []
7779
else:
78-
return CHECK.UNDETERMINABLE
80+
return CHECK.UNDETERMINABLE, []
7981

8082
def get_parent_epics_branch(
8183
self,
@@ -91,7 +93,7 @@ def get_parent_epics_branch(
9193
9294
"""
9395
command = f"cd {self.repo_dir} && git log"
94-
ssh_process = SSHAccessUtils.run_ssh_commandd(
96+
ssh_process = SSHAccessUtils.run_ssh_command(
9597
hostname,
9698
os.environ["SSH_CREDENTIALS_USR"],
9799
os.environ["SSH_CREDENTIALS_PSW"],
@@ -131,9 +133,9 @@ def git_branch_comparer(
131133
else:
132134
branch_details = ""
133135

134-
# fetch latest changes from the remote
136+
# Fetch latest changes from the remote, NOT PULL
135137
fetch_command = f"cd {self.repo_dir} && git fetch origin"
136-
ssh_process_fetch = SSHAccessUtils.run_ssh_commandd(
138+
ssh_process_fetch = SSHAccessUtils.run_ssh_command(
137139
hostname,
138140
os.environ["SSH_CREDENTIALS_USR"],
139141
os.environ["SSH_CREDENTIALS_PSW"],
@@ -143,29 +145,24 @@ def git_branch_comparer(
143145
if os.environ["DEBUG_MODE"] == "true":
144146
print(f"DEBUG: Running command {fetch_command}")
145147

146-
# if os.environ["DEBUG_MODE"] == "true":
147-
# print(f"DEBUG: {ssh_process_fetch}")
148-
149148
if not ssh_process_fetch["success"]:
150149
return (
151150
CHECK.UNDETERMINABLE,
152151
None,
153152
)
154153

155154
command = f'cd {self.repo_dir} && git log --format="%h %s" {branch_details}'
155+
156156
if os.environ["DEBUG_MODE"] == "true":
157157
print(f"DEBUG: Running command {command}")
158158

159-
ssh_process = SSHAccessUtils.run_ssh_commandd(
159+
ssh_process = SSHAccessUtils.run_ssh_command(
160160
hostname,
161161
os.environ["SSH_CREDENTIALS_USR"],
162162
os.environ["SSH_CREDENTIALS_PSW"],
163163
command,
164164
)
165165

166-
# if os.environ["DEBUG_MODE"] == "true":
167-
# print(f"DEBUG: {ssh_process}")
168-
169166
if ssh_process["success"]:
170167
output = ssh_process["output"]
171168
commit_dict = self.split_git_log(output, prefix)
@@ -210,8 +207,7 @@ def split_git_log(self, git_log: str, prefix: str) -> dict:
210207
if prefix is None or message.startswith(prefix):
211208
commit_dict[hash] = message
212209
return commit_dict
213-
214-
# TODO: Improve this function to allow selecting of what checks to run and combine the uncommited one to this function
210+
215211
def check_instrument(self) -> dict:
216212
"""Check if there are any hotfixes or uncommitted changes on AN instrument.
217213
@@ -222,9 +218,8 @@ def check_instrument(self) -> dict:
222218
# Examples of how to use the git_branch_comparer function decided to not be used in this iteration of the check
223219
# Check if any hotfixes run on the instrument with the prefix "Hotfix:"
224220
# hotfix_commits_enum, hotfix_commits_messages = git_branch_comparer(
225-
# hostname, prefix="Hotfix:")
221+
# hostname, local_branch, upstream_branch, prefix="Hotfix:")
226222

227-
# Check if any unpushed commits run on the instrument
228223
upstream_branch = None
229224
if os.environ["UPSTREAM_BRANCH_CONFIG"] == "hostname":
230225
upstream_branch = "origin/" + self.hostname
@@ -239,7 +234,7 @@ def check_instrument(self) -> dict:
239234
# if the UPSTREAM_BRANCH_CONFIG is not set to any of the above, set it to the value of the environment variable assuming user wants custom branch
240235
upstream_branch = os.environ["UPSTREAM_BRANCH_CONFIG"]
241236

242-
# Check if any upstream commits are not on the instrument, default to the parent origin branch, either main or galil-old
237+
# Check if any commits on upstream that are not on the local branch
243238
(
244239
self.commits_upstream_not_on_local_enum,
245240
self.commits_upstream_not_on_local_messages,
@@ -250,30 +245,19 @@ def check_instrument(self) -> dict:
250245
prefix=None,
251246
)
252247

253-
# print(self.commits_upstream_not_on_local_enum, self.commits_upstream_not_on_local_messages)
254-
248+
# Check if any commits on local branch that are not on the upstream
255249
(
256250
self.commits_local_not_on_upstream_enum,
257251
self.commits_local_not_on_upstream_messages,
258252
) = self.git_branch_comparer(
259253
self.hostname,
260254
changes_on="HEAD",
261-
# subtracted_against="origin/" + self.hostname,
262-
subtracted_against=upstream_branch, # for inst scripts repo
255+
subtracted_against=upstream_branch,
263256
prefix=None,
264257
)
265258

266-
# Check if any uncommitted changes run on the instrument
267-
self.uncommitted_changes_enum = self.check_for_uncommitted_changes()
268-
269-
# # return the result of the checks
270-
# instrument_status = {
271-
# "commits_not_pushed_messages": commits_local_not_on_upstream_messages,
272-
# "commits_not_pushed": commits_local_not_on_upstream_enum,
273-
# "uncommitted_changes": uncommitted_changes_enum,
274-
# }
275-
276-
# return instrument_status
259+
# Check if any uncommitted changes are on the instrument
260+
self.uncommitted_changes_enum, self.uncommitted_changes_messages = self.check_for_uncommitted_changes()
277261

278262
def as_string(self) -> str:
279263
"""Return the Instrument object as a string.

0 commit comments

Comments
 (0)