snapctl 0.48.1__py3-none-any.whl → 0.49.1__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.

Potentially problematic release.


This version of snapctl might be problematic. Click here for more details.

@@ -11,10 +11,10 @@ import subprocess
11
11
  import platform as sys_platform
12
12
  from sys import platform
13
13
  from typing import Union, List
14
+ import importlib.resources as pkg_resources
14
15
  import requests
15
16
  from requests.exceptions import RequestException
16
17
  import yaml
17
-
18
18
  from rich.progress import Progress, SpinnerColumn, TextColumn
19
19
  from snapctl.commands.snapend import Snapend
20
20
  from snapctl.config.constants import SERVER_CALL_TIMEOUT
@@ -29,10 +29,11 @@ from snapctl.config.constants import HTTP_ERROR_SERVICE_VERSION_EXISTS, \
29
29
  SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, SNAPCTL_BYOSNAP_UPDATE_VERSION_SERVICE_IN_USE_ERROR, \
30
30
  SNAPCTL_BYOSNAP_UPDATE_VERSION_TAG_ERROR, SNAPCTL_BYOSNAP_NOT_FOUND, \
31
31
  HTTP_ERROR_RESOURCE_NOT_FOUND, SNAPCTL_BYOSNAP_PUBLISH_ERROR, \
32
- SNAPCTL_BYOSNAP_GENERATE_PROFILE_ERROR, SNAPCTL_CONFIGURATION_INCORRECT, SNAPCTL_BYOSNAP_SWAGGER_ERROR
32
+ SNAPCTL_BYOSNAP_GENERATE_PROFILE_ERROR, SNAPCTL_CONFIGURATION_INCORRECT
33
33
  from snapctl.utils.echo import info, warning, success
34
34
  from snapctl.utils.helper import get_composite_token, snapctl_error, snapctl_success, \
35
35
  check_dockerfile_architecture, check_use_containerd_snapshotter
36
+ import snapctl.data.profiles
36
37
 
37
38
 
38
39
  class ByoSnap:
@@ -52,8 +53,8 @@ class ByoSnap:
52
53
  TO_DEPRECATE_SUBCOMMANDS = [
53
54
  'create', 'publish-image', 'publish-version', 'update-version']
54
55
  DEFAULT_PROFILE_NAME_JSON = 'snapser-byosnap-profile.json'
56
+ DEFAULT_PROFILE_NAME_YML = 'snapser-byosnap-profile.yml'
55
57
  DEFAULT_PROFILE_NAME_YAML = 'snapser-byosnap-profile.yaml'
56
- PROFILE_FILES_PATH = 'snapctl/data/profiles/'
57
58
  PROFILE_FORMATS = ['json', 'yaml', 'yml']
58
59
  PLATFORMS = ['linux/arm64', 'linux/amd64']
59
60
  LANGUAGES = ['go', 'node', 'python', 'java', 'csharp', 'cpp', 'rust',
@@ -501,13 +502,23 @@ class ByoSnap:
501
502
  )
502
503
 
503
504
  @staticmethod
504
- def _handle_output_file(input_filepath, output_filepath) -> bool:
505
- file_written = False
506
- with open(input_filepath, 'r') as in_file, open(output_filepath, 'w') as outfile:
507
- for line in in_file:
508
- outfile.write(line)
509
- file_written = True
510
- return file_written
505
+ def _handle_output_file(resource_filename: str, output_filepath: str) -> bool:
506
+ try:
507
+ with pkg_resources.open_text(snapctl.data.profiles, resource_filename) as in_file, open(output_filepath, 'w') as outfile:
508
+ for line in in_file:
509
+ outfile.write(line)
510
+ return True
511
+ except FileNotFoundError:
512
+ print(f"[ERROR] Could not find profile file: {resource_filename}")
513
+ return False
514
+ # @staticmethod
515
+ # def _handle_output_file(input_filepath, output_filepath) -> bool:
516
+ # file_written = False
517
+ # with open(input_filepath, 'r') as in_file, open(output_filepath, 'w') as outfile:
518
+ # for line in in_file:
519
+ # outfile.write(line)
520
+ # file_written = True
521
+ # return file_written
511
522
 
512
523
  def _get_profile_contents(self) -> dict:
513
524
  """
@@ -1574,9 +1585,9 @@ class ByoSnap:
1574
1585
  file_save_path = os.path.join(
1575
1586
  os.getcwd(), self.profile_filename)
1576
1587
  extension = self.profile_filename.split('.')[-1]
1588
+ resource_filename = f"snapser-byosnap-profile.{extension}"
1577
1589
  file_written = ByoSnap._handle_output_file(
1578
- f"{ByoSnap.PROFILE_FILES_PATH}snapser-byosnap-profile.{extension}", file_save_path
1579
- )
1590
+ resource_filename, file_save_path)
1580
1591
  if file_written:
1581
1592
  snapctl_success(
1582
1593
  message="BYOSNAP Profile generation successful. " +
@@ -0,0 +1,401 @@
1
+ """
2
+ BYOWs CLI commands
3
+ """
4
+ import os
5
+ import platform
6
+ import subprocess
7
+ import shutil
8
+ import time
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Union
12
+ import threading
13
+ import requests
14
+ from requests.exceptions import RequestException, HTTPError
15
+ from rich.progress import Progress, SpinnerColumn, TextColumn
16
+ from snapctl.config.constants import SERVER_CALL_TIMEOUT, SNAPCTL_INPUT_ERROR, \
17
+ SNAPCTL_BYOWS_GENERIC_ERROR, SNAPCTL_DEPENDENCY_MISSING
18
+ from snapctl.utils.helper import snapctl_error, snapctl_success, get_dot_snapser_dir
19
+ from snapctl.utils.echo import info, warning
20
+
21
+ class Byows:
22
+ """
23
+ CLI commands exposed for a Bring your own Workstation
24
+ """
25
+ SUBCOMMANDS = ['attach']
26
+ SSH_FILE = 'id_snapser_byows_attach_ed25519'
27
+ PORT_FORWARD_TTL = 3600 * 24 * 7
28
+ BYOWS_ENV_FILE = 'byows_env_setup'
29
+
30
+ def __init__(
31
+ self, *, subcommand: str, base_url: str, base_snapend_url: str, api_key: Union[str, None],
32
+ snapend_id: Union[str, None] = None, byosnap_id: Union[str, None] = None,
33
+ http_port: Union[int, None] = None, grpc_port: Union[int, None] = None,
34
+
35
+ ) -> None:
36
+ self.subcommand: str = subcommand
37
+ self.base_url: str = base_url
38
+ self.base_snapend_url: str = base_snapend_url
39
+ self.api_key: Union[str, None] = api_key
40
+ self.snapend_id: Union[str, None] = snapend_id
41
+ self.byosnap_id: Union[str, None] = byosnap_id
42
+ self.http_port: Union[int, None] = http_port
43
+ self.grpc_port: Union[int, None] = grpc_port
44
+ self.validate_input()
45
+
46
+ def validate_input(self) -> None:
47
+ """
48
+ Validator
49
+ """
50
+ # Check API Key and Base URL
51
+ if not self.api_key or self.base_url == '':
52
+ snapctl_error(
53
+ message="Missing API Key.", code=SNAPCTL_INPUT_ERROR)
54
+ # Check subcommand
55
+ if not self.subcommand in Byows.SUBCOMMANDS:
56
+ snapctl_error(
57
+ message="Invalid command. Valid commands are " +
58
+ f"{', '.join(Byows.SUBCOMMANDS)}.",
59
+ code=SNAPCTL_INPUT_ERROR)
60
+ # Check sdk-download commands
61
+ if self.subcommand == 'attach':
62
+ if not shutil.which("ssh"):
63
+ snapctl_error("ssh is not installed or not in PATH",
64
+ SNAPCTL_DEPENDENCY_MISSING)
65
+ if self.snapend_id is None or self.snapend_id == '':
66
+ snapctl_error(
67
+ message="Missing Input --snapend-id=$your_snapend_id",
68
+ code=SNAPCTL_INPUT_ERROR)
69
+ if self.byosnap_id is None or self.byosnap_id == '':
70
+ snapctl_error(
71
+ message="Missing Input --byosnap-id=$your_byosnap_id",
72
+ code=SNAPCTL_INPUT_ERROR)
73
+ if self.http_port is None and self.grpc_port is None:
74
+ snapctl_error(
75
+ message="Missing Input. One of --http-port=$your_local_server_port or --grpc-port=$your_local_server_port is required.",
76
+ code=SNAPCTL_INPUT_ERROR)
77
+
78
+
79
+ # Static methods
80
+ @staticmethod
81
+ def stream_output(pipe):
82
+ '''
83
+ Stream the output of a subprocess to the console.
84
+ This function reads lines from the pipe and prints them to the console.
85
+ '''
86
+ for line in iter(pipe.readline, b''):
87
+ info(line.decode().rstrip())
88
+
89
+ @staticmethod
90
+ def _get_export_commands(snap_ids, port):
91
+ '''
92
+ Generate export commands for the given snap IDs and port.
93
+ '''
94
+ env_vars = []
95
+
96
+ for snap_id in snap_ids:
97
+ upper_id = snap_id.upper()
98
+ env_vars.append(f"SNAPEND_{upper_id}_GRPC_URL=localhost:{port}")
99
+ env_vars.append(
100
+ f"SNAPEND_{upper_id}_HTTP_URL=http://localhost:{port}")
101
+
102
+ system = platform.system()
103
+
104
+ if system == "Windows":
105
+ # PowerShell syntax
106
+ return "; ".join([f'$env:{env_vars[i].replace("=", " = ")}' for i in range(len(env_vars))])
107
+ else:
108
+ # Linux or macOS bash/zsh syntax
109
+ return "\n".join([f"{env_vars[i]}" for i in range(len(env_vars))])
110
+
111
+ @staticmethod
112
+ def _generate_env_file(snap_ids, port):
113
+ '''
114
+ Generate an environment file with the given snap IDs and port.
115
+ '''
116
+ env_lines = []
117
+
118
+ for snap_id in snap_ids:
119
+ upper_id = snap_id.upper()
120
+ env_lines.append(
121
+ f"export SNAPEND_{upper_id}_GRPC_URL=localhost:{port}")
122
+ env_lines.append(
123
+ f"export SNAPEND_{upper_id}_HTTP_URL=http://localhost:{port}")
124
+
125
+ system = platform.system()
126
+ filename = f"{Byows.BYOWS_ENV_FILE}.ps1" if system == "Windows" else \
127
+ f"{Byows.BYOWS_ENV_FILE}.sh"
128
+ env_file = get_dot_snapser_dir() / filename
129
+
130
+ with env_file.open("w") as f:
131
+ if system == "Windows":
132
+ for line in env_lines:
133
+ var, value = line.replace("export ", "").split("=", 1)
134
+ f.write(f'$env:{var} = "{value}"\n')
135
+ else:
136
+ for line in env_lines:
137
+ f.write(f"{line}\n")
138
+
139
+ return env_file
140
+
141
+ @staticmethod
142
+ def _format_portal_http_error(msg, http_err, response):
143
+ """
144
+ Format a portal HTTP error response for display.
145
+ Args:
146
+ http_err: The HTTPError exception.
147
+ response: The HTTP response object.
148
+ Returns:
149
+ str: A nicely formatted error message.
150
+ """
151
+ try:
152
+ # Check if the response content is JSON-like
153
+ # FIXME: The portal should always return application/json for errors, but doesn't.
154
+ if response.text.strip().startswith('{'):
155
+ error_data = response.json()
156
+ api_error_code = error_data.get(
157
+ "api_error_code", "Unknown Code")
158
+ message = error_data.get("message", "No message provided")
159
+ details = error_data.get("details", [])
160
+ if details:
161
+ details_str = "\n - " + "\n - ".join(details)
162
+ else:
163
+ details_str = ""
164
+ return (
165
+ f"Message: {msg}\n"
166
+ f"Exception: {http_err}\n"
167
+ f"Snapser Error Code: {api_error_code}\n"
168
+ f"Error: {message}{details_str}"
169
+ )
170
+ except Exception:
171
+ pass # Fallback to default error formatting if parsing fails
172
+
173
+ # Default error message if not JSON or parsing fails
174
+ return f"HTTP Error {http_err.response.status_code}: {http_err.response.reason}\nResponse: {response.text.strip()}"
175
+
176
+ # Private methods
177
+ def _setup_port_forward(
178
+ self, private_key, public_key, snapend_id, snap_id, port, reverse_port, reverse_grpc_port,
179
+ incoming_http, incoming_grpc, outgoing_http, snap_ids, ssh_connect_addr) -> bool:
180
+ '''
181
+ Setup the SSH port forward
182
+ '''
183
+ dot_snapser_dir = get_dot_snapser_dir()
184
+ id_file = dot_snapser_dir / Byows.SSH_FILE
185
+ pub_file = dot_snapser_dir / f'{Byows.SSH_FILE}.pub'
186
+
187
+ # Write public and private key files
188
+ with open(id_file, 'w') as f:
189
+ f.write(private_key)
190
+ if os.name != 'nt':
191
+ # Only chmod on Unix-like systems
192
+ os.chmod(id_file, 0o600)
193
+
194
+ # write the public key
195
+ with open(pub_file, 'w') as f:
196
+ f.write(public_key)
197
+
198
+ # Combine all the information into a single info() call
199
+ info(
200
+ f"Forwarding {self.base_snapend_url}/{snapend_id}/v1/{snap_id} -> http://localhost:{incoming_http}/*\n"
201
+ f"Your BYOSnap HTTP server should listen on: localhost:{incoming_http}\n"
202
+ f"Connect to other snaps over HTTP on: localhost:{outgoing_http}\n"
203
+ f"Set the environment variables before starting your local server:\n\n"
204
+ f"{Byows._get_export_commands(snap_ids, port)}\n\n"
205
+ f"Run the following command to set them in your session:\n\n"
206
+ f" source {Byows._generate_env_file(snap_ids, port)}\n\n"
207
+ f"Press <ctrl-c> to stop forwarding"
208
+ )
209
+
210
+ ssh_addr = ssh_connect_addr
211
+
212
+ # Extract the port from the ssh_addr if present, otherwise default to 22
213
+ if ':' in ssh_addr:
214
+ ssh_host, ssh_port = ssh_addr.split(':')
215
+ ssh_port = int(ssh_port)
216
+ else:
217
+ ssh_host = ssh_addr
218
+ ssh_port = 22
219
+
220
+ ssh_command = [
221
+ 'ssh',
222
+ '-q',
223
+ '-4', # use IPv4
224
+ '-o', 'ServerAliveInterval=60',
225
+ '-o', 'StrictHostKeyChecking=no',
226
+ '-o', 'UserKnownHostsFile=/dev/null',
227
+ '-p', str(ssh_port),
228
+ '-i', str(id_file),
229
+ '-N',
230
+ '-L', f'{outgoing_http}:localhost:{port}',
231
+ '-R', f'{reverse_port}:localhost:{incoming_http}',
232
+ ]
233
+ if incoming_grpc:
234
+ ssh_command += ['-R', f'{reverse_grpc_port}:localhost:{incoming_grpc}']
235
+ ssh_command += [ssh_host]
236
+
237
+ process = None
238
+ try:
239
+ process = subprocess.Popen(
240
+ ssh_command,
241
+ shell=False,
242
+ stdout=subprocess.PIPE,
243
+ stderr=subprocess.PIPE
244
+ )
245
+
246
+ # Give it a few seconds to report any immediate errors
247
+ timeout_seconds = 5
248
+ start_time = time.time()
249
+ while True:
250
+ if process.poll() is not None:
251
+ # Process exited early — likely an error
252
+ stderr_output = process.stderr.read().decode().strip()
253
+ if stderr_output:
254
+ info(f"[SSH Error] {stderr_output}")
255
+ else:
256
+ warning("SSH process exited unexpectedly with no output.")
257
+ return False
258
+
259
+ if time.time() - start_time > timeout_seconds:
260
+ break
261
+
262
+ time.sleep(0.2)
263
+
264
+ # Start background thread to stream live stderr
265
+ threading.Thread(target=Byows.stream_output, args=(
266
+ process.stderr,), daemon=True).start()
267
+
268
+ # Now block for the full tunnel lifetime
269
+ process.wait(timeout=Byows.PORT_FORWARD_TTL)
270
+ except KeyboardInterrupt:
271
+ process.terminate()
272
+ try:
273
+ process.wait(timeout=5)
274
+ except subprocess.TimeoutExpired:
275
+ warning('SSH process did not shut down gracefully.')
276
+
277
+ info('Shutting down port forward...')
278
+
279
+ try:
280
+ url = f"{self.base_url}/v1/snapser-api/byows/snapends/" + \
281
+ f"{self.snapend_id}/snaps/{self.byosnap_id}/enabled"
282
+ res = requests.put(
283
+ url, headers={'api-key': self.api_key}, json=False, timeout=SERVER_CALL_TIMEOUT)
284
+ res.raise_for_status()
285
+ info('Forwarding disabled')
286
+ except HTTPError as http_err:
287
+ snapctl_error(
288
+ message=Byows._format_portal_http_error(
289
+ "Unable to disable BYOWs", http_err, res),
290
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
291
+ )
292
+ return False
293
+ except RequestException as req_err:
294
+ warning(
295
+ f"Response Status Code: {res.status_code}, Body: {res.text}")
296
+ snapctl_error(
297
+ message=f"Request Exception: Unable to disable BYOWs {req_err}",
298
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
299
+ )
300
+ return False
301
+ except Exception as e:
302
+ snapctl_error(
303
+ message=f"Unexpected error occurred: {e}",
304
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
305
+ )
306
+ return False
307
+
308
+ except Exception as e:
309
+ print('Error running SSH command:', e)
310
+ return False
311
+ finally:
312
+ # Cleanup the SSH key files
313
+ # Only remove the files if they exist
314
+ try:
315
+ id_file.unlink()
316
+ pub_file.unlink()
317
+ except Exception as e:
318
+ warning(
319
+ f"Cleanup warning: Failed to delete SSH key files – {e}")
320
+ return True
321
+
322
+ # Commands
323
+ def attach(self) -> bool:
324
+ """
325
+ BYOWs port forward
326
+ """
327
+ progress = Progress(
328
+ SpinnerColumn(),
329
+ TextColumn("[progress.description]{task.description}"),
330
+ transient=True,
331
+ )
332
+ progress.start()
333
+ progress.add_task(description='Setting up BYOWs port forward...', total=None)
334
+ try:
335
+ url = f"{self.base_url}/v1/snapser-api/byows/snapends/" + \
336
+ f"{self.snapend_id}/snaps/{self.byosnap_id}"
337
+ res = requests.put(
338
+ url,
339
+ headers={'api-key': self.api_key},
340
+ json={
341
+ "snapend_id": self.snapend_id,
342
+ "snap_id": self.byosnap_id
343
+ },
344
+ timeout=SERVER_CALL_TIMEOUT
345
+ )
346
+ res.raise_for_status()
347
+ response_json = res.json()
348
+
349
+
350
+ if res.ok and 'workstationPort' in response_json and \
351
+ 'workstationReversePort' in response_json and \
352
+ 'snapendId' in response_json and \
353
+ 'proxyPrivateKey' in response_json and \
354
+ 'proxyPublicKey' in response_json:
355
+
356
+ # We do not use the workstationReversePort now, we ask the user
357
+ # to provide the port they want to use
358
+ # incoming_http_port = response_json['workstationReversePort']
359
+ incoming_http_port = self.http_port
360
+ incoming_grpc_port = self.grpc_port
361
+
362
+ outgoing_http_port = response_json['workstationPort']
363
+
364
+ progress.stop()
365
+
366
+ self._setup_port_forward(
367
+ response_json['proxyPrivateKey'],
368
+ response_json['proxyPublicKey'],
369
+ response_json['snapendId'],
370
+ response_json['snapId'],
371
+ response_json['workstationPort'],
372
+ response_json['workstationReversePort'],
373
+ response_json['workstationReverseGrpcPort'],
374
+ incoming_http_port,
375
+ incoming_grpc_port,
376
+ outgoing_http_port,
377
+ response_json['snapIds'],
378
+ response_json['sshConnectAddr'],
379
+ )
380
+ return snapctl_success(
381
+ message='complete', progress=progress)
382
+ snapctl_error(
383
+ message='Unable to setup port forward.',
384
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
385
+ except HTTPError as http_err:
386
+ snapctl_error(
387
+ message=Byows._format_portal_http_error(
388
+ "Unable to setup port forward", http_err, res),
389
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
390
+ snapctl_error(
391
+ message=f"Server Error: Unable to setup port forward {http_err}",
392
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
393
+ except RequestException as e:
394
+ snapctl_error(
395
+ message=f"Exception: Unable to setup port forward {e}",
396
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
397
+ finally:
398
+ progress.stop()
399
+ snapctl_error(
400
+ message='Failed to setup port forward.',
401
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
snapctl/commands/game.py CHANGED
@@ -40,7 +40,7 @@ class Game:
40
40
  # Check subcommand
41
41
  if not self.subcommand in Game.SUBCOMMANDS:
42
42
  snapctl_error(
43
- message="Invalid command. Valid commands are" +
43
+ message="Invalid command. Valid commands are " +
44
44
  f"{', '.join(Game.SUBCOMMANDS)}.",
45
45
  code=SNAPCTL_INPUT_ERROR)
46
46
  # Check sdk-download commands
@@ -1,8 +1,9 @@
1
1
  """
2
- Release Notes Command
2
+ Release Notes
3
3
  """
4
- import os
5
4
  from typing import Union
5
+ import importlib.resources as pkg_resources
6
+ import snapctl.data.releases # must have __init__.py under releases
6
7
  from snapctl.config.constants import SNAPCTL_INPUT_ERROR
7
8
  from snapctl.utils.helper import snapctl_error, snapctl_success
8
9
 
@@ -12,7 +13,6 @@ class ReleaseNotes:
12
13
  Release Notes Command
13
14
  """
14
15
  SUBCOMMANDS = ["releases", "show"]
15
- RELEASES_PATH = 'snapctl/data/releases'
16
16
 
17
17
  def __init__(self, *, subcommand: str, version: Union[str, None] = None) -> None:
18
18
  self.subcommand = subcommand
@@ -29,19 +29,15 @@ class ReleaseNotes:
29
29
  f"{', '.join(ReleaseNotes.SUBCOMMANDS)}.",
30
30
  code=SNAPCTL_INPUT_ERROR)
31
31
 
32
- # Upper echelon commands
33
32
  def releases(self) -> None:
34
33
  """
35
34
  List versions
36
35
  """
37
- # List all files and directories in the specified path
38
- files_and_directories = os.listdir(ReleaseNotes.RELEASES_PATH)
39
-
40
- # Print only files, excluding subdirectories
41
36
  print('== Releases ' + '=' * (92))
42
- for item in files_and_directories:
43
- if os.path.isfile(os.path.join(ReleaseNotes.RELEASES_PATH, item)):
44
- print(item.replace('.mdx', '').replace('.md', ''))
37
+ # List all resource files in snapctl.data.releases
38
+ for resource in pkg_resources.contents(snapctl.data.releases):
39
+ if resource.endswith('.mdx'):
40
+ print(resource.replace('.mdx', '').replace('.md', ''))
45
41
  print('=' * (104))
46
42
  snapctl_success(message="List versions")
47
43
 
@@ -49,17 +45,15 @@ class ReleaseNotes:
49
45
  """
50
46
  Show version
51
47
  """
52
- # Check if the specified version exists
53
- version_file = os.path.join(
54
- ReleaseNotes.RELEASES_PATH, f'{self.version}.mdx')
55
- if not os.path.isfile(version_file):
48
+ version_filename = f"{self.version}.mdx"
49
+
50
+ if version_filename not in pkg_resources.contents(snapctl.data.releases):
56
51
  snapctl_error(
57
52
  message=f"Version {self.version} does not exist.",
58
53
  code=SNAPCTL_INPUT_ERROR)
59
54
 
60
- # Read the contents of the specified version file
61
- print('== Releases Notes ' + '=' * (86))
62
- with open(version_file, 'r') as file:
55
+ print('== Release Notes ' + '=' * (86))
56
+ with pkg_resources.open_text(snapctl.data.releases, version_filename) as file:
63
57
  print(file.read())
64
58
  print('=' * (104))
65
59
  snapctl_success(message=f"Show version {self.version}")
@@ -37,7 +37,9 @@ class Snapend:
37
37
  DOWNLOAD_CATEGORY = [
38
38
  'sdk', 'protos', 'snapend-manifest'
39
39
  ]
40
- CATEGORY_TYPE_SDK = ['user', 'server', 'internal', 'app']
40
+ CATEGORY_TYPE_SDK = [
41
+ # 'omni',
42
+ 'user', 'api-key', 'internal', 'app']
41
43
  CATEGORY_TYPE_PROTOS = ['messages', 'services']
42
44
  # CATEGORY_TYPE_HTTP_LIB_FORMATS = ['unity', 'web-ts', 'ts']
43
45
 
@@ -412,26 +414,26 @@ class Snapend:
412
414
  )
413
415
  # Special cases for client SDKs
414
416
  if self.category_format in CLIENT_SDK_TYPES:
415
- if self.category_type in ['server', 'app']:
417
+ if self.category_type in ['api-key', 'app', 'internal']:
416
418
  snapctl_error(
417
419
  message="Invalid combination of format and type. " +
418
420
  ", ".join(CLIENT_SDK_TYPES.keys()) +
421
+ # " SDKs are only available for --type=omni or --type=user",
419
422
  " SDKs are only available for --type=user",
420
423
  code=SNAPCTL_INPUT_ERROR
421
424
  )
422
- if self.category_type == 'internal':
423
- snapctl_error(
424
- message="Internal access type is not supported for " +
425
- ", ".join(CLIENT_SDK_TYPES.keys()) + " SDKs.",
426
- code=SNAPCTL_INPUT_ERROR
427
- )
425
+ # if self.category_type == 'internal':
426
+ # snapctl_error(
427
+ # message="Internal access type is not supported for " +
428
+ # ", ".join(CLIENT_SDK_TYPES.keys()) + " SDKs.",
429
+ # code=SNAPCTL_INPUT_ERROR
430
+ # )
428
431
  if self.category_http_lib:
429
432
  # First check if the format supports http-lib
430
433
  if self.category_format in Snapend.get_formats_supporting_http_lib():
431
434
  # Check if the http-lib is supported for the format
432
435
  valid_http_libs = SDK_TYPES[self.category_format]['http-lib']
433
436
  if self.category_http_lib not in valid_http_libs:
434
- print('Caught you')
435
437
  snapctl_error(
436
438
  message="Invalid HTTP Library. Valid libraries are " +
437
439
  Snapend.get_http_formats_str(),
@@ -3,7 +3,7 @@ Constants used by snapctl
3
3
  """
4
4
  COMPANY_NAME = 'Snapser'
5
5
  VERSION_PREFIX = 'beta-'
6
- VERSION = '0.48.1'
6
+ VERSION = '0.49.1'
7
7
  CONFIG_FILE_MAC = '~/.snapser/config'
8
8
  CONFIG_FILE_WIN = '%homepath%\\.snapser\\config'
9
9
 
@@ -50,7 +50,7 @@ SNAPCTL_BYOGS_PUBLISH_ERROR = 25
50
50
  SNAPCTL_BYOGS_PUBLISH_PERMISSION_ERROR = 26
51
51
  SNAPCTL_BYOGS_PUBLISH_DUPLICATE_TAG_ERROR = 27
52
52
 
53
- # BYOSNAP Errors - 30 - 49 then 86 - 99
53
+ # BYOSNAP Errors - 30 - 49 then 86 - 90
54
54
  SNAPCTL_BYOSNAP_NOT_FOUND = SNAPCTL_RESOURCE_NOT_FOUND
55
55
  SNAPCTL_BYOSNAP_GENERIC_ERROR = 30
56
56
  SNAPCTL_BYOSNAP_DEPENDENCY_MISSING = 31
@@ -108,3 +108,6 @@ SNAPCTL_SNAPEND_APPLY_MANIFEST_MISMATCH_ERROR = 76
108
108
  # Generate Errors - 80 - 85
109
109
  SNAPCTL_GENERATE_GENERIC_ERROR = 80
110
110
  SNAPCTL_GENERATE_CREDENTIALS_ERROR = 81
111
+
112
+ # BYOWs Errors - 95 - 99
113
+ SNAPCTL_BYOWS_GENERIC_ERROR = 95
@@ -9,3 +9,8 @@ END_POINTS: Dict[str, str] = {
9
9
  'PLAYTEST': 'https://gateway.dev.snapser.io/playtest',
10
10
  'PROD': 'https://gateway.snapser.com/snapser'
11
11
  }
12
+
13
+ GATEWAY_END_POINTS: Dict[str, str] = {
14
+ 'SANDBOX': 'https://gateway.dev.snapser.io',
15
+ 'LIVE': 'https://gateway-accel.snapser.com',
16
+ }
snapctl/config/hashes.py CHANGED
@@ -189,11 +189,15 @@ SERVICE_IDS: List[str] = [
189
189
  ]
190
190
 
191
191
  SDK_ACCESS_AUTH_TYPE_LOOKUP: Dict[str, Dict[str, str]] = {
192
+ # 'omni': {
193
+ # 'access_type': 'external',
194
+ # 'auth_type': 'omni'
195
+ # },
192
196
  'user': {
193
197
  'access_type': 'external',
194
198
  'auth_type': 'user'
195
199
  },
196
- 'server': {
200
+ 'api-key': {
197
201
  'access_type': 'external',
198
202
  'auth_type': 'api-key'
199
203
  },
File without changes
File without changes
File without changes
@@ -0,0 +1,9 @@
1
+ ## beta-0.49.0
2
+ ##### May 5, 2025
3
+
4
+ ### Breaking Change
5
+ 1. Renamed SDK type `server` to `api-key` to be consistent with the Snapser Web app.
6
+
7
+ ### Bug Fixes
8
+ 1. The `snapctl byosnap generate-profile` command was not working when outside the root snapctl folder. This is now fixed.
9
+ 2. The `snapctl release-notes` commands were not working when outside the root snapctl folder. This is now fixed.