warlock-manager 2.2.5__tar.gz → 2.2.7__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 (63) hide show
  1. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/PKG-INFO +1 -1
  2. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/pyproject.toml +1 -1
  3. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_cmd.py +29 -2
  4. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/apps/base_app.py +0 -1
  5. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/cmd.py +74 -14
  6. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/download.py +6 -2
  7. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/firewall.py +12 -1
  8. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/utils.py +1 -1
  9. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/nexus/nexus.py +3 -3
  10. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/services/base_service.py +5 -5
  11. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager.egg-info/PKG-INFO +1 -1
  12. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/LICENSE +0 -0
  13. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/README.md +0 -0
  14. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/setup.cfg +0 -0
  15. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_app.py +0 -0
  16. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_base_config.py +0 -0
  17. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_base_service.py +0 -0
  18. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_cli_config.py +0 -0
  19. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_cli_formatter.py +0 -0
  20. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_config_key.py +0 -0
  21. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_ini_config.py +0 -0
  22. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_sensitive_data_filter.py +0 -0
  23. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_socket_service.py +0 -0
  24. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_unreal_config.py +0 -0
  25. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_unreal_config_ark_spawn_entities.py +0 -0
  26. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_unreal_save.py +0 -0
  27. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/tests/test_version.py +0 -0
  28. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/__init__.py +0 -0
  29. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/apps/__init__.py +0 -0
  30. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/apps/manual_app.py +0 -0
  31. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/apps/steam_app.py +0 -0
  32. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/__init__.py +0 -0
  33. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/base_config.py +0 -0
  34. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/cli_config.py +0 -0
  35. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/config_key.py +0 -0
  36. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/ini_config.py +0 -0
  37. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/json_config.py +0 -0
  38. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/properties_config.py +0 -0
  39. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/config/unreal_config.py +0 -0
  40. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/formatters/__init__.py +0 -0
  41. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/formatters/cli_formatter.py +0 -0
  42. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/__init__.py +0 -0
  43. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/app.py +0 -0
  44. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/app_runner.py +0 -0
  45. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/cache.py +0 -0
  46. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/get_wan_ip.py +0 -0
  47. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/java.py +0 -0
  48. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/meta.py +0 -0
  49. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/ports.py +0 -0
  50. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/sensitive_data_filter.py +0 -0
  51. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/tui.py +0 -0
  52. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/libs/version.py +0 -0
  53. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/mods/__init__.py +0 -0
  54. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/mods/base_mod.py +0 -0
  55. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/mods/warlock_nexus_mod.py +0 -0
  56. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/services/__init__.py +0 -0
  57. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/services/http_service.py +0 -0
  58. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/services/rcon_service.py +0 -0
  59. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager/services/socket_service.py +0 -0
  60. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager.egg-info/SOURCES.txt +0 -0
  61. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager.egg-info/dependency_links.txt +0 -0
  62. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager.egg-info/requires.txt +0 -0
  63. {warlock_manager-2.2.5 → warlock_manager-2.2.7}/warlock_manager.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warlock-manager
3
- Version: 2.2.5
3
+ Version: 2.2.7
4
4
  Summary: Dependency library for game-server management applications.
5
5
  Author-email: Charlie Powell <cdp1337@bitsnbytes.dev>
6
6
  Maintainer-email: Charlie Powell <cdp1337@bitsnbytes.dev>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "warlock-manager"
7
- version = "2.2.5"
7
+ version = "2.2.7"
8
8
  description = "Dependency library for game-server management applications."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -3,16 +3,34 @@ from warlock_manager.libs.cmd import Cmd
3
3
 
4
4
 
5
5
  class TestCmd(unittest.TestCase):
6
- def test_exists_true(self):
6
+ def test_exists(self):
7
+ """
8
+ Test if commands exist and do not exist
9
+ :return:
10
+ """
7
11
  cmd = Cmd(["echo"])
8
12
  self.assertTrue(cmd.exists)
9
13
 
10
- def test_exists_false(self):
11
14
  cmd = Cmd(["nonexistentbinary12345"])
12
15
  self.assertFalse(cmd.exists)
13
16
  self.assertFalse(cmd.success)
14
17
 
18
+ def test_exists_sudo(self):
19
+ """
20
+ Test exists functionality when used with sudo
21
+ :return:
22
+ """
23
+ cmd = Cmd(["true"]).sudo('nobody')
24
+ self.assertTrue(cmd.exists)
25
+
26
+ cmd = Cmd(["nonexistentbinary12345"]).sudo('nobody')
27
+ self.assertFalse(cmd.exists)
28
+
15
29
  def test_text(self):
30
+ """
31
+ Test that .text returns the output of the command
32
+ :return:
33
+ """
16
34
  cmd = Cmd(["echo", "hello world"])
17
35
  self.assertEqual(cmd.text, "hello world")
18
36
 
@@ -42,6 +60,15 @@ class TestCmd(unittest.TestCase):
42
60
  with self.assertRaises(Exception):
43
61
  _ = cmd.json
44
62
 
63
+ def test_cwd(self):
64
+ """
65
+ Test that the cwd is set and used correctly
66
+
67
+ :return:
68
+ """
69
+ cmd = Cmd(["pwd"]).cwd("/tmp")
70
+ self.assertEqual(cmd.text, "/tmp")
71
+
45
72
 
46
73
  if __name__ == "__main__":
47
74
  unittest.main()
@@ -88,7 +88,6 @@ class BaseApp(ABC):
88
88
  'api', # Game supports baseline API features
89
89
  'cmd', # Game supports commands sent via the API
90
90
  'create_service', # Game supports creating new services
91
- 'mods', # Game supports mods
92
91
  }
93
92
  """
94
93
  List of features available in this game
@@ -3,6 +3,7 @@ import subprocess
3
3
  import json
4
4
  import logging
5
5
  import time
6
+ import pwd
6
7
 
7
8
  from warlock_manager.libs import cache
8
9
 
@@ -39,7 +40,7 @@ class Cmd:
39
40
  CompletedProcess: The result of the command execution
40
41
  """
41
42
 
42
- self.executable: str | None = cmd[0] if len(cmd) > 0 else None
43
+ self.executable: str | None = self.cmd[0] if len(self.cmd) > 0 else None
43
44
  """
44
45
  str: The executable name of the command
45
46
  """
@@ -65,7 +66,12 @@ class Cmd:
65
66
  These commands are NOT persistent across calls!
66
67
  """
67
68
 
68
- def sudo(self, runas: str | int):
69
+ self._cwd: str | None = None
70
+ """
71
+ The current working directory for this command
72
+ """
73
+
74
+ def sudo(self, runas: str | int) -> 'Cmd':
69
75
  """
70
76
  Run this command as another user using sudo.
71
77
 
@@ -78,55 +84,73 @@ class Cmd:
78
84
  :return:
79
85
  """
80
86
  if isinstance(runas, str):
81
- if os.getlogin() == runas:
87
+ # Get the name of the user owning the current process
88
+ # use pwd instead of os.getlogin to address CI tests on 3.13
89
+ current_user = pwd.getpwuid(os.geteuid()).pw_name
90
+ if current_user == runas:
82
91
  # If we're already running as this user, no need to prefix with sudo
83
- return
92
+ return self
84
93
  prefix = ['sudo', '-u', runas]
85
94
  else:
86
95
  if os.geteuid() == runas:
87
96
  # If we're already running as this user, no need to prefix with sudo
88
- return
97
+ return self
89
98
  prefix = ['sudo', '-u', '#%s' % runas]
90
99
 
91
100
  self.cmd = prefix + self.cmd
92
101
  self.result = None
102
+ return self
93
103
 
94
- def use_stdout(self):
104
+ def use_stdout(self) -> 'Cmd':
95
105
  """
96
106
  Set this command to use stdout for output instead of stderr.
97
107
  :return:
98
108
  """
99
109
  self.uses = 'stdout'
110
+ return self
100
111
 
101
- def use_stderr(self):
112
+ def use_stderr(self) -> 'Cmd':
102
113
  """
103
114
  Set this command to use stderr for output instead of stdout.
104
115
  :return:
105
116
  """
106
117
  self.uses = 'stderr'
118
+ return self
107
119
 
108
- def stream_output(self):
120
+ def stream_output(self) -> 'Cmd':
109
121
  """
110
122
  Set this command to stream to stdout/stderr directly. Useful for long-running commands.
111
123
  :return:
112
124
  """
113
125
  self.uses = None
126
+ return self
114
127
 
115
- def is_cacheable(self, expires: int = 3600):
128
+ def is_cacheable(self, expires: int = 3600) -> 'Cmd':
116
129
  """
117
130
  Set this command as cacheable for N seconds.
118
131
  :param expires:
119
132
  :return:
120
133
  """
121
134
  self.cacheable = expires
135
+ return self
122
136
 
123
- def is_memory_cacheable(self, expires: int = 2):
137
+ def is_memory_cacheable(self, expires: int = 2) -> 'Cmd':
124
138
  """
125
139
  Set this command as cacheable in memory for N seconds.
126
140
  :param expires:
127
141
  :return:
128
142
  """
129
143
  self.memory_cacheable = expires
144
+ return self
145
+
146
+ def cwd(self, path: str | None) -> 'Cmd':
147
+ """
148
+ Set the current working directory for this command.
149
+ :param path:
150
+ :return:
151
+ """
152
+ self._cwd = path
153
+ return self
130
154
 
131
155
  @property
132
156
  def exists(self) -> bool:
@@ -230,6 +254,7 @@ class Cmd:
230
254
  self.cmd,
231
255
  capture_output=capture_output,
232
256
  check=False,
257
+ cwd=self._cwd,
233
258
  encoding='utf-8'
234
259
  )
235
260
  except FileNotFoundError as e:
@@ -265,21 +290,55 @@ class Cmd:
265
290
 
266
291
  return self.result
267
292
 
268
- def extend(self, args: list):
293
+ def extend(self, args: list) -> 'Cmd':
269
294
  """
270
295
  Extend the command with additional arguments.
271
296
  :param args:
272
297
  """
273
298
  self.cmd = self.cmd + args
274
299
  self.result = None
300
+ return self
275
301
 
276
- def append(self, arg: str):
302
+ def append(self, arg: str) -> 'Cmd':
277
303
  """
278
304
  Append a single argument to the command.
279
305
  :param arg:
280
306
  """
281
307
  self.cmd.append(arg)
282
308
  self.result = None
309
+ return self
310
+
311
+
312
+ class PipeCmd(Cmd):
313
+ """
314
+ Convenience wrapper for piping command output to a parent process
315
+ """
316
+
317
+ def run(self):
318
+ """
319
+ Run the command in the background using nohup. Caches the result so subsequent calls don't re-run the command.
320
+
321
+ :return:
322
+ """
323
+ if self.result is None:
324
+
325
+ if self.cacheable is not False:
326
+ logging.warning('Piped commands cannot be cached!')
327
+
328
+ try:
329
+ logging.debug('Running piped command: %s' % ' '.join(self.cmd))
330
+ self.result = subprocess.Popen(
331
+ self.cmd,
332
+ cwd=self._cwd,
333
+ stdout=subprocess.PIPE,
334
+ stderr=subprocess.PIPE
335
+ )
336
+ except FileNotFoundError as e:
337
+ self.result = CmdFakeResponse('', str(e), 127)
338
+ except OSError as e:
339
+ self.result = CmdFakeResponse('', str(e), 1)
340
+
341
+ return self.result
283
342
 
284
343
 
285
344
  class BackgroundCmd(Cmd):
@@ -299,11 +358,12 @@ class BackgroundCmd(Cmd):
299
358
  logging.warning('Background commands cannot be cached!')
300
359
 
301
360
  try:
361
+ logging.debug('Running background command: %s' % ' '.join(self.cmd))
302
362
  self.result = subprocess.Popen(
303
363
  self.cmd,
364
+ cwd=self._cwd,
304
365
  stdout=subprocess.DEVNULL,
305
- stderr=subprocess.DEVNULL,
306
- preexec_fn=lambda: logging.debug('Running background command: %s' % ' '.join(self.cmd))
366
+ stderr=subprocess.DEVNULL
307
367
  )
308
368
  except FileNotFoundError as e:
309
369
  self.result = CmdFakeResponse('', str(e), 127)
@@ -3,6 +3,10 @@ import requests
3
3
  import json
4
4
  from warlock_manager.libs import utils
5
5
  from warlock_manager.libs import cache
6
+ from warlock_manager.libs.meta import get_meta
7
+
8
+ # "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
9
+ USER_AGENT = 'Warlock Manager/%s (warlock.nexus) <staff@bitsnbytes.dev>' % get_meta()['version']
6
10
 
7
11
 
8
12
  def download_file(url: str, destination: str):
@@ -18,7 +22,7 @@ def download_file(url: str, destination: str):
18
22
  # Ensure the target directory exists
19
23
  utils.ensure_file_parent_exists(destination)
20
24
 
21
- response = requests.get(url, stream=True)
25
+ response = requests.get(url, stream=True, headers={'User-Agent': USER_AGENT})
22
26
  response.raise_for_status() # Check if the request was successful
23
27
 
24
28
  with open(destination, 'wb') as f:
@@ -47,7 +51,7 @@ def download_json(url: str) -> dict:
47
51
  if cached is not None:
48
52
  return json.loads(cached)
49
53
 
50
- response = requests.get(url)
54
+ response = requests.get(url, headers={'User-Agent': USER_AGENT, 'Accept': 'application/json'})
51
55
  response.raise_for_status() # Check if the request was successful
52
56
  data = response.json()
53
57
 
@@ -95,9 +95,15 @@ class Firewall:
95
95
  logging.error(f"Invalid port number: {port}")
96
96
  return False
97
97
 
98
+ if protocol.lower() not in ['tcp', 'udp']:
99
+ logging.error(f"Invalid protocol: {protocol}")
100
+ return False
101
+
98
102
  firewall = cls.get_available()
99
103
 
100
104
  if firewall == 'ufw':
105
+ # UFW requires the protocol to be all lowercase.
106
+ protocol = protocol.lower()
101
107
  logging.info(f"Allowing {port}/{protocol} via UFW")
102
108
  cmd = Cmd(['ufw', 'allow', f'{port}/{protocol}'])
103
109
  if comment:
@@ -139,6 +145,10 @@ class Firewall:
139
145
  logging.error(f"Invalid port number: {port}")
140
146
  return False
141
147
 
148
+ if protocol.lower() not in ['tcp', 'udp']:
149
+ logging.error(f"Invalid protocol: {protocol}")
150
+ return False
151
+
142
152
  firewall = cls.get_available()
143
153
 
144
154
  if firewall == 'ufw':
@@ -183,7 +193,8 @@ class Firewall:
183
193
  ufw_check = Cmd(['ufw', 'status'])
184
194
  ufw_check.is_memory_cacheable(3)
185
195
  result = ufw_check.text
186
- port_proto = f"{port}/{protocol}"
196
+ # UFW requires the protocol to be all lowercase.
197
+ port_proto = f"{port}/{protocol}".lower()
187
198
  for line in result.splitlines():
188
199
  if port_proto in line and "ALLOW" in line and ("Anywhere" in line or "Anywhere (v6)" in line):
189
200
  return True
@@ -121,7 +121,7 @@ def makedirs(target_dir: str):
121
121
  return
122
122
 
123
123
  # Ensure the directory exists within the context of either this game or the game owner's home directory
124
- if not (target_dir.startswith(get_app_directory()) or target_dir.startswith(get_home_directory())):
124
+ if not (target_dir.startswith(get_base_directory()) or target_dir.startswith(get_home_directory())):
125
125
  raise Exception('Cannot create directory outside of game directory: %s' % target_dir)
126
126
 
127
127
  # Iterate up until the parent directory exists.
@@ -8,7 +8,7 @@ import hashlib
8
8
  import secrets
9
9
  from warlock_manager.libs.cmd import Cmd
10
10
  from warlock_manager.services.base_service import BaseService
11
- from warlock_manager.libs.utils import get_app_directory
11
+ from warlock_manager.libs.utils import get_base_directory
12
12
 
13
13
 
14
14
  class Nexus:
@@ -34,7 +34,7 @@ class Nexus:
34
34
  with open(self.email_file, 'r') as f:
35
35
  self.email = f.read().strip()
36
36
 
37
- manager_meta_path = os.path.join(get_app_directory(), '.manage.json')
37
+ manager_meta_path = os.path.join(get_base_directory(), '.manage.json')
38
38
  if os.path.exists(manager_meta_path):
39
39
  with open(manager_meta_path, 'r') as f:
40
40
  meta = json.load(f)
@@ -222,7 +222,7 @@ class Nexus:
222
222
  # Only push stats for running services
223
223
  return
224
224
 
225
- last_checkin_file = os.path.join(get_app_directory(), '.cache', '.%s.checkin' % service.service)
225
+ last_checkin_file = os.path.join(get_base_directory(), '.cache', '.%s.checkin' % service.service)
226
226
  now = int(time.time())
227
227
  if os.path.exists(last_checkin_file):
228
228
  # Only push stats once every 15 minutes
@@ -1321,7 +1321,7 @@ class BaseService(ABC):
1321
1321
  logging.info('Removed environment file for %s at %s' % (self.service, self._env_file))
1322
1322
 
1323
1323
  target_dir = self.get_app_directory()
1324
- app_dir = os.path.join(self.game.get_app_directory(), 'AppFiles')
1324
+ app_dir = os.path.join(utils.get_base_directory(), 'AppFiles')
1325
1325
  if target_dir != app_dir and os.path.exists(target_dir):
1326
1326
  # Only remove app directory if it's different than the game app.
1327
1327
  # This is important because by default game instances share the same application base.
@@ -1379,7 +1379,7 @@ class BaseService(ABC):
1379
1379
 
1380
1380
  :return:
1381
1381
  """
1382
- base = self.game.get_app_directory()
1382
+ base = utils.get_base_directory()
1383
1383
  temp_store = os.path.join(base, '.save-%s' % self.service)
1384
1384
  save_source = self.get_save_directory()
1385
1385
  save_files = self.get_save_files()
@@ -1433,7 +1433,7 @@ class BaseService(ABC):
1433
1433
 
1434
1434
  :return:
1435
1435
  """
1436
- base = self.game.get_app_directory()
1436
+ base = utils.get_base_directory()
1437
1437
  temp_store = os.path.join(base, '.save-%s' % self.service)
1438
1438
  target_dir = self.get_backup_directory()
1439
1439
  base_name = self.service
@@ -1517,7 +1517,7 @@ class BaseService(ABC):
1517
1517
  print('Game server is currently running, please stop it before restoring a backup!', file=sys.stderr)
1518
1518
  return False
1519
1519
 
1520
- base = self.game.get_app_directory()
1520
+ base = utils.get_base_directory()
1521
1521
  temp_store = os.path.join(base, '.restore-%s' % self.service)
1522
1522
  save_dest = self.get_save_directory()
1523
1523
 
@@ -1567,7 +1567,7 @@ class BaseService(ABC):
1567
1567
 
1568
1568
  :return:
1569
1569
  """
1570
- base = self.game.get_app_directory()
1570
+ base = utils.get_base_directory()
1571
1571
  temp_store = os.path.join(base, '.restore-%s' % self.service)
1572
1572
 
1573
1573
  # Cleanup
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: warlock-manager
3
- Version: 2.2.5
3
+ Version: 2.2.7
4
4
  Summary: Dependency library for game-server management applications.
5
5
  Author-email: Charlie Powell <cdp1337@bitsnbytes.dev>
6
6
  Maintainer-email: Charlie Powell <cdp1337@bitsnbytes.dev>
File without changes