protool 3.0.0__tar.gz → 3.0.1__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.
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: protool
3
- Version: 3.0.0
3
+ Version: 3.0.1
4
4
  Summary: A tool for dealing with provisioning profiles
5
- Home-page: https://github.com/Microsoft/protool
6
5
  License: MIT
6
+ License-File: LICENSE
7
7
  Keywords: provisioning,profiles,apple,ios,xcode,mobileprovision
8
8
  Author: Dale Myers
9
9
  Author-email: dalemy@microsoft.com
@@ -17,12 +17,14 @@ Classifier: Programming Language :: Python :: 3
17
17
  Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
20
21
  Classifier: Programming Language :: Python :: 3.10
21
22
  Classifier: Programming Language :: Python :: 3.8
22
23
  Classifier: Programming Language :: Python :: 3.9
23
24
  Classifier: Topic :: Software Development
24
25
  Classifier: Topic :: Utilities
25
26
  Requires-Dist: pyOpenSSL (>=25.1.0,<26.0.0)
27
+ Project-URL: Homepage, https://github.com/Microsoft/protool
26
28
  Project-URL: Repository, https://github.com/Microsoft/protool
27
29
  Description-Content-Type: text/markdown
28
30
 
@@ -72,6 +74,13 @@ Alternatively, from the command line:
72
74
  # Get the raw XML (identical to using `security cms -D -i /path/to/profile`)
73
75
  protool decode --profile /path/to/profile
74
76
 
77
+ Custom diff commands (`tool_override` in Python or `--tool` on the command line)
78
+ accept an executable and optional arguments, for example `--tool 'diff -u'`.
79
+ Quote executable paths or arguments containing spaces within the command string,
80
+ for example `--tool '"/path with spaces/diff" -u'`. Commands run directly, without
81
+ a shell: pipes, redirection, environment-variable expansion, and command
82
+ substitution are not supported. Profile paths are always passed as literal arguments.
83
+
75
84
 
76
85
  # Contributing
77
86
 
@@ -44,6 +44,13 @@ Alternatively, from the command line:
44
44
  # Get the raw XML (identical to using `security cms -D -i /path/to/profile`)
45
45
  protool decode --profile /path/to/profile
46
46
 
47
+ Custom diff commands (`tool_override` in Python or `--tool` on the command line)
48
+ accept an executable and optional arguments, for example `--tool 'diff -u'`.
49
+ Quote executable paths or arguments containing spaces within the command string,
50
+ for example `--tool '"/path with spaces/diff" -u'`. Commands run directly, without
51
+ a shell: pipes, redirection, environment-variable expansion, and command
52
+ substitution are not supported. Profile paths are always passed as literal arguments.
53
+
47
54
 
48
55
  # Contributing
49
56
 
@@ -8,7 +8,7 @@ import copy
8
8
  import datetime
9
9
  import os
10
10
  import plistlib
11
- import shutil
11
+ import shlex
12
12
  import subprocess
13
13
  import sys
14
14
  import tempfile
@@ -201,9 +201,9 @@ class ProvisioningProfile:
201
201
  if not os.path.exists(self.file_path):
202
202
  raise Exception(f"File does not exist: {self.file_path}")
203
203
 
204
- security_cmd = f'security cms -D -i "{self.file_path}" 2> /dev/null'
204
+ security_cmd = ["security", "cms", "-D", "-i", self.file_path]
205
205
  return subprocess.check_output(
206
- security_cmd, universal_newlines=True, shell=True
206
+ security_cmd, universal_newlines=True, stderr=subprocess.DEVNULL
207
207
  ).strip()
208
208
 
209
209
 
@@ -239,14 +239,17 @@ def diff(
239
239
  ignore_keys: list[str] | None = None,
240
240
  tool_override: str | None = None,
241
241
  ) -> str:
242
- """Diff two provisioning profiles."""
242
+ """Diff two provisioning profiles without shell expansion of tool_override."""
243
243
 
244
244
  # pylint: disable=too-many-locals
245
245
 
246
246
  if tool_override is None:
247
- diff_tool = "opendiff"
247
+ diff_command = ["opendiff"]
248
248
  else:
249
- diff_tool = tool_override
249
+ diff_command = shlex.split(tool_override)
250
+
251
+ if not diff_command or not diff_command[0]:
252
+ raise ValueError("Diff command must include an executable")
250
253
 
251
254
  profile_a = ProvisioningProfile(a_path, sort_keys=sort_keys)
252
255
  profile_b = ProvisioningProfile(b_path, sort_keys=sort_keys)
@@ -271,31 +274,26 @@ def diff(
271
274
  a_xml = plistlib.dumps(a_dict).decode("utf-8")
272
275
  b_xml = plistlib.dumps(b_dict).decode("utf-8")
273
276
 
274
- temp_dir = tempfile.mkdtemp()
275
-
276
- a_temp_path = os.path.join(temp_dir, profile_a.file_name)
277
- b_temp_path = os.path.join(temp_dir, profile_b.file_name)
278
-
279
- with open(a_temp_path, "w", encoding="utf-8") as temp_profile:
280
- temp_profile.write(a_xml)
277
+ with tempfile.TemporaryDirectory() as temp_dir:
278
+ a_temp_path = os.path.join(temp_dir, profile_a.file_name)
279
+ b_temp_path = os.path.join(temp_dir, profile_b.file_name)
281
280
 
282
- with open(b_temp_path, "w", encoding="utf-8") as temp_profile:
283
- temp_profile.write(b_xml)
281
+ with open(a_temp_path, "w", encoding="utf-8") as temp_profile:
282
+ temp_profile.write(a_xml)
284
283
 
285
- # We deliberately don't wrap the tool so that arguments work as well
286
- diff_command = f'{diff_tool} "{a_temp_path}" "{b_temp_path}"'
284
+ with open(b_temp_path, "w", encoding="utf-8") as temp_profile:
285
+ temp_profile.write(b_xml)
287
286
 
288
- try:
289
- diff_contents = subprocess.check_output(
290
- diff_command, universal_newlines=True, shell=True
291
- ).strip()
292
- except subprocess.CalledProcessError as ex:
293
- # Diff tools usually return a non-0 exit code if there are differences,
294
- # so we just swallow this error
295
- diff_contents = ex.output
287
+ diff_command.extend([a_temp_path, b_temp_path])
296
288
 
297
- # Cleanup
298
- shutil.rmtree(temp_dir)
289
+ try:
290
+ diff_contents = subprocess.check_output(
291
+ diff_command, universal_newlines=True
292
+ ).strip()
293
+ except subprocess.CalledProcessError as ex:
294
+ # Diff tools usually return a non-0 exit code if there are differences,
295
+ # so we just swallow this error
296
+ diff_contents = ex.output
299
297
 
300
298
  return diff_contents
301
299
 
@@ -131,7 +131,7 @@ def _handle_arguments() -> int:
131
131
  dest="tool",
132
132
  action="store",
133
133
  default=None,
134
- help="Specify a diff command to use. It should take two file paths as the final two arguments. Defaults to opendiff", # pylint: disable=line-too-long
134
+ help="Specify a diff executable with optional quoted arguments (no shell expansion). It should take two file paths as the final two arguments. Defaults to opendiff", # pylint: disable=line-too-long
135
135
  )
136
136
 
137
137
  diff_parser.add_argument(
@@ -177,7 +177,7 @@ def _handle_arguments() -> int:
177
177
  dest="tool",
178
178
  action="store",
179
179
  default=None,
180
- help="Specify a diff command to use. It should take two file paths as the final two arguments. Defaults to opendiff", # pylint: disable=line-too-long
180
+ help="Specify a diff executable with optional quoted arguments (no shell expansion). It should take two file paths as the final two arguments. Defaults to opendiff", # pylint: disable=line-too-long
181
181
  )
182
182
 
183
183
  gitdiff_parser.add_argument(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "protool"
3
- version = "3.0.0"
3
+ version = "3.0.1"
4
4
  description = "A tool for dealing with provisioning profiles"
5
5
 
6
6
  license = "MIT"
@@ -46,7 +46,7 @@ pyOpenSSL = "^25.1.0"
46
46
  black = "^26.1.0"
47
47
  mypy = "^1.19.1"
48
48
  pylint = "^4.0.4"
49
- pytest = "^9.0.2"
49
+ pytest = "^9.0.3"
50
50
  pytest-cov = "^7.0.0"
51
51
  types-pyOpenSSL = "^24.1.0.20240722"
52
52
 
File without changes