Skip to content

Commit 0bacd93

Browse files
authored
Merge pull request #69496 from cdalvaro/feature/mac_brew_pkg_trust_taps
feat(macOS): add Homebrew trust/untrust support to mac_brew_pkg
2 parents 445d03e + 91e34c9 commit 0bacd93

5 files changed

Lines changed: 629 additions & 0 deletions

File tree

changelog/69496.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added ``pkg.trust``, ``pkg.untrust``, ``pkg.is_trusted``, and ``pkg.list_trusted`` to ``salt.modules.mac_brew_pkg`` to manage Homebrew's trust list for non-official taps, formulae, casks and external commands. Added ``pkg.trusted`` and ``pkg.untrusted`` state functions to ``salt.states.pkg`` to enforce trust state declaratively on macOS.

salt/modules/mac_brew_pkg.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
# Define the module's virtual name
3434
__virtualname__ = "pkg"
3535

36+
_TRUST_TYPES = ("tap", "formula", "cask", "command")
37+
3638

3739
def __virtual__():
3840
"""
@@ -895,3 +897,156 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06
895897

896898

897899
unpin = unhold
900+
901+
902+
def list_trusted(type=None):
903+
"""
904+
List trusted taps, formulae, casks and commands.
905+
906+
.. versionadded:: 3008.2
907+
908+
type
909+
Filter by type. Valid values: ``tap``, ``formula``, ``cask``, ``command``.
910+
When ``None`` (default), returns a dict with all trusted items grouped by
911+
type (keys: ``taps``, ``formulae``, ``casks``, ``commands``). When a type
912+
is specified, returns a list of trusted items of that type.
913+
914+
CLI Example:
915+
916+
.. code-block:: bash
917+
918+
salt '*' pkg.list_trusted
919+
salt '*' pkg.list_trusted type=tap
920+
"""
921+
if type is not None and type not in _TRUST_TYPES:
922+
raise SaltInvocationError(
923+
f"Invalid type '{type}'. Valid values: {', '.join(_TRUST_TYPES)}"
924+
)
925+
926+
cmd = ["trust", "--json=v1"]
927+
if type is not None:
928+
cmd.append(f"--{type}")
929+
930+
result = _call_brew(*cmd)
931+
try:
932+
return salt.utils.json.loads(result["stdout"])
933+
except ValueError as err:
934+
msg = f'Unable to interpret output from "brew trust": {err}'
935+
log.error(msg)
936+
raise CommandExecutionError(msg)
937+
938+
939+
def trust(name, type=None):
940+
"""
941+
Trust a tap, formula, cask or command so Homebrew may load it when
942+
``$HOMEBREW_REQUIRE_TAP_TRUST`` is set.
943+
944+
.. versionadded:: 3008.2
945+
946+
name
947+
The name of the tap, formula, cask or command to trust. Can also be a
948+
remote URL for taps.
949+
950+
type
951+
The type of the item. Valid values: ``tap``, ``formula``, ``cask``,
952+
``command``. If not specified, Homebrew will auto-detect the type.
953+
954+
Returns ``True`` on success (including when the item was already trusted),
955+
``False`` on failure.
956+
957+
CLI Example:
958+
959+
.. code-block:: bash
960+
961+
salt '*' pkg.trust cdalvaro/tap
962+
salt '*' pkg.trust cdalvaro/tap type=tap
963+
salt '*' pkg.trust cdalvaro/tap/salt type=formula
964+
"""
965+
if type is not None and type not in _TRUST_TYPES:
966+
raise SaltInvocationError(
967+
f"Invalid type '{type}'. Valid values: {', '.join(_TRUST_TYPES)}"
968+
)
969+
970+
cmd = ["trust"]
971+
if type is not None:
972+
cmd.append(f"--{type}")
973+
cmd.append(name)
974+
975+
try:
976+
_call_brew(*cmd)
977+
except CommandExecutionError:
978+
log.error('Failed to trust "%s"', name)
979+
return False
980+
981+
return True
982+
983+
984+
def untrust(name, type=None):
985+
"""
986+
Stop trusting a tap, formula, cask or command.
987+
988+
.. versionadded:: 3008.2
989+
990+
name
991+
The name of the tap, formula, cask or command to untrust.
992+
993+
type
994+
The type of the item. Valid values: ``tap``, ``formula``, ``cask``,
995+
``command``. If not specified, Homebrew will auto-detect the type.
996+
997+
Returns ``True`` on success (including when the item was not trusted),
998+
``False`` on failure.
999+
1000+
CLI Example:
1001+
1002+
.. code-block:: bash
1003+
1004+
salt '*' pkg.untrust cdalvaro/tap
1005+
salt '*' pkg.untrust cdalvaro/tap type=tap
1006+
salt '*' pkg.untrust cdalvaro/tap/salt type=formula
1007+
"""
1008+
if type is not None and type not in _TRUST_TYPES:
1009+
raise SaltInvocationError(
1010+
f"Invalid type '{type}'. Valid values: {', '.join(_TRUST_TYPES)}"
1011+
)
1012+
1013+
cmd = ["untrust"]
1014+
if type is not None:
1015+
cmd.append(f"--{type}")
1016+
cmd.append(name)
1017+
1018+
try:
1019+
_call_brew(*cmd)
1020+
except CommandExecutionError:
1021+
log.error('Failed to untrust "%s"', name)
1022+
return False
1023+
1024+
return True
1025+
1026+
1027+
def is_trusted(name, type=None):
1028+
"""
1029+
Check whether a tap, formula, cask or command is trusted.
1030+
1031+
.. versionadded:: 3008.2
1032+
1033+
name
1034+
The name of the tap, formula, cask or command to check.
1035+
1036+
type
1037+
The type of the item. Valid values: ``tap``, ``formula``, ``cask``,
1038+
``command``. If not specified, checks across all types.
1039+
1040+
Returns ``True`` if the item is trusted, ``False`` otherwise.
1041+
1042+
CLI Example:
1043+
1044+
.. code-block:: bash
1045+
1046+
salt '*' pkg.is_trusted cdalvaro/tap
1047+
salt '*' pkg.is_trusted cdalvaro/tap type=tap
1048+
"""
1049+
trusted = list_trusted(type=type)
1050+
if isinstance(trusted, list):
1051+
return name in trusted
1052+
return any(name in items for items in trusted.values())

salt/states/pkg.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3936,6 +3936,120 @@ def held(name, version=None, pkgs=None, replace=False, **kwargs):
39363936
return ret
39373937

39383938

3939+
def trusted(name, **kwargs):
3940+
"""
3941+
Ensure a package source or component is marked as trusted by the package
3942+
manager. Only available for package managers that implement a trust model
3943+
(e.g. Homebrew on macOS).
3944+
3945+
.. versionadded:: 3008.2
3946+
3947+
name
3948+
The identifier of the package source or component to trust. The exact
3949+
format depends on the package manager (e.g. a tap name, a formula, a
3950+
URL).
3951+
3952+
kwargs
3953+
Additional keyword arguments are passed through to the underlying
3954+
``pkg.trust`` and ``pkg.is_trusted`` module functions, allowing
3955+
package-manager-specific options to be supplied. For example, on macOS
3956+
with Homebrew, ``type`` can be set to ``tap``, ``formula``, ``cask``
3957+
or ``command`` to disambiguate the target.
3958+
3959+
Examples:
3960+
3961+
.. code-block:: yaml
3962+
3963+
# Homebrew: trust a third-party tap
3964+
cdalvaro/tap:
3965+
pkg.trusted:
3966+
- type: tap
3967+
3968+
# Homebrew: trust a specific formula from a third-party tap
3969+
cdalvaro/tap/salt:
3970+
pkg.trusted:
3971+
- type: formula
3972+
"""
3973+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
3974+
3975+
if "pkg.trust" not in __salt__:
3976+
ret["result"] = False
3977+
ret["comment"] = "`trust` is not available for this package manager."
3978+
return ret
3979+
3980+
if __salt__["pkg.is_trusted"](name, **kwargs):
3981+
ret["comment"] = f"{name} is already trusted."
3982+
return ret
3983+
3984+
if __opts__["test"]:
3985+
ret["result"] = None
3986+
ret["comment"] = f"{name} would be trusted."
3987+
return ret
3988+
3989+
if not __salt__["pkg.trust"](name, **kwargs):
3990+
ret["result"] = False
3991+
ret["comment"] = f"Failed to trust {name}."
3992+
return ret
3993+
3994+
ret["changes"] = {name: {"old": "untrusted", "new": "trusted"}}
3995+
ret["comment"] = f"{name} is now trusted."
3996+
return ret
3997+
3998+
3999+
def untrusted(name, **kwargs):
4000+
"""
4001+
Ensure a package source or component is not marked as trusted by the
4002+
package manager. Only available for package managers that implement a
4003+
trust model (e.g. Homebrew on macOS).
4004+
4005+
.. versionadded:: 3008.2
4006+
4007+
name
4008+
The identifier of the package source or component to untrust. The
4009+
exact format depends on the package manager.
4010+
4011+
kwargs
4012+
Additional keyword arguments are passed through to the underlying
4013+
``pkg.untrust`` and ``pkg.is_trusted`` module functions, allowing
4014+
package-manager-specific options to be supplied. For example, on macOS
4015+
with Homebrew, ``type`` can be set to ``tap``, ``formula``, ``cask``
4016+
or ``command`` to disambiguate the target.
4017+
4018+
Example:
4019+
4020+
.. code-block:: yaml
4021+
4022+
# Homebrew: remove trust from a third-party tap
4023+
cdalvaro/tap:
4024+
pkg.untrusted:
4025+
- type: tap
4026+
"""
4027+
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
4028+
4029+
if "pkg.untrust" not in __salt__:
4030+
ret["result"] = False
4031+
ret["comment"] = "`untrust` is not available for this package manager."
4032+
return ret
4033+
4034+
if not __salt__["pkg.is_trusted"](name, **kwargs):
4035+
ret["comment"] = f"{name} is already not trusted."
4036+
return ret
4037+
4038+
if __opts__["test"]:
4039+
ret["result"] = None
4040+
ret["comment"] = f"{name} would be untrusted."
4041+
return ret
4042+
4043+
if not __salt__["pkg.untrust"](name, **kwargs):
4044+
ret["result"] = False
4045+
ret["comment"] = f"Failed to untrust {name}."
4046+
return ret
4047+
4048+
ret["changes"] = {name: {"old": "trusted", "new": "untrusted"}}
4049+
ret["comment"] = f"{name} is no longer trusted."
4050+
return ret
4051+
4052+
39394053
def unheld(name, version=None, pkgs=None, all=False, **kwargs):
39404054
"""
39414055
.. versionadded:: 3005

0 commit comments

Comments
 (0)