Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-10 08:38:57

0001 import distutils
0002 import getpass
0003 import glob
0004 import grp
0005 import json
0006 import os
0007 import pwd
0008 import re
0009 import socket
0010 import stat
0011 import subprocess
0012 import sys
0013 import sysconfig
0014 
0015 import requests
0016 from hatchling.builders.hooks.plugin.interface import BuildHookInterface
0017 
0018 PACKAGE_EMOJI_PANDA = ":panda_face:"
0019 PACKAGE_EMOJI_JEDI = ":lightsaber:"
0020 PACKAGE_NAME = "panda-server"
0021 
0022 
0023 def get_user():
0024     # Run the 'klist' command and capture its output
0025     result = subprocess.run(["klist"], capture_output=True, text=True)
0026 
0027     # Filter the lines containing 'Default principal' and extract the last field
0028     for line in result.stdout.splitlines():
0029         if "Default principal" in line:
0030             # Split the line by spaces and get the last element (field)
0031             default_principal = line.split()[-1]
0032             default_principal = default_principal.split("@")[0]
0033             return default_principal
0034 
0035     return ""
0036 
0037 
0038 def get_repo_info() -> object:
0039     # Get the current remote URL of the repository
0040     repo_url = subprocess.check_output(["git", "config", "--get", "remote.origin.url"]).strip().decode()
0041 
0042     # Get the repo and branch name
0043     match = re.match(r"https://github.com/(.*).git@(.*)", repo_url)
0044 
0045     if match:
0046         repo_name = match.group(1)
0047         branch_name = match.group(2)
0048     else:
0049         repo_name = repo_url.removesuffix(".git")
0050         branch_name = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).strip().decode()
0051         if branch_name in ("HEAD", "main", "master"):
0052             try:
0053                 # Use the account name as branch name if the repository is a fork from the original project
0054                 account_name = repo_url.split("/")[-2]
0055                 if account_name != "PanDAWMS":
0056                     branch_name = account_name
0057             except Exception:
0058                 pass
0059     # Commit hash
0060     commit_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode()
0061 
0062     return repo_name, branch_name, commit_hash
0063 
0064 
0065 def mm_notification():
0066     # Environment variable to check if we should silence the notification
0067     if os.environ.get("DISABLE_MM"):
0068         return
0069 
0070     # Get user that is running the upgrade
0071     user = get_user()
0072 
0073     # Get repository information
0074     repo_name, branch_name, commit_hash = get_repo_info()
0075 
0076     # Get Server Name
0077     server_name = socket.gethostname()
0078 
0079     file_path = os.path.expanduser("~/mm_webhook_url.txt")
0080     with open(file_path, "r") as file:
0081         mm_webhook_url = file.read().strip()
0082         if not mm_webhook_url:
0083             return
0084 
0085     # Determine the emoji based on the existence of the jedi config file
0086     jedi_config_path = "/etc/panda/panda_jedi.cfg"
0087     if os.path.exists(jedi_config_path):
0088         emoji_to_use = PACKAGE_EMOJI_JEDI
0089     else:
0090         emoji_to_use = PACKAGE_EMOJI_PANDA
0091 
0092     # On the repository name we enter an empty space to prevent the URLs to preview on Mattermost
0093     # We shorten the commit hash to the first seven characters, as they are usually enough to identify a commit
0094     mm_message = {
0095         "text": f"{emoji_to_use}**{PACKAGE_NAME}@{branch_name} upgrade on:** `{server_name}` by `{user}`.",
0096         "props": {
0097             "card": f"""
0098 | **Property** | **Value** |
0099 |--------------|-----------|
0100 | **Package**  | {repo_name} |
0101 | **Branch**   | [`{branch_name}`]({repo_name}/tree/{branch_name}) |
0102 | **Commit**   |  [`{commit_hash}`]({repo_name}/commit/{commit_hash}) |
0103 """
0104         },
0105     }
0106     headers = {"Content-Type": "application/json"}
0107     try:
0108         response = requests.post(mm_webhook_url, data=json.dumps(mm_message), headers=headers)
0109     except requests.exceptions.RequestException as e:
0110         pass
0111 
0112 
0113 class CustomBuildHook(BuildHookInterface):
0114     def initialize(self, version, build_data):
0115         # user
0116         if os.getgid() == 0:
0117             panda_user = "atlpan"
0118             panda_group = "zp"
0119         else:
0120             panda_user = getpass.getuser()
0121             panda_group = grp.getgrgid(os.getgid()).gr_name
0122 
0123         # parameters to be resolved
0124         self.params = {}
0125         self.params["install_dir"] = os.environ.get("PANDA_INSTALL_TARGET")
0126         if self.params["install_dir"]:
0127             # non-standard installation path
0128             self.params["install_purelib"] = self.params["install_dir"]
0129             self.params["install_scripts"] = os.path.join(self.params["install_dir"], "bin")
0130         else:
0131             self.params["install_dir"] = sys.prefix
0132             try:
0133                 # python3.2 or higher
0134                 self.params["install_purelib"] = sysconfig.get_path("purelib")
0135                 self.params["install_scripts"] = sysconfig.get_path("scripts")
0136             except Exception:
0137                 # old python
0138                 self.params["install_purelib"] = distutils.sysconfig.get_python_lib()
0139                 self.params["install_scripts"] = os.path.join(sys.prefix, "bin")
0140         for k in self.params:
0141             path = self.params[k]
0142             self.params[k] = os.path.abspath(os.path.expanduser(path))
0143 
0144         # other parameters
0145         self.params["panda_user"] = panda_user
0146         self.params["panda_group"] = panda_group
0147         self.params["python_exec_version"] = "%s.%s" % sys.version_info[:2]
0148         self.params["virtual_env"] = ""
0149         self.params["virtual_env_setup"] = ""
0150         if "VIRTUAL_ENV" in os.environ:
0151             self.params["virtual_env"] = os.environ["VIRTUAL_ENV"]
0152             self.params["virtual_env_setup"] = f"source {os.environ['VIRTUAL_ENV']}/bin/activate"
0153         elif sys.executable:
0154             venv_dir = os.path.dirname(os.path.dirname(sys.executable))
0155             py_venv_activate = os.path.join(venv_dir, "bin/activate")
0156             if os.path.exists(py_venv_activate):
0157                 self.params["virtual_env"] = venv_dir
0158                 self.params["virtual_env_setup"] = f"source {py_venv_activate}"
0159 
0160         # instantiate templates
0161         for in_f in glob.glob("./templates/**", recursive=True):
0162             if not in_f.endswith(".template"):
0163                 continue
0164             with open(in_f) as in_fh:
0165                 file_data = in_fh.read()
0166                 # replace patterns
0167                 for item in re.findall(r"@@([^@]+)@@", file_data):
0168                     if item not in self.params:
0169                         raise RuntimeError(f"unknown pattern {item} in {in_f}")
0170                     # get pattern
0171                     patt = self.params[item]
0172                     # convert to absolute path
0173                     if item.startswith("install"):
0174                         patt = os.path.abspath(patt)
0175                     # remove build/*/dump for bdist
0176                     patt = re.sub("build/[^/]+/dumb", "", patt)
0177                     # remove /var/tmp/*-buildroot for bdist_rpm
0178                     patt = re.sub("/var/tmp/.*-buildroot", "", patt)
0179                     # replace
0180                     file_data = file_data.replace(f"@@{item}@@", patt)
0181                 out_f = re.sub(r"(\.exe)*\.template$", "", in_f)
0182                 with open(out_f, "w") as out_fh:
0183                     out_fh.write(file_data)
0184                 # chmod +x
0185                 if in_f.endswith(".exe.template"):
0186                     tmp_st = os.stat(out_f)
0187                     os.chmod(out_f, tmp_st.st_mode | stat.S_IEXEC | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
0188 
0189     def finalize(self, version, build_data, artifact_path):
0190         # post install
0191         uid = pwd.getpwnam(self.params["panda_user"]).pw_uid
0192         gid = grp.getgrnam(self.params["panda_group"]).gr_gid
0193         for directory in ["/var/log/panda", "/var/log/panda/wsgisocks", "/var/log/panda/fastsocks"]:
0194             directory = self.params["virtual_env"] + directory
0195             if not os.path.exists(directory):
0196                 os.makedirs(directory)
0197                 os.chown(directory, uid, gid)
0198         if self.params["virtual_env"]:
0199             target_dir = os.path.join(self.params["virtual_env"], "etc/sysconfig")
0200             if not os.path.exists(target_dir):
0201                 os.makedirs(target_dir)
0202             target = os.path.join(target_dir, "panda_server")
0203             try:
0204                 os.symlink(os.path.join(self.params["virtual_env"], "etc/panda/panda_server.sysconfig"), target)
0205             except Exception:
0206                 pass
0207 
0208         # update the mattermost chat-ops channel
0209         try:
0210             mm_notification()
0211         except:
0212             pass