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.
- {envrcctl-0.2.6 → envrcctl-0.2.8}/PKG-INFO +1 -1
- {envrcctl-0.2.6 → envrcctl-0.2.8}/pyproject.toml +1 -1
- {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/release_artifacts.py +35 -3
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/auth.py +6 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/keychain.py +16 -26
- {envrcctl-0.2.6 → envrcctl-0.2.8}/.gitignore +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/LICENSE +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/README.md +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.bash +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.fish +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/completions/envrcctl.zsh +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/build_macos_auth_helper.sh +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/generate_completions.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/macos/envrcctl-macos-auth.swift +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/scripts/package_for_ship.sh +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/__init__.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/audit.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/cli.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/command_runner.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/envrc.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/envrcctl-macos-auth.bak +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/errors.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/main.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/managed_block.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/secrets.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/src/envrcctl/secretservice.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/__init__.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/conftest.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/helpers/__init__.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/helpers/cli_support.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_audit.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_audit_cli.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_auth.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_doctor.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_errors.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_eval.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_exec.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_helpers.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_inject.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_migrate.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_get.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_list.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_cli_secret_set_unset.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_command_runner.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_envrc.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_keychain.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_main.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_managed_block.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_secrets.py +0 -0
- {envrcctl-0.2.6 → envrcctl-0.2.8}/tests/test_secretservice.py +0 -0
- {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.
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
|
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
|
|
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
|