snapctl 1.1.0__tar.gz → 1.1.2__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.

Potentially problematic release.


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

Files changed (55) hide show
  1. {snapctl-1.1.0 → snapctl-1.1.2}/PKG-INFO +43 -9
  2. {snapctl-1.1.0 → snapctl-1.1.2}/README.md +42 -8
  3. {snapctl-1.1.0 → snapctl-1.1.2}/pyproject.toml +1 -1
  4. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/snapend_manifest.py +376 -56
  5. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/config/constants.py +3 -2
  6. snapctl-1.1.2/snapctl/data/releases/1.1.2.mdx +8 -0
  7. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/main.py +27 -3
  8. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/utils/helper.py +8 -0
  9. {snapctl-1.1.0 → snapctl-1.1.2}/LICENSE +0 -0
  10. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/__init__.py +0 -0
  11. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/__main__.py +0 -0
  12. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/__init__.py +0 -0
  13. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/application.py +0 -0
  14. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/byogs.py +0 -0
  15. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/byosnap.py +0 -0
  16. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/byows.py +0 -0
  17. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/game.py +0 -0
  18. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/generate.py +0 -0
  19. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/release_notes.py +0 -0
  20. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/snapend.py +0 -0
  21. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/commands/snaps.py +0 -0
  22. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/config/__init__.py +0 -0
  23. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/config/app.py +0 -0
  24. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/config/endpoints.py +0 -0
  25. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/config/hashes.py +0 -0
  26. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/__init__.py +0 -0
  27. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/profiles/__init__.py +0 -0
  28. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/profiles/snapser-byosnap-profile.json +0 -0
  29. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/profiles/snapser-byosnap-profile.yaml +0 -0
  30. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/profiles/snapser-byosnap-profile.yml +0 -0
  31. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/1.0.0.mdx +0 -0
  32. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/1.0.2.mdx +0 -0
  33. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/1.0.3.mdx +0 -0
  34. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/1.0.4.mdx +0 -0
  35. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/1.1.0.mdx +0 -0
  36. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/__init__.py +0 -0
  37. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.46.0.mdx +0 -0
  38. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.46.4.mdx +0 -0
  39. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.47.0.mdx +0 -0
  40. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.47.1.mdx +0 -0
  41. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.47.2.mdx +0 -0
  42. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.48.0.mdx +0 -0
  43. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.49.0.mdx +0 -0
  44. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.49.1.mdx +0 -0
  45. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.49.2.mdx +0 -0
  46. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.49.3.mdx +0 -0
  47. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.50.0.mdx +0 -0
  48. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.51.0.mdx +0 -0
  49. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/data/releases/beta-0.53.1.mdx +0 -0
  50. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/types/__init__.py +0 -0
  51. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/types/definitions.py +0 -0
  52. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/utils/__init__.py +0 -0
  53. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/utils/echo.py +0 -0
  54. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/utils/exceptions.py +0 -0
  55. {snapctl-1.1.0 → snapctl-1.1.2}/snapctl/utils/telemetry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: snapctl
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Snapser CLI Tool
5
5
  Author: Ajinkya Apte
6
6
  Author-email: aj@snapser.com
@@ -641,7 +641,9 @@ snapctl snapend-manifest --help
641
641
 
642
642
  #### 2. Snapend Manifest Create
643
643
 
644
- Create a Snapend manifest. The output manifest file can then be used with the `snapend create` or `snapend clone` commands.
644
+ Create a Snapend manifest. It should be noted that the latest versions of the snap at the time of running the command is what gets added to the manifest. For example, say the `analytics` snap has versions `v1.0.0`, `v1.1.0` available on the web portal. If you run the create command with `--snaps analytics` the `v1.1.0` which is the latest at that moment in time, gets added to your manifest.
645
+
646
+ The output manifest file can then be used with the `snapend create` or `snapend clone` commands.
645
647
 
646
648
  ```bash
647
649
  # Create a Snapend manifest file
@@ -654,15 +656,21 @@ Create a Snapend manifest. The output manifest file can then be used with the `s
654
656
  # Note: One of snaps or features is required
655
657
  # Example:
656
658
  # snapend-manifest create --name my-dev-snapend --env DEVELOPMENT --snaps auth,analytics --add-features WEB_SOCKETS --out-path ./snapend-manifest.json
657
- snapctl snapend-manifest create --name $name$ --env $env$ --snaps $snaps --features $features --out-path-filename $output_path_filename
659
+
660
+ snapctl snapend-manifest create --name $name --env $env --snaps $snaps --features $features --out-path-filename $output_path_filename
658
661
  ```
659
662
 
660
- #### 3. Snapend Manifest Update
663
+ #### 3. Snapend Manifest Sync
664
+
665
+ Sync with an existing Snapend manifest. This command is idempotent and destructive.
661
666
 
662
- Update an existing Snapend manifest.
667
+ - It ensures the input manifest gets replaced with the values you pass in `--snaps` and `--features`. For example: If your input manifest has a snap but you do not pass the snap in the `--snaps`, it will be removed from the output. Same goes for `--features`.
668
+ - Additionally, if a snap is present in the manifest and you pass it in as part of `--snaps` it will not be touched. But if a snap is not part of the manifest and you pass it in as part of `--snaps` it will be added with the latest version of the snap. For example, say the `analytics` snap has versions `v1.0.0`, `v1.1.0` available on the web portal. If you run the sync command with `--snaps analytics` and you had `v1.0.0` in the manifest then it wont be touched. However, if you did not have it in the manifest then the `v1.1.0` which is the latest at that moment in time, gets added to your manifest.
669
+
670
+ **IMPORTANT**: If you are looking for a command to purely add or remove snaps or features, consider using the `update` command instead.
663
671
 
664
672
  ```bash
665
- # Update a Snapend manifest file
673
+ # Sync a Snapend manifest file
666
674
  # $input_manifest = Path and file name of the current manifest. The filename should end with .json or .yaml
667
675
  # $output_path_filename = Path and file name to store the manifest. The filename should end with .json or .yaml
668
676
  # $snaps = Comma separated list of snap ids you want to have at the end. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
@@ -671,14 +679,14 @@ Update an existing Snapend manifest.
671
679
  # IMPORTANT: If your manifest has a feature but your --features does not. It will be removed.
672
680
  # Note: One of snaps, and features is required
673
681
  # Example:
674
- # snapctl snapend-manifest update --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
682
+ # snapctl snapend-manifest sync --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
675
683
 
676
- snapctl snapend-manifest update --manifest-path-filename $input_manifest --snaps $snaps --features $features--out-path-filename $output_path_filename
684
+ snapctl snapend-manifest sync --manifest-path-filename $input_manifest --snaps $snaps --features $features--out-path-filename $output_path_filename
677
685
  ```
678
686
 
679
687
  #### 4. Snapend Manifest Upgrade
680
688
 
681
- Upgrade all snaps or a list of snaps to the latest version
689
+ Upgrade all snaps or a list of snaps to the latest version of the snap available at that moment in time. If you do not pass in `--snaps` then all the snaps in the manifest will be upgraded. If you do pass a list of snaps then all snap Ids are expected to be valid.
682
690
 
683
691
  ```bash
684
692
  # Update a Snapend manifest file
@@ -691,9 +699,35 @@ Upgrade all snaps or a list of snaps to the latest version
691
699
  # snapctl snapend-manifest upgrade --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --snaps game-server-fleets --out-path ./snapend-updated-manifest.yaml
692
700
  # snapctl snapend-manifest upgrade --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --out-path ./snapend-updated-manifest.yaml
693
701
 
702
+ snapctl snapend-manifest upgrade --manifest-path-filename $input_manifest --out-path-filename $output_path_filename
703
+
694
704
  snapctl snapend-manifest upgrade --manifest-path-filename $input_manifest --snaps $snaps --out-path-filename $output_path_filename
695
705
  ```
696
706
 
707
+ #### 5. Snapend Manifest Update
708
+
709
+ Update your manifest. This command is additive and subtractive. It allows you to add or remove, snaps and features from an existing manifest. Unlike `sync`, this will only add or remove entities from your manifest, keeping the remaining part of the manifest intact. If your manifest has a snap and you pass it with `--add-snaps` then it will be ignored. However if you do not have a snap and you pass it with `--add-snaps` then the latest version of the snap will be added. If you pass a snap with `--remove-snaps` that is already not in the manifest, then it will be skipped. This ensures idempotency after the initial update is made to the snapend manifest.
710
+
711
+ **IMPORTANT**:
712
+ - All additions are performed before removals.
713
+ - If you are looking to have strict relation between your input (snaps + features) and your output manifest, consider using the `sync` command.
714
+
715
+ ```bash
716
+ # Update a Snapend manifest file
717
+ # $input_manifest = Path and file name of the current manifest. The filename should end with .json or .yaml
718
+ # $output_path_filename = Path and file name to store the manifest. The filename should end with .json or .yaml
719
+ # $addSnaps = Comma separated list of snap ids you want to add to your manifest. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
720
+ # $removeSnaps = Comma separated list of snap ids you want to remove from your manifest. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
721
+ # $addFeatures = Pass `WEB_SOCKETS` if you want to enable web sockets for your backend.
722
+ # $removeFeatures = Pass `WEB_SOCKETS` if you want to disable web sockets for your backend.
723
+ # Note: One of add-snaps, remove-snaps, add-features or remove-features is required
724
+ # Example:
725
+ # snapctl snapend-manifest update --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
726
+
727
+ snapctl snapend-manifest update --manifest-path-filename $input_manifest --add-snaps $addSnaps --remove-snaps $removeSnaps --add-features $addFeatures --remove-features $removeFeatures --out-path-filename $output_path_filename
728
+ ```
729
+
730
+
697
731
  ### 7. Snapend
698
732
  Snapctl commands for your snapend
699
733
 
@@ -620,7 +620,9 @@ snapctl snapend-manifest --help
620
620
 
621
621
  #### 2. Snapend Manifest Create
622
622
 
623
- Create a Snapend manifest. The output manifest file can then be used with the `snapend create` or `snapend clone` commands.
623
+ Create a Snapend manifest. It should be noted that the latest versions of the snap at the time of running the command is what gets added to the manifest. For example, say the `analytics` snap has versions `v1.0.0`, `v1.1.0` available on the web portal. If you run the create command with `--snaps analytics` the `v1.1.0` which is the latest at that moment in time, gets added to your manifest.
624
+
625
+ The output manifest file can then be used with the `snapend create` or `snapend clone` commands.
624
626
 
625
627
  ```bash
626
628
  # Create a Snapend manifest file
@@ -633,15 +635,21 @@ Create a Snapend manifest. The output manifest file can then be used with the `s
633
635
  # Note: One of snaps or features is required
634
636
  # Example:
635
637
  # snapend-manifest create --name my-dev-snapend --env DEVELOPMENT --snaps auth,analytics --add-features WEB_SOCKETS --out-path ./snapend-manifest.json
636
- snapctl snapend-manifest create --name $name$ --env $env$ --snaps $snaps --features $features --out-path-filename $output_path_filename
638
+
639
+ snapctl snapend-manifest create --name $name --env $env --snaps $snaps --features $features --out-path-filename $output_path_filename
637
640
  ```
638
641
 
639
- #### 3. Snapend Manifest Update
642
+ #### 3. Snapend Manifest Sync
643
+
644
+ Sync with an existing Snapend manifest. This command is idempotent and destructive.
640
645
 
641
- Update an existing Snapend manifest.
646
+ - It ensures the input manifest gets replaced with the values you pass in `--snaps` and `--features`. For example: If your input manifest has a snap but you do not pass the snap in the `--snaps`, it will be removed from the output. Same goes for `--features`.
647
+ - Additionally, if a snap is present in the manifest and you pass it in as part of `--snaps` it will not be touched. But if a snap is not part of the manifest and you pass it in as part of `--snaps` it will be added with the latest version of the snap. For example, say the `analytics` snap has versions `v1.0.0`, `v1.1.0` available on the web portal. If you run the sync command with `--snaps analytics` and you had `v1.0.0` in the manifest then it wont be touched. However, if you did not have it in the manifest then the `v1.1.0` which is the latest at that moment in time, gets added to your manifest.
648
+
649
+ **IMPORTANT**: If you are looking for a command to purely add or remove snaps or features, consider using the `update` command instead.
642
650
 
643
651
  ```bash
644
- # Update a Snapend manifest file
652
+ # Sync a Snapend manifest file
645
653
  # $input_manifest = Path and file name of the current manifest. The filename should end with .json or .yaml
646
654
  # $output_path_filename = Path and file name to store the manifest. The filename should end with .json or .yaml
647
655
  # $snaps = Comma separated list of snap ids you want to have at the end. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
@@ -650,14 +658,14 @@ Update an existing Snapend manifest.
650
658
  # IMPORTANT: If your manifest has a feature but your --features does not. It will be removed.
651
659
  # Note: One of snaps, and features is required
652
660
  # Example:
653
- # snapctl snapend-manifest update --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
661
+ # snapctl snapend-manifest sync --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
654
662
 
655
- snapctl snapend-manifest update --manifest-path-filename $input_manifest --snaps $snaps --features $features--out-path-filename $output_path_filename
663
+ snapctl snapend-manifest sync --manifest-path-filename $input_manifest --snaps $snaps --features $features--out-path-filename $output_path_filename
656
664
  ```
657
665
 
658
666
  #### 4. Snapend Manifest Upgrade
659
667
 
660
- Upgrade all snaps or a list of snaps to the latest version
668
+ Upgrade all snaps or a list of snaps to the latest version of the snap available at that moment in time. If you do not pass in `--snaps` then all the snaps in the manifest will be upgraded. If you do pass a list of snaps then all snap Ids are expected to be valid.
661
669
 
662
670
  ```bash
663
671
  # Update a Snapend manifest file
@@ -670,9 +678,35 @@ Upgrade all snaps or a list of snaps to the latest version
670
678
  # snapctl snapend-manifest upgrade --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --snaps game-server-fleets --out-path ./snapend-updated-manifest.yaml
671
679
  # snapctl snapend-manifest upgrade --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --out-path ./snapend-updated-manifest.yaml
672
680
 
681
+ snapctl snapend-manifest upgrade --manifest-path-filename $input_manifest --out-path-filename $output_path_filename
682
+
673
683
  snapctl snapend-manifest upgrade --manifest-path-filename $input_manifest --snaps $snaps --out-path-filename $output_path_filename
674
684
  ```
675
685
 
686
+ #### 5. Snapend Manifest Update
687
+
688
+ Update your manifest. This command is additive and subtractive. It allows you to add or remove, snaps and features from an existing manifest. Unlike `sync`, this will only add or remove entities from your manifest, keeping the remaining part of the manifest intact. If your manifest has a snap and you pass it with `--add-snaps` then it will be ignored. However if you do not have a snap and you pass it with `--add-snaps` then the latest version of the snap will be added. If you pass a snap with `--remove-snaps` that is already not in the manifest, then it will be skipped. This ensures idempotency after the initial update is made to the snapend manifest.
689
+
690
+ **IMPORTANT**:
691
+ - All additions are performed before removals.
692
+ - If you are looking to have strict relation between your input (snaps + features) and your output manifest, consider using the `sync` command.
693
+
694
+ ```bash
695
+ # Update a Snapend manifest file
696
+ # $input_manifest = Path and file name of the current manifest. The filename should end with .json or .yaml
697
+ # $output_path_filename = Path and file name to store the manifest. The filename should end with .json or .yaml
698
+ # $addSnaps = Comma separated list of snap ids you want to add to your manifest. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
699
+ # $removeSnaps = Comma separated list of snap ids you want to remove from your manifest. You can get the snap ids from # the `snapctl snaps enumerate --out-path ./snaps.json` command.
700
+ # $addFeatures = Pass `WEB_SOCKETS` if you want to enable web sockets for your backend.
701
+ # $removeFeatures = Pass `WEB_SOCKETS` if you want to disable web sockets for your backend.
702
+ # Note: One of add-snaps, remove-snaps, add-features or remove-features is required
703
+ # Example:
704
+ # snapctl snapend-manifest update --manifest-path-filename ./snapser-jn86b0dv-manifest.yaml --add-snaps game-server-fleets --remove-snaps analytics --remove-features WEB_SOCKETS --out-path ./snapend-updated-manifest.yaml
705
+
706
+ snapctl snapend-manifest update --manifest-path-filename $input_manifest --add-snaps $addSnaps --remove-snaps $removeSnaps --add-features $addFeatures --remove-features $removeFeatures --out-path-filename $output_path_filename
707
+ ```
708
+
709
+
676
710
  ### 7. Snapend
677
711
  Snapctl commands for your snapend
678
712
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "snapctl"
3
- version = "1.1.0"
3
+ version = "1.1.2"
4
4
  description = "Snapser CLI Tool"
5
5
  authors = ["Ajinkya Apte <aj@snapser.com>"]
6
6
  readme = "README.md"
@@ -8,9 +8,9 @@ from requests.exceptions import RequestException
8
8
  from rich.progress import Progress, SpinnerColumn, TextColumn
9
9
  from snapctl.config.constants import SNAPCTL_SNAPEND_MANIFEST_UPGRADE_ERROR, SNAPCTL_INPUT_ERROR, \
10
10
  SNAPCTL_SNAPEND_MANIFEST_CREATE_ERROR, SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR, \
11
- SNAPCTL_INTERNAL_SERVER_ERROR
11
+ SNAPCTL_INTERNAL_SERVER_ERROR, SNAPCTL_SNAPEND_MANIFEST_SYNC_ERROR
12
12
  from snapctl.commands.snaps import Snaps
13
- from snapctl.utils.helper import snapctl_error, snapctl_success
13
+ from snapctl.utils.helper import snapctl_error, snapctl_success, check_duplicates_in_list
14
14
  from snapctl.utils.echo import info, warning, success
15
15
 
16
16
 
@@ -18,7 +18,7 @@ class SnapendManifest:
18
18
  """
19
19
  CLI commands exposed for Snapend manifest
20
20
  """
21
- SUBCOMMANDS = ['create', 'update', 'upgrade']
21
+ SUBCOMMANDS = ['create', 'sync', 'update', 'upgrade']
22
22
  ENVIRONMENTS = ['DEVELOPMENT', 'STAGING', 'PRODUCTION']
23
23
  FEATURES = ['WEB_SOCKETS']
24
24
  AUTH_SNAP_ID = 'auth'
@@ -30,6 +30,10 @@ class SnapendManifest:
30
30
  manifest_path_filename: Union[str, None] = None,
31
31
  snaps: Union[str, None] = None,
32
32
  features: Union[str, None] = None,
33
+ add_snaps: Union[str, None] = None,
34
+ remove_snaps: Union[str, None] = None,
35
+ add_features: Union[str, None] = None,
36
+ remove_features: Union[str, None] = None,
33
37
  out_path_filename: Union[str, None] = None,
34
38
  ) -> None:
35
39
  self.subcommand: str = subcommand
@@ -42,6 +46,10 @@ class SnapendManifest:
42
46
  self.out_path_filename: Union[str, None] = out_path_filename
43
47
  self.snaps = snaps
44
48
  self.features = features
49
+ self.add_snaps = add_snaps
50
+ self.remove_snaps = remove_snaps
51
+ self.add_features = add_features
52
+ self.remove_features = remove_features
45
53
  self.remote_snaps: list = self.load_snaps()
46
54
  # Setup
47
55
  self.setup_manifest()
@@ -137,6 +145,12 @@ class SnapendManifest:
137
145
  message="Environment must be one of " +
138
146
  f"{', '.join(SnapendManifest.ENVIRONMENTS)}.",
139
147
  code=SNAPCTL_INPUT_ERROR)
148
+ if (not self.snaps or self.snaps == '') and \
149
+ (not self.features or self.features == ''):
150
+ snapctl_error(
151
+ message="At least one of snaps or features " +
152
+ "is required to sync a snapend manifest.",
153
+ code=SNAPCTL_INPUT_ERROR)
140
154
  if not self.out_path_filename:
141
155
  snapctl_error(
142
156
  message="Output path is required for create command.",
@@ -147,33 +161,80 @@ class SnapendManifest:
147
161
  snapctl_error(
148
162
  message="Output path must end with .json, .yaml or .yml",
149
163
  code=SNAPCTL_INPUT_ERROR)
150
- if not self.snaps or self.snaps == '':
151
- snapctl_error(
152
- message="At least one snap ID is required to create a " +
153
- "snapend manifest.",
154
- code=SNAPCTL_INPUT_ERROR)
164
+ if self.snaps and self.snaps != '':
165
+ input_snaps_list = self.snaps.split(',')
166
+ repeat_snaps = check_duplicates_in_list(input_snaps_list)
167
+ if len(repeat_snaps) > 0:
168
+ snapctl_error(
169
+ message="Duplicate snaps found in input: " +
170
+ f"{', '.join(repeat_snaps)}. Please check and try again.",
171
+ code=SNAPCTL_INPUT_ERROR)
172
+ remote_snaps_ids = [snap['id'] for snap in self.remote_snaps]
173
+ for input_snap in input_snaps_list:
174
+ if not input_snap in remote_snaps_ids:
175
+ snapctl_error(
176
+ message="Invalid Snap " + input_snap +
177
+ " provided with --snaps. Please check and try again.",
178
+ code=SNAPCTL_INPUT_ERROR)
155
179
  if self.features:
156
- for feature in self.features.split(','):
180
+ input_features_list = self.features.split(',')
181
+ repeat_features = check_duplicates_in_list(input_features_list)
182
+ if len(repeat_features) > 0:
183
+ snapctl_error(
184
+ message="Duplicate features found in input: " +
185
+ f"{', '.join(repeat_features)}. Please check and try again.",
186
+ code=SNAPCTL_INPUT_ERROR)
187
+ for feature in input_features_list:
157
188
  feature = feature.strip()
158
189
  if feature.upper() not in SnapendManifest.FEATURES:
159
190
  snapctl_error(
160
- message="-add-features must be one of " +
191
+ message="--features must be one of " +
161
192
  f"{', '.join(SnapendManifest.FEATURES)}.",
162
193
  code=SNAPCTL_INPUT_ERROR)
163
- elif self.subcommand == 'update':
194
+ elif self.subcommand == 'sync':
164
195
  if not self.manifest_path_filename:
165
196
  snapctl_error(
166
- message="Manifest path is required for update command.",
197
+ message="Manifest path is required for sync command.",
167
198
  code=SNAPCTL_INPUT_ERROR)
168
199
  if (not self.snaps or self.snaps == '') and \
169
200
  (not self.features or self.features == ''):
170
201
  snapctl_error(
171
202
  message="At least one of snaps or features " +
172
- "is required to update a snapend manifest.",
203
+ "is required to sync a snapend manifest.",
173
204
  code=SNAPCTL_INPUT_ERROR)
205
+ if self.snaps and self.snaps != '':
206
+ input_snaps_list = self.snaps.split(',')
207
+ repeat_snaps = check_duplicates_in_list(input_snaps_list)
208
+ if len(repeat_snaps) > 0:
209
+ snapctl_error(
210
+ message="Duplicate snaps found in input: " +
211
+ f"{', '.join(repeat_snaps)}. Please check and try again.",
212
+ code=SNAPCTL_INPUT_ERROR)
213
+ remote_snaps_ids = [snap['id'] for snap in self.remote_snaps]
214
+ for input_snap in input_snaps_list:
215
+ if not input_snap in remote_snaps_ids:
216
+ snapctl_error(
217
+ message="Invalid Snap " + input_snap +
218
+ " provided with --snaps. Please check and try again.",
219
+ code=SNAPCTL_INPUT_ERROR)
220
+ if self.features:
221
+ input_features_list = self.features.split(',')
222
+ repeat_features = check_duplicates_in_list(input_features_list)
223
+ if len(repeat_features) > 0:
224
+ snapctl_error(
225
+ message="Duplicate features found in input: " +
226
+ f"{', '.join(repeat_features)}. Please check and try again.",
227
+ code=SNAPCTL_INPUT_ERROR)
228
+ for feature in input_features_list:
229
+ feature = feature.strip()
230
+ if feature.upper() not in SnapendManifest.FEATURES:
231
+ snapctl_error(
232
+ message="--features must be one of " +
233
+ f"{', '.join(SnapendManifest.FEATURES)}.",
234
+ code=SNAPCTL_INPUT_ERROR)
174
235
  if not self.out_path_filename:
175
236
  snapctl_error(
176
- message="Output path is required for update command.",
237
+ message="Output path is required for sync command.",
177
238
  code=SNAPCTL_INPUT_ERROR)
178
239
  if self.out_path_filename and not (self.out_path_filename.endswith('.json') or
179
240
  self.out_path_filename.endswith('.yaml') or
@@ -192,10 +253,132 @@ class SnapendManifest:
192
253
  "Please check the file and try again.",
193
254
  code=SNAPCTL_INPUT_ERROR)
194
255
  elif self.subcommand == 'upgrade':
256
+ if not self.manifest_path_filename:
257
+ snapctl_error(
258
+ message="Manifest path is required for upgrade command.",
259
+ code=SNAPCTL_INPUT_ERROR)
260
+ if self.snaps and self.snaps != '':
261
+ input_snaps_list = self.snaps.split(',')
262
+ repeat_snaps = check_duplicates_in_list(input_snaps_list)
263
+ if len(repeat_snaps) > 0:
264
+ snapctl_error(
265
+ message="Duplicate snaps found in input: " +
266
+ f"{', '.join(repeat_snaps)}. Please check and try again.",
267
+ code=SNAPCTL_INPUT_ERROR)
268
+ if not self.out_path_filename:
269
+ snapctl_error(
270
+ message="Output path is required for upgrade command.",
271
+ code=SNAPCTL_INPUT_ERROR)
272
+ if self.out_path_filename and not (self.out_path_filename.endswith('.json') or
273
+ self.out_path_filename.endswith('.yaml') or
274
+ self.out_path_filename.endswith('.yml')):
275
+ snapctl_error(
276
+ message="Output path must end with .json, .yaml or .yml",
277
+ code=SNAPCTL_INPUT_ERROR)
278
+ if not self.manifest:
279
+ snapctl_error(
280
+ message="Unable to read the manifest file. " +
281
+ "Please check the file and try again.",
282
+ code=SNAPCTL_INPUT_ERROR)
283
+ if 'service_definitions' not in self.manifest:
284
+ snapctl_error(
285
+ message="Invalid manifest file. Need service_definitions. " +
286
+ "Please check the file and try again.",
287
+ code=SNAPCTL_INPUT_ERROR)
288
+ if self.snaps and self.snaps != '':
289
+ input_snaps_list = self.snaps.split(',')
290
+ remote_snaps_ids = [snap['id'] for snap in self.remote_snaps]
291
+ current_snap_ids = [snap['id']
292
+ for snap in self.manifest['service_definitions']]
293
+ for input_snap in input_snaps_list:
294
+ if not input_snap in remote_snaps_ids:
295
+ snapctl_error(
296
+ message="Invalid Snap " + input_snap +
297
+ " provided with --snaps. Please check and try again.",
298
+ code=SNAPCTL_INPUT_ERROR)
299
+ if not input_snap in current_snap_ids:
300
+ snapctl_error(
301
+ message="Snap " + input_snap +
302
+ " provided with --snaps is not present in the manifest. " +
303
+ "Please check and try again.",
304
+ code=SNAPCTL_INPUT_ERROR)
305
+ elif self.subcommand == 'update':
195
306
  if not self.manifest_path_filename:
196
307
  snapctl_error(
197
308
  message="Manifest path is required for update command.",
198
309
  code=SNAPCTL_INPUT_ERROR)
310
+ if (not self.add_snaps or self.add_snaps == '') and \
311
+ (not self.remove_snaps or self.remove_snaps == '') and \
312
+ (not self.add_features or self.add_features == '') and \
313
+ (not self.remove_features or self.remove_features == ''):
314
+ snapctl_error(
315
+ message="At least one of --add-snaps, --remove-snaps, add-features " +
316
+ "or --remove-features is required to update a snapend manifest.",
317
+ code=SNAPCTL_INPUT_ERROR)
318
+ if self.add_snaps and self.add_snaps != '':
319
+ input_snaps_list = self.add_snaps.split(',')
320
+ repeat_snaps = check_duplicates_in_list(input_snaps_list)
321
+ if len(repeat_snaps) > 0:
322
+ snapctl_error(
323
+ message="Duplicate snaps found in input: " +
324
+ f"{', '.join(repeat_snaps)}. Please check and try again.",
325
+ code=SNAPCTL_INPUT_ERROR)
326
+ remote_snaps_ids = [snap['id'] for snap in self.remote_snaps]
327
+ for input_snap in input_snaps_list:
328
+ if not input_snap in remote_snaps_ids:
329
+ snapctl_error(
330
+ message="Invalid Snap " + input_snap +
331
+ " provided with --add-snaps. Please check and try again.",
332
+ code=SNAPCTL_INPUT_ERROR)
333
+ if self.remove_snaps and self.remove_snaps != '':
334
+ input_snaps_list = self.remove_snaps.split(',')
335
+ if SnapendManifest.AUTH_SNAP_ID in input_snaps_list:
336
+ snapctl_error(
337
+ message="Auth snap cannot be removed from the manifest.",
338
+ code=SNAPCTL_INPUT_ERROR)
339
+ repeat_snaps = check_duplicates_in_list(input_snaps_list)
340
+ if len(repeat_snaps) > 0:
341
+ snapctl_error(
342
+ message="Duplicate snaps found in input: " +
343
+ f"{', '.join(repeat_snaps)}. Please check and try again.",
344
+ code=SNAPCTL_INPUT_ERROR)
345
+ remote_snaps_ids = [snap['id'] for snap in self.remote_snaps]
346
+ for input_snap in input_snaps_list:
347
+ if not input_snap in remote_snaps_ids:
348
+ snapctl_error(
349
+ message="Invalid Snap " + input_snap +
350
+ " provided with --remove-snaps. Please check and try again.",
351
+ code=SNAPCTL_INPUT_ERROR)
352
+ if self.add_features:
353
+ input_features_list = self.add_features.split(',')
354
+ repeat_features = check_duplicates_in_list(input_features_list)
355
+ if len(repeat_features) > 0:
356
+ snapctl_error(
357
+ message="Duplicate features found in input: " +
358
+ f"{', '.join(repeat_features)}. Please check and try again.",
359
+ code=SNAPCTL_INPUT_ERROR)
360
+ for feature in input_features_list:
361
+ feature = feature.strip()
362
+ if feature.upper() not in SnapendManifest.FEATURES:
363
+ snapctl_error(
364
+ message="--add-features must be one of " +
365
+ f"{', '.join(SnapendManifest.FEATURES)}.",
366
+ code=SNAPCTL_INPUT_ERROR)
367
+ if self.remove_features:
368
+ input_features_list = self.remove_features.split(',')
369
+ repeat_features = check_duplicates_in_list(input_features_list)
370
+ if len(repeat_features) > 0:
371
+ snapctl_error(
372
+ message="Duplicate features found in input: " +
373
+ f"{', '.join(repeat_features)}. Please check and try again.",
374
+ code=SNAPCTL_INPUT_ERROR)
375
+ for feature in input_features_list:
376
+ feature = feature.strip()
377
+ if feature.upper() not in SnapendManifest.FEATURES:
378
+ snapctl_error(
379
+ message="--remove-features must be one of " +
380
+ f"{', '.join(SnapendManifest.FEATURES)}.",
381
+ code=SNAPCTL_INPUT_ERROR)
199
382
  if not self.out_path_filename:
200
383
  snapctl_error(
201
384
  message="Output path is required for update command.",
@@ -250,7 +433,7 @@ class SnapendManifest:
250
433
  """
251
434
  Create a snapend manifest
252
435
  @test -
253
- `python -m snapctl snapend-manifest create --name my-dev-snapend --env DEVELOPMENT --snaps auth,analytics --out-path-filename ./snapend-manifest.json`
436
+ `python -m snapctl snapend-manifest create --name my-dev-snapend --env DEVELOPMENT --snaps auth,analytics --out-path-filename ./snapend-create-manifest.json`
254
437
  """
255
438
  progress = Progress(
256
439
  SpinnerColumn(),
@@ -270,21 +453,14 @@ class SnapendManifest:
270
453
  "external_endpoints": [],
271
454
  "settings": []
272
455
  }
273
- snap_ids = [snap_id.strip()
274
- for snap_id in self.snaps.split(',')]
275
- for snap_id in snap_ids:
276
- snap_found = False
277
- for snap in self.remote_snaps:
278
- if snap['id'] == snap_id:
279
- snap_sd = self._get_snap_sd(snap_id)
280
- new_manifest['service_definitions'].append(snap_sd)
281
- snap_found = True
282
- break
283
- if not snap_found:
284
- snapctl_error(
285
- message=f"Snap ID {snap_id} not found in your snaps.",
286
- code=SNAPCTL_INPUT_ERROR,
287
- progress=progress)
456
+ if self.snaps and self.snaps != '':
457
+ snap_ids = [snap_id.strip()
458
+ for snap_id in self.snaps.split(',')]
459
+ for snap_id in snap_ids:
460
+ snap_sd = self._get_snap_sd(snap_id)
461
+ new_manifest['service_definitions'].append(snap_sd)
462
+ info(f"Added snap {snap_id} to the manifest.")
463
+ # If auth snap is not present, add it
288
464
  found_auth = False
289
465
  for final_snap in new_manifest['service_definitions']:
290
466
  if final_snap['id'] == SnapendManifest.AUTH_SNAP_ID:
@@ -303,6 +479,7 @@ class SnapendManifest:
303
479
  if feature.upper() not in new_manifest['feature_definitions']:
304
480
  new_manifest['feature_definitions'].append(
305
481
  feature.upper())
482
+ info(f"Added feature {feature} to the manifest.")
306
483
  if self.out_path_filename:
307
484
  # Based on the out-path extension, write JSON or YAML
308
485
  if self.out_path_filename.endswith('.yaml') or self.out_path_filename.endswith('.yml'):
@@ -343,11 +520,11 @@ class SnapendManifest:
343
520
  message='Failed to create snapend manifest.',
344
521
  code=SNAPCTL_SNAPEND_MANIFEST_CREATE_ERROR, progress=progress)
345
522
 
346
- def update(self) -> bool:
523
+ def sync(self) -> bool:
347
524
  """
348
- Update a snapend manifest
525
+ Sync with a snapend manifest
349
526
  @test -
350
- `python -m snapctl snapend-manifest update --manifest-path-filename ./snapend-manifest.json --features WEB_SOCKETS --out-path-filename ./snapend-updated-manifest.json`
527
+ `python -m snapctl snapend-manifest sync --manifest-path-filename ./snapend-manifest.json --snaps analytics,auth --features WEB_SOCKETS --out-path-filename ./snapend-updated-manifest.json`
351
528
  """
352
529
  progress = Progress(
353
530
  SpinnerColumn(),
@@ -356,41 +533,33 @@ class SnapendManifest:
356
533
  )
357
534
  progress.start()
358
535
  progress.add_task(
359
- description='Updating snapend manifest...', total=None)
536
+ description='Syncing snapend manifest...', total=None)
360
537
  try:
361
538
  if 'applied_configuration' in self.manifest:
362
539
  info('Applied configuration found in the manifest. ')
363
540
  warning(
364
541
  'You need to ensure you have synced the manifest from remote. ' +
365
542
  'Else if you try applying the newly generated manifest it may not work.')
366
-
367
543
  current_snaps = self.manifest['service_definitions']
368
544
  current_snap_ids = [snap['id'] for snap in current_snaps]
369
545
  final_snaps = []
370
546
  if self.snaps and self.snaps != '':
371
- for snap_id in self.snaps.split(','):
547
+ input_snap_list = self.snaps.split(',')
548
+ for snap_id in input_snap_list:
372
549
  snap_id = snap_id.strip()
373
550
  if snap_id == '':
374
551
  continue
552
+ # Copy existing snap if already present
375
553
  if snap_id in current_snap_ids:
376
554
  warning(
377
555
  f"Snap {snap_id} already exists in the manifest. Skipping...")
378
556
  final_snaps.append(
379
557
  current_snaps[current_snap_ids.index(snap_id)])
380
558
  continue
381
- snap_found = False
382
- for snap in self.remote_snaps:
383
- if snap['id'] == snap_id:
384
- snap_found = True
385
- snap_sd = self._get_snap_sd(snap_id)
386
- final_snaps.append(snap_sd)
387
- info(f"Added snap {snap_id} to the manifest.")
388
- break
389
- if not snap_found:
390
- snapctl_error(
391
- message=f"Snap ID {snap_id} not found in your snaps.",
392
- code=SNAPCTL_INPUT_ERROR,
393
- progress=progress)
559
+ # Else add new snap from remote snaps
560
+ snap_sd = self._get_snap_sd(snap_id)
561
+ final_snaps.append(snap_sd)
562
+ info(f"Added snap {snap_id} to the manifest.")
394
563
  found_auth = False
395
564
  for final_snap in final_snaps:
396
565
  if final_snap['id'] == SnapendManifest.AUTH_SNAP_ID:
@@ -404,19 +573,23 @@ class SnapendManifest:
404
573
  warning(
405
574
  f'Old snaps list "{",".join(current_snap_ids)}" will be ' +
406
575
  f'replaced with new snaps list "{",".join([snap['id'] for snap in final_snaps])}"')
576
+ final_snaps.sort(key=lambda x: x["id"])
407
577
  self.manifest['service_definitions'] = final_snaps
408
578
 
409
579
  final_features = []
410
580
  if self.features and self.features != '':
411
581
  current_features = self.manifest['feature_definitions']
412
- for feature in self.features.split(','):
582
+ input_feature_list = self.features.split(',')
583
+ for feature in input_feature_list:
413
584
  feature = feature.strip()
414
585
  if feature == '':
415
586
  continue
587
+ final_features.append(feature.upper())
416
588
  if feature.upper() in current_features:
417
589
  warning(
418
590
  f"Feature {feature} already exists in the manifest. Skipping...")
419
- final_features.append(feature.upper())
591
+ else:
592
+ info(f"Added feature {feature} to the manifest.")
420
593
  warning(
421
594
  f'Old features list: "{",".join(self.manifest["feature_definitions"])}" will ' +
422
595
  f'be replaced with new features list: "{",".join([feature for feature in final_features])}"')
@@ -441,7 +614,7 @@ class SnapendManifest:
441
614
  out_file.write(json.dumps(self.manifest, indent=4))
442
615
  info(f"Output written to {self.out_path_filename}")
443
616
  snapctl_success(
444
- message="Snapend manifest updated successfully.",
617
+ message="Snapend manifest synced successfully.",
445
618
  progress=progress)
446
619
  except ValueError as e:
447
620
  snapctl_error(
@@ -449,13 +622,13 @@ class SnapendManifest:
449
622
  code=SNAPCTL_INTERNAL_SERVER_ERROR, progress=progress)
450
623
  except RequestException as e:
451
624
  snapctl_error(
452
- message=f"Exception: Unable to update snapend manifest {e}",
453
- code=SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR, progress=progress)
625
+ message=f"Exception: Unable to synced snapend manifest {e}",
626
+ code=SNAPCTL_SNAPEND_MANIFEST_SYNC_ERROR, progress=progress)
454
627
  finally:
455
628
  progress.stop()
456
629
  snapctl_error(
457
- message='Failed to update the snapend manifest.',
458
- code=SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR, progress=progress)
630
+ message='Failed to synced the snapend manifest.',
631
+ code=SNAPCTL_SNAPEND_MANIFEST_SYNC_ERROR, progress=progress)
459
632
 
460
633
  def upgrade(self) -> bool:
461
634
  """
@@ -504,6 +677,7 @@ class SnapendManifest:
504
677
  f"Snap {snap['id']} is already at the latest " +
505
678
  f"version {snap['version']}. Skipping...")
506
679
  break
680
+ current_snaps.sort(key=lambda x: x["id"])
507
681
  self.manifest['service_definitions'] = current_snaps
508
682
 
509
683
  # Write output
@@ -539,3 +713,149 @@ class SnapendManifest:
539
713
  snapctl_error(
540
714
  message='Failed to upgrade the snapend manifest.',
541
715
  code=SNAPCTL_SNAPEND_MANIFEST_UPGRADE_ERROR, progress=progress)
716
+
717
+ def update(self) -> bool:
718
+ """
719
+ Update a snapend manifest
720
+ @test -
721
+ `python -m snapctl snapend-manifest update --manifest-path-filename ./snapend-manifest.json --add-snaps analytics,auth --add-features WEB_SOCKETS --out-path-filename ./snapend-updated-manifest.json`
722
+ """
723
+ progress = Progress(
724
+ SpinnerColumn(),
725
+ TextColumn("[progress.description]{task.description}"),
726
+ transient=True,
727
+ )
728
+ progress.start()
729
+ progress.add_task(
730
+ description='Updating snapend manifest...', total=None)
731
+ try:
732
+ if 'applied_configuration' in self.manifest:
733
+ info('Applied configuration found in the manifest. ')
734
+ warning(
735
+ 'You need to ensure you have synced the manifest from remote. ' +
736
+ 'Else if you try applying the newly generated manifest it may not work.')
737
+ current_snaps = self.manifest['service_definitions']
738
+ current_snap_ids = [snap['id'] for snap in current_snaps]
739
+ final_added_snap_ids = []
740
+ final_removed_snap_ids = []
741
+ if self.add_snaps and self.add_snaps != '':
742
+ add_snap_list = self.add_snaps.split(',')
743
+ for snap_id in add_snap_list:
744
+ snap_id = snap_id.strip()
745
+ if snap_id == '':
746
+ continue
747
+ # Copy existing snap if already present
748
+ if snap_id in current_snap_ids:
749
+ warning(
750
+ f"Snap {snap_id} already exists in the manifest. Skipping...")
751
+ continue
752
+ # Else add new snap from remote snaps
753
+ snap_sd = self._get_snap_sd(snap_id)
754
+ current_snaps.append(snap_sd)
755
+ current_snap_ids.append(snap_id)
756
+ final_added_snap_ids.append(snap_id)
757
+ info(f"Added snap {snap_id} to the manifest.")
758
+ if self.remove_snaps and self.remove_snaps != '':
759
+ remove_snap_list = self.remove_snaps.split(',')
760
+ for snap_id in remove_snap_list:
761
+ snap_id = snap_id.strip()
762
+ if snap_id == '':
763
+ continue
764
+ if snap_id not in current_snap_ids:
765
+ warning(
766
+ f"Snap {snap_id} does not exist in the manifest. Skipping...")
767
+ continue
768
+ # Remove snap from current snaps
769
+ index = current_snap_ids.index(snap_id)
770
+ current_snaps.pop(index)
771
+ current_snap_ids.pop(index)
772
+ final_removed_snap_ids.append(snap_id)
773
+ info(f"Removed snap {snap_id} from the manifest.")
774
+
775
+ found_auth = False
776
+ for final_snap in current_snap_ids:
777
+ if final_snap == SnapendManifest.AUTH_SNAP_ID:
778
+ found_auth = True
779
+ break
780
+ if not found_auth:
781
+ auth_sd = self._get_snap_sd(SnapendManifest.AUTH_SNAP_ID)
782
+ current_snaps.append(auth_sd)
783
+ current_snap_ids.append(SnapendManifest.AUTH_SNAP_ID)
784
+ final_added_snap_ids.append(SnapendManifest.AUTH_SNAP_ID)
785
+ warning(
786
+ 'Auth snap is required for snapend. Added auth snap to the manifest.')
787
+ warning(
788
+ f'New snaps "{",".join(final_added_snap_ids)}" were added. ' +
789
+ f'Snaps "{",".join(final_removed_snap_ids)}" were removed. ')
790
+ current_snaps.sort(key=lambda x: x["id"])
791
+ self.manifest['service_definitions'] = current_snaps
792
+
793
+ current_features = self.manifest['feature_definitions']
794
+ final_added_features = []
795
+ final_removed_features = []
796
+ if self.add_features and self.add_features != '':
797
+ add_feature_list = self.add_features.split(',')
798
+ for feature in add_feature_list:
799
+ feature = feature.strip()
800
+ if feature == '':
801
+ continue
802
+ if feature.upper() in current_features:
803
+ warning(
804
+ f"Feature {feature} already exists in the manifest. Skipping...")
805
+ continue
806
+ current_features.append(feature.upper())
807
+ final_added_features.append(feature.upper())
808
+ info(f"Added feature {feature} to the manifest.")
809
+ if self.remove_features and self.remove_features != '':
810
+ remove_feature_list = self.remove_features.split(',')
811
+ for feature in remove_feature_list:
812
+ feature = feature.strip()
813
+ if feature == '':
814
+ continue
815
+ if feature.upper() not in current_features:
816
+ warning(
817
+ f"Feature {feature} does not exist in the manifest. Skipping...")
818
+ continue
819
+ index = current_features.index(feature.upper())
820
+ current_features.pop(index)
821
+ final_removed_features.append(feature.upper())
822
+ info(f"Removed feature {feature} from the manifest.")
823
+ warning(
824
+ f'New features "{",".join(final_added_features)}" were added. ' +
825
+ f'Features "{",".join(final_removed_features)}" were removed. ')
826
+ current_features.sort()
827
+ self.manifest['feature_definitions'] = current_features
828
+
829
+ # Write output
830
+ # Based on the out-path extension, write JSON or YAML
831
+ if self.out_path_filename.endswith('.yaml') or self.out_path_filename.endswith('.yml'):
832
+ try:
833
+ import yaml # type: ignore
834
+ except ImportError as e:
835
+ snapctl_error(
836
+ message="YAML output requested but PyYAML is not installed. "
837
+ "Install with: pip install pyyaml",
838
+ code=SNAPCTL_INPUT_ERROR,
839
+ progress=progress)
840
+ with open(self.out_path_filename, 'w') as out_file:
841
+ yaml.dump(self.manifest, out_file, sort_keys=False)
842
+ else:
843
+ with open(self.out_path_filename, 'w') as out_file:
844
+ out_file.write(json.dumps(self.manifest, indent=4))
845
+ info(f"Output written to {self.out_path_filename}")
846
+ snapctl_success(
847
+ message="Snapend manifest updated successfully.",
848
+ progress=progress)
849
+ except ValueError as e:
850
+ snapctl_error(
851
+ message=f"Exception: {e}",
852
+ code=SNAPCTL_INTERNAL_SERVER_ERROR, progress=progress)
853
+ except RequestException as e:
854
+ snapctl_error(
855
+ message=f"Exception: Unable to update snapend manifest {e}",
856
+ code=SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR, progress=progress)
857
+ finally:
858
+ progress.stop()
859
+ snapctl_error(
860
+ message='Failed to update the snapend manifest.',
861
+ code=SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR, progress=progress)
@@ -3,7 +3,7 @@ Constants used by snapctl
3
3
  """
4
4
  COMPANY_NAME = 'Snapser'
5
5
  VERSION_PREFIX = ''
6
- VERSION = '1.1.0'
6
+ VERSION = '1.1.2'
7
7
  CONFIG_FILE_MAC = '~/.snapser/config'
8
8
  CONFIG_FILE_WIN = '%homepath%\\.snapser\\config'
9
9
 
@@ -53,8 +53,9 @@ SNAPCTL_SNAPS_ENUMERATE_ERROR = 14
53
53
 
54
54
  # Snapend Manifest Errors - 16 - 19
55
55
  SNAPCTL_SNAPEND_MANIFEST_CREATE_ERROR = 16
56
- SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR = 17
56
+ SNAPCTL_SNAPEND_MANIFEST_SYNC_ERROR = 17
57
57
  SNAPCTL_SNAPEND_MANIFEST_UPGRADE_ERROR = 18
58
+ SNAPCTL_SNAPEND_MANIFEST_UPDATE_ERROR = 19
58
59
 
59
60
  # BYOGS Errors - 20 - 29
60
61
  SNAPCTL_BYOGS_GENERIC_ERROR = 20
@@ -0,0 +1,8 @@
1
+ ## Release 1.1.2
2
+ ##### Oct 15, 2025
3
+
4
+ ### Features
5
+ 1. Snapend manifest command now supports `sync` and `update` subcommands.
6
+
7
+ ### Bug Fixes
8
+ 1. The update command was always upgrading the auth snap to the latest version. This has now been fixed.
@@ -861,20 +861,40 @@ def snapend_manifest(
861
861
  ),
862
862
  manifest_path_filename: Union[str, None] = typer.Option(
863
863
  None, "--manifest-path-filename", help=(
864
- "(req: update, upgrade) Full Path to the manifest file including the filename."
864
+ "(req: sync, upgrade) Full Path to the manifest file including the filename."
865
865
  )
866
866
  ),
867
867
  snaps_list_str: str = typer.Option(
868
868
  None, "--snaps", help=(
869
- "(use: create, update, upgrade) Comma separated list of snap ids to add, update or upgrade. "
869
+ "(use: create, sync, upgrade) Comma separated list of snap ids to add, sync or upgrade. "
870
870
  )
871
871
  ),
872
872
  features: str = typer.Option(
873
873
  None, "--features", help=(
874
- "(use: create, update) Comma separated list of feature flags to add, update. "
874
+ "(use: create, sync) Comma separated list of feature flags to add, sync. "
875
875
  "Features: " + ", ".join(SnapendManifest.FEATURES)
876
876
  )
877
877
  ),
878
+ add_snaps: str = typer.Option(
879
+ None, "--add-snaps", help=(
880
+ "(use: update) Comma separated list of snap ids to add. "
881
+ )
882
+ ),
883
+ remove_snaps: str = typer.Option(
884
+ None, "--remove-snaps", help=(
885
+ "(use: update) Comma separated list of snap ids to remove. "
886
+ )
887
+ ),
888
+ add_features: str = typer.Option(
889
+ None, "--add-features", help=(
890
+ "(use: update) Comma separated list of features to add. "
891
+ )
892
+ ),
893
+ remove_features: str = typer.Option(
894
+ None, "--remove-features", help=(
895
+ "(use: update) Comma separated list of features to remove. "
896
+ )
897
+ ),
878
898
  out_path_filename: Union[str, None] = typer.Option(
879
899
  None, "--out-path-filename", help=(
880
900
  "(optional: enumerate) Path and filename to output the manifest. The filename should end with .json or .yaml"
@@ -901,6 +921,10 @@ def snapend_manifest(
901
921
  manifest_path_filename=manifest_path_filename,
902
922
  snaps=snaps_list_str,
903
923
  features=features,
924
+ add_snaps=add_snaps,
925
+ remove_snaps=remove_snaps,
926
+ add_features=add_features,
927
+ remove_features=remove_features,
904
928
  out_path_filename=out_path_filename,
905
929
  )
906
930
  getattr(snapend_manifest_obj, subcommand.replace('-', '_'))()
@@ -3,6 +3,7 @@ Helper functions for snapctl
3
3
  """
4
4
  from typing import Union, Dict
5
5
  from pathlib import Path
6
+ from collections import Counter
6
7
  import re
7
8
  import platform
8
9
  import os
@@ -204,3 +205,10 @@ def get_config_value(environment: str, key: str) -> str:
204
205
  if environment == '' or environment not in APP_CONFIG or key not in APP_CONFIG[environment]:
205
206
  return ''
206
207
  return APP_CONFIG[environment][key]
208
+
209
+
210
+ def check_duplicates_in_list(items: list[str]) -> list[str]:
211
+ '''
212
+ Check for duplicates in a list and return the duplicate items
213
+ '''
214
+ return [k for k, v in Counter(items).items() if v > 1]
File without changes
File without changes
File without changes
File without changes
File without changes