envrcctl 0.2.6__tar.gz → 0.2.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {envrcctl-0.2.6 → envrcctl-0.2.8}/PKG-INFO +1 -1
  2. {envrcctl-0.2.6 → envrcctl-0.2.8}/pyproject.toml +1 -1
  3. {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/release_artifacts.py +35 -3
  4. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/auth.py +6 -0
  5. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/keychain.py +16 -26
  6. {envrcctl-0.2.6 → envrcctl-0.2.8}/.gitignore +0 -0
  7. {envrcctl-0.2.6 → envrcctl-0.2.8}/LICENSE +0 -0
  8. {envrcctl-0.2.6 → envrcctl-0.2.8}/README.md +0 -0
  9. {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.bash +0 -0
  10. {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.fish +0 -0
  11. {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.zsh +0 -0
  12. {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/build_macos_auth_helper.sh +0 -0
  13. {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/generate_completions.py +0 -0
  14. {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/macos/envrcctl-macos-auth.swift +0 -0
  15. {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/package_for_ship.sh +0 -0
  16. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/__init__.py +0 -0
  17. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/audit.py +0 -0
  18. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/cli.py +0 -0
  19. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/command_runner.py +0 -0
  20. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/envrc.py +0 -0
  21. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/envrcctl-macos-auth.bak +0 -0
  22. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/errors.py +0 -0
  23. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/main.py +0 -0
  24. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/managed_block.py +0 -0
  25. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/secrets.py +0 -0
  26. {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/secretservice.py +0 -0
  27. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/__init__.py +0 -0
  28. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/conftest.py +0 -0
  29. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/helpers/__init__.py +0 -0
  30. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/helpers/cli_support.py +0 -0
  31. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_audit.py +0 -0
  32. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_audit_cli.py +0 -0
  33. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_auth.py +0 -0
  34. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli.py +0 -0
  35. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_doctor.py +0 -0
  36. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_errors.py +0 -0
  37. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_eval.py +0 -0
  38. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_exec.py +0 -0
  39. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_helpers.py +0 -0
  40. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_inject.py +0 -0
  41. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_migrate.py +0 -0
  42. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_get.py +0 -0
  43. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_list.py +0 -0
  44. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_set_unset.py +0 -0
  45. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_command_runner.py +0 -0
  46. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_envrc.py +0 -0
  47. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_keychain.py +0 -0
  48. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_main.py +0 -0
  49. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_managed_block.py +0 -0
  50. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_secrets.py +0 -0
  51. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_secretservice.py +0 -0
  52. {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: envrcctl
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: Manage .envrc with managed blocks and OS-backed secrets.
5
5
  Project-URL: Homepage, https://github.com/rioriost/envrcctl
6
6
  Project-URL: Issues, https://github.com/rioriost/envrcctl/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "envrcctl"
3
- version = "0.2.6"
3
+ version = "0.2.8"
4
4
  description = "Manage .envrc with managed blocks and OS-backed secrets."
5
5
  readme = { file = "README.md", content-type = "text/markdown" }
6
6
  license = "MIT"
@@ -70,6 +70,24 @@ def dependency_name(requirement: str) -> str:
70
70
  return normalize_package_name(base)
71
71
 
72
72
 
73
+ def package_dependencies(block: str) -> list[str]:
74
+ deps_match = re.search(r"dependencies = \[(.*?)\]\n", block, re.DOTALL)
75
+ if not deps_match:
76
+ return []
77
+
78
+ dependencies: list[str] = []
79
+ for match in re.finditer(r'\{ name = "([^"]+)"(?:, marker = "([^"]+)")?', deps_match.group(1)):
80
+ name = normalize_package_name(match.group(1))
81
+ marker = match.group(2)
82
+ if marker and "win32" in marker.lower():
83
+ continue
84
+ if marker and "windows" in marker.lower():
85
+ continue
86
+ dependencies.append(name)
87
+
88
+ return dependencies
89
+
90
+
73
91
  def uv_lock_package_block(lock_text: str, package_name: str) -> str:
74
92
  normalized = normalize_package_name(package_name)
75
93
  current: list[str] = []
@@ -111,10 +129,24 @@ def extract_sdist_url_and_sha(block: str, package_name: str) -> tuple[str, str]:
111
129
 
112
130
  def dependency_resource_specs(repo_root: Path) -> list[tuple[str, str, str]]:
113
131
  lock_text = (repo_root / "uv.lock").read_text(encoding="utf-8")
114
- specs: list[tuple[str, str, str]] = []
132
+ ordered_names: list[str] = []
133
+ seen: set[str] = set()
134
+ queue = [dependency_name(req) for req in project_dependencies(repo_root / "pyproject.toml")]
135
+
136
+ while queue:
137
+ package_name = normalize_package_name(queue.pop(0))
138
+ if package_name in seen:
139
+ continue
140
+ seen.add(package_name)
141
+ ordered_names.append(package_name)
115
142
 
116
- for requirement in project_dependencies(repo_root / "pyproject.toml"):
117
- package_name = dependency_name(requirement)
143
+ block = uv_lock_package_block(lock_text, package_name)
144
+ for dependency in package_dependencies(block):
145
+ if dependency not in seen and dependency not in queue:
146
+ queue.append(dependency)
147
+
148
+ specs: list[tuple[str, str, str]] = []
149
+ for package_name in ordered_names:
118
150
  block = uv_lock_package_block(lock_text, package_name)
119
151
  url, sha256 = extract_sdist_url_and_sha(block, package_name)
120
152
  specs.append((package_name, url, sha256))
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ import shutil
4
5
  import subprocess
5
6
  import sys
6
7
  from pathlib import Path
@@ -19,6 +20,11 @@ def _helper_path() -> Path:
19
20
  configured = os.getenv(_HELPER_ENV_VAR)
20
21
  if configured:
21
22
  return Path(configured).expanduser()
23
+
24
+ helper_on_path = shutil.which(_DEFAULT_HELPER_BASENAME)
25
+ if helper_on_path:
26
+ return Path(helper_on_path)
27
+
22
28
  return _default_helper_path()
23
29
 
24
30
 
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import os
5
+ import shutil
5
6
  from pathlib import Path
6
7
  from typing import List
7
8
 
@@ -20,6 +21,11 @@ class KeychainBackend(SecretBackend):
20
21
  configured = os.getenv(self.HELPER_ENV_VAR)
21
22
  if configured:
22
23
  return Path(configured).expanduser()
24
+
25
+ on_path = shutil.which(self.DEFAULT_HELPER_BASENAME)
26
+ if on_path:
27
+ return Path(on_path)
28
+
23
29
  return Path(__file__).resolve().parent / self.DEFAULT_HELPER_BASENAME
24
30
 
25
31
  def _ensure_helper_ready(self, helper_path: Path) -> None:
@@ -30,19 +36,16 @@ class KeychainBackend(SecretBackend):
30
36
  )
31
37
  if not helper_path.is_file():
32
38
  raise EnvrcctlError(
33
- "macOS authentication helper path is invalid. "
34
- "Expected an executable file."
39
+ "macOS authentication helper path is invalid. Expected an executable file."
35
40
  )
36
41
  if not os.access(helper_path, os.X_OK):
37
42
  raise EnvrcctlError(
38
- "macOS authentication helper is not executable. "
39
- "Fix permissions and retry."
43
+ "macOS authentication helper is not executable. Fix permissions and retry."
40
44
  )
41
45
 
42
46
  def _build_auth_reason(self, action: str, ref: SecretRef) -> str:
43
47
  return (
44
- f"envrcctl needs device owner authentication to {action} "
45
- f"the secret for {ref.account}."
48
+ f"envrcctl needs device owner authentication to {action} the secret for {ref.account}."
46
49
  )
47
50
 
48
51
  def _run_auth_helper(self, args: list[str], input_text: str | None = None) -> str:
@@ -85,9 +88,7 @@ class KeychainBackend(SecretBackend):
85
88
  seen_refs.add(key)
86
89
  unique_refs.append(ref)
87
90
 
88
- items = [
89
- {"service": ref.service, "account": ref.account} for ref in unique_refs
90
- ]
91
+ items = [{"service": ref.service, "account": ref.account} for ref in unique_refs]
91
92
  payload = json.dumps({"items": items})
92
93
 
93
94
  output = self._run_auth_helper(
@@ -108,22 +109,16 @@ class KeychainBackend(SecretBackend):
108
109
  try:
109
110
  decoded = json.loads(output)
110
111
  except json.JSONDecodeError as exc:
111
- raise EnvrcctlError(
112
- "Authenticated Keychain helper returned invalid JSON."
113
- ) from exc
112
+ raise EnvrcctlError("Authenticated Keychain helper returned invalid JSON.") from exc
114
113
 
115
114
  raw_items = decoded.get("items")
116
115
  if not isinstance(raw_items, list):
117
- raise EnvrcctlError(
118
- "Authenticated Keychain helper returned an invalid response."
119
- )
116
+ raise EnvrcctlError("Authenticated Keychain helper returned an invalid response.")
120
117
 
121
118
  values: dict[tuple[str, str], str] = {}
122
119
  for item in raw_items:
123
120
  if not isinstance(item, dict):
124
- raise EnvrcctlError(
125
- "Authenticated Keychain helper returned an invalid item."
126
- )
121
+ raise EnvrcctlError("Authenticated Keychain helper returned an invalid item.")
127
122
  service = item.get("service")
128
123
  account = item.get("account")
129
124
  value = item.get("value")
@@ -132,9 +127,7 @@ class KeychainBackend(SecretBackend):
132
127
  "Authenticated Keychain helper returned an invalid item payload."
133
128
  )
134
129
  if not isinstance(value, str):
135
- raise EnvrcctlError(
136
- "Authenticated Keychain helper response is missing a value."
137
- )
130
+ raise EnvrcctlError("Authenticated Keychain helper response is missing a value.")
138
131
  key = (service, account)
139
132
  if key in values:
140
133
  raise EnvrcctlError(
@@ -145,12 +138,9 @@ class KeychainBackend(SecretBackend):
145
138
  expected = {(ref.service, ref.account) for ref in unique_refs}
146
139
  missing = expected - set(values.keys())
147
140
  if missing:
148
- missing_list = ", ".join(
149
- f"{service}/{account}" for service, account in sorted(missing)
150
- )
141
+ missing_list = ", ".join(f"{service}/{account}" for service, account in sorted(missing))
151
142
  raise EnvrcctlError(
152
- "Authenticated Keychain helper response is missing secrets: "
153
- f"{missing_list}"
143
+ f"Authenticated Keychain helper response is missing secrets: {missing_list}"
154
144
  )
155
145
 
156
146
  return values
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes