envrcctl 0.2.1__tar.gz → 0.2.3__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 (53) hide show
  1. {envrcctl-0.2.1 → envrcctl-0.2.3}/.gitignore +4 -0
  2. envrcctl-0.2.1/README.md → envrcctl-0.2.3/PKG-INFO +25 -49
  3. envrcctl-0.2.1/PKG-INFO → envrcctl-0.2.3/README.md +7 -83
  4. envrcctl-0.2.3/pyproject.toml +67 -0
  5. {envrcctl-0.2.1 → envrcctl-0.2.3}/scripts/build_macos_auth_helper.sh +6 -1
  6. envrcctl-0.2.3/scripts/package_for_ship.sh +6 -0
  7. envrcctl-0.2.3/scripts/release_artifacts.py +286 -0
  8. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/cli.py +15 -44
  9. envrcctl-0.2.3/tests/__init__.py +3 -0
  10. envrcctl-0.2.3/tests/conftest.py +15 -0
  11. envrcctl-0.2.3/tests/helpers/__init__.py +3 -0
  12. envrcctl-0.2.3/tests/helpers/cli_support.py +34 -0
  13. envrcctl-0.2.3/tests/test_audit.py +830 -0
  14. envrcctl-0.2.3/tests/test_audit_cli.py +364 -0
  15. envrcctl-0.2.3/tests/test_auth.py +181 -0
  16. envrcctl-0.2.3/tests/test_cli.py +81 -0
  17. envrcctl-0.2.3/tests/test_cli_doctor.py +213 -0
  18. envrcctl-0.2.3/tests/test_cli_errors.py +485 -0
  19. envrcctl-0.2.3/tests/test_cli_eval.py +84 -0
  20. envrcctl-0.2.3/tests/test_cli_exec.py +361 -0
  21. envrcctl-0.2.3/tests/test_cli_helpers.py +150 -0
  22. envrcctl-0.2.3/tests/test_cli_inject.py +222 -0
  23. envrcctl-0.2.3/tests/test_cli_migrate.py +50 -0
  24. envrcctl-0.2.3/tests/test_cli_secret_get.py +304 -0
  25. envrcctl-0.2.3/tests/test_cli_secret_list.py +66 -0
  26. envrcctl-0.2.3/tests/test_cli_secret_set_unset.py +138 -0
  27. envrcctl-0.2.3/tests/test_command_runner.py +51 -0
  28. envrcctl-0.2.3/tests/test_envrc.py +147 -0
  29. envrcctl-0.2.3/tests/test_keychain.py +609 -0
  30. envrcctl-0.2.3/tests/test_main.py +30 -0
  31. envrcctl-0.2.3/tests/test_managed_block.py +68 -0
  32. envrcctl-0.2.3/tests/test_secrets.py +186 -0
  33. envrcctl-0.2.3/tests/test_secretservice.py +91 -0
  34. envrcctl-0.2.3/tests/test_smoke.py +4 -0
  35. envrcctl-0.2.1/pyproject.toml +0 -31
  36. {envrcctl-0.2.1 → envrcctl-0.2.3}/LICENSE +0 -0
  37. {envrcctl-0.2.1 → envrcctl-0.2.3}/completions/envrcctl.bash +0 -0
  38. {envrcctl-0.2.1 → envrcctl-0.2.3}/completions/envrcctl.fish +0 -0
  39. {envrcctl-0.2.1 → envrcctl-0.2.3}/completions/envrcctl.zsh +0 -0
  40. {envrcctl-0.2.1 → envrcctl-0.2.3}/scripts/generate_completions.py +0 -0
  41. {envrcctl-0.2.1 → envrcctl-0.2.3}/scripts/macos/envrcctl-macos-auth.swift +0 -0
  42. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/__init__.py +0 -0
  43. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/audit.py +0 -0
  44. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/auth.py +0 -0
  45. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/command_runner.py +0 -0
  46. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/envrc.py +0 -0
  47. /envrcctl-0.2.1/src/envrcctl/envrcctl-macos-auth → /envrcctl-0.2.3/src/envrcctl/envrcctl-macos-auth.bak +0 -0
  48. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/errors.py +0 -0
  49. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/keychain.py +0 -0
  50. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/main.py +0 -0
  51. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/managed_block.py +0 -0
  52. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/secrets.py +0 -0
  53. {envrcctl-0.2.1 → envrcctl-0.2.3}/src/envrcctl/secretservice.py +0 -0
@@ -9,3 +9,7 @@ __pycache__/
9
9
  htmlcov/
10
10
  .DS_Store
11
11
  .envrc
12
+ dist/helper/
13
+ dist/*-arm64.tar.gz
14
+ dist/*-darwin-arm64.tar.gz
15
+ src/*.egg-info/
@@ -1,3 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: envrcctl
3
+ Version: 0.2.3
4
+ Summary: Manage .envrc with managed blocks and OS-backed secrets.
5
+ Project-URL: Homepage, https://github.com/rioriost/envrcctl
6
+ Project-URL: Issues, https://github.com/rioriost/envrcctl/issues
7
+ Project-URL: Repository, https://github.com/rioriost/envrcctl
8
+ Author-email: Rio Fujita <rio_github@rio.st>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.14
12
+ Requires-Dist: typer>=0.24.1
13
+ Provides-Extra: test
14
+ Requires-Dist: bandit>=1.7.10; extra == 'test'
15
+ Requires-Dist: pytest-cov>=7; extra == 'test'
16
+ Requires-Dist: pytest>=9; extra == 'test'
17
+ Description-Content-Type: text/markdown
18
+
1
19
  # envrcctl
2
20
 
3
21
  envrcctl is a CLI tool that manages `.envrc` files safely through a managed
@@ -34,23 +52,17 @@ It is designed for macOS first, with Linux support via SecretService.
34
52
  Tap and install:
35
53
 
36
54
  ```sh
37
- brew tap rioriost/envrcctl
55
+ brew tap rioriost/tap
38
56
  brew install envrcctl
39
57
  ```
40
58
 
41
- For the next patch release, the Homebrew formula is intended to install a
42
- prebuilt Apple Silicon macOS authentication helper from a GitHub release
43
- `tar.gz` asset instead of compiling it at install time.
44
-
45
- This Homebrew path is therefore intended for:
59
+ This Homebrew path is intended for:
46
60
 
47
61
  - Apple Silicon (`arm64`) Macs
48
62
  - macOS installs that should not require a full Xcode.app build dependency
49
63
 
50
64
  Intel Macs are not a target for this Homebrew distribution path.
51
65
 
52
- After release, Homebrew will download the release from GitHub.
53
-
54
66
  Install direnv with Homebrew:
55
67
 
56
68
  ```sh
@@ -69,42 +81,25 @@ pipx install envrcctl
69
81
  uv tool install envrcctl
70
82
  ```
71
83
 
72
- ### From source (macOS/Linux)
73
-
74
- ```sh
75
- git clone <REPO_URL>
76
- cd envrcctl
77
- uv sync
78
- uv run python -m envrcctl.main --help
79
- ```
80
-
81
- ### Build the macOS auth helper manually (macOS only)
84
+ ### About the macOS auth helper (Apple Silicon macOS only)
82
85
 
83
86
  The macOS device owner authentication flow requires a native helper named
84
87
  `envrcctl-macos-auth`.
85
88
 
86
- On Apple Silicon macOS, the Homebrew installation path for the next patch
87
- release is intended to install a prebuilt helper automatically from a GitHub
88
- release `tar.gz` asset, so you should not need to compile it yourself in the
89
- common case.
89
+ Homebrew on Apple Silicon is intended to install this helper automatically, so
90
+ you should not need to build it yourself in the common case.
90
91
 
91
92
  Manual helper installation is still useful when:
92
93
 
93
- - you are running from source
94
- - you are developing on this repository
95
94
  - you want to place the helper in a custom location
96
95
  - you are not using the Apple Silicon Homebrew distribution path
97
96
 
98
- The Homebrew release asset is expected to be named
99
- `envrcctl-macos-auth-arm64.tar.gz` and to contain an executable named
100
- `envrcctl-macos-auth`.
101
-
102
- If you are building the helper yourself, place the binary at either:
97
+ If you are building the helper yourself, use Apple Silicon (`arm64`) macOS and place the binary at either:
103
98
 
104
99
  - `src/envrcctl/envrcctl-macos-auth`
105
100
  - or a custom path set via `ENVRCCTL_MACOS_AUTH_HELPER`
106
101
 
107
- Example build flow:
102
+ Example build flow on Apple Silicon macOS:
108
103
 
109
104
  ```sh
110
105
  swiftc -O -framework LocalAuthentication -framework Security \
@@ -113,20 +108,6 @@ swiftc -O -framework LocalAuthentication -framework Security \
113
108
  chmod +x src/envrcctl/envrcctl-macos-auth
114
109
  ```
115
110
 
116
- You can also use the repository build script:
117
-
118
- ```sh
119
- sh scripts/build_macos_auth_helper.sh
120
- ```
121
-
122
- If you want to write the helper to a custom location, pass the source and output paths explicitly:
123
-
124
- ```sh
125
- sh scripts/build_macos_auth_helper.sh \
126
- scripts/macos/envrcctl-macos-auth.swift \
127
- /usr/local/bin/envrcctl-macos-auth
128
- ```
129
-
130
111
  If you install the helper elsewhere, set:
131
112
 
132
113
  ```sh
@@ -373,12 +354,7 @@ uv run python scripts/generate_completions.py
373
354
  - Audit integrity is based on a hash chain and can be checked with `envrcctl audit verify`
374
355
  - The tool refuses to write to world-writable `.envrc`
375
356
 
376
- ## Development
377
357
 
378
- ```sh
379
- uv sync
380
- .venv/bin/envrcctl --help
381
- ```
382
358
 
383
359
  ## Acknowledgements
384
360
 
@@ -1,37 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: envrcctl
3
- Version: 0.2.1
4
- Summary: Manage .envrc with managed blocks and OS-backed secrets.
5
- License: MIT License
6
-
7
- Copyright (c) 2026 Rio Fujita
8
-
9
- Permission is hereby granted, free of charge, to any person obtaining a copy
10
- of this software and associated documentation files (the "Software"), to deal
11
- in the Software without restriction, including without limitation the rights
12
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- copies of the Software, and to permit persons to whom the Software is
14
- furnished to do so, subject to the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be included in all
17
- copies or substantial portions of the Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
- SOFTWARE.
26
- License-File: LICENSE
27
- Requires-Python: >=3.14
28
- Requires-Dist: typer>=0.24.1
29
- Provides-Extra: test
30
- Requires-Dist: bandit>=1.7.10; extra == 'test'
31
- Requires-Dist: pytest-cov>=7.0.0; extra == 'test'
32
- Requires-Dist: pytest>=9.0.2; extra == 'test'
33
- Description-Content-Type: text/markdown
34
-
35
1
  # envrcctl
36
2
 
37
3
  envrcctl is a CLI tool that manages `.envrc` files safely through a managed
@@ -68,23 +34,17 @@ It is designed for macOS first, with Linux support via SecretService.
68
34
  Tap and install:
69
35
 
70
36
  ```sh
71
- brew tap rioriost/envrcctl
37
+ brew tap rioriost/tap
72
38
  brew install envrcctl
73
39
  ```
74
40
 
75
- For the next patch release, the Homebrew formula is intended to install a
76
- prebuilt Apple Silicon macOS authentication helper from a GitHub release
77
- `tar.gz` asset instead of compiling it at install time.
78
-
79
- This Homebrew path is therefore intended for:
41
+ This Homebrew path is intended for:
80
42
 
81
43
  - Apple Silicon (`arm64`) Macs
82
44
  - macOS installs that should not require a full Xcode.app build dependency
83
45
 
84
46
  Intel Macs are not a target for this Homebrew distribution path.
85
47
 
86
- After release, Homebrew will download the release from GitHub.
87
-
88
48
  Install direnv with Homebrew:
89
49
 
90
50
  ```sh
@@ -103,42 +63,25 @@ pipx install envrcctl
103
63
  uv tool install envrcctl
104
64
  ```
105
65
 
106
- ### From source (macOS/Linux)
107
-
108
- ```sh
109
- git clone <REPO_URL>
110
- cd envrcctl
111
- uv sync
112
- uv run python -m envrcctl.main --help
113
- ```
114
-
115
- ### Build the macOS auth helper manually (macOS only)
66
+ ### About the macOS auth helper (Apple Silicon macOS only)
116
67
 
117
68
  The macOS device owner authentication flow requires a native helper named
118
69
  `envrcctl-macos-auth`.
119
70
 
120
- On Apple Silicon macOS, the Homebrew installation path for the next patch
121
- release is intended to install a prebuilt helper automatically from a GitHub
122
- release `tar.gz` asset, so you should not need to compile it yourself in the
123
- common case.
71
+ Homebrew on Apple Silicon is intended to install this helper automatically, so
72
+ you should not need to build it yourself in the common case.
124
73
 
125
74
  Manual helper installation is still useful when:
126
75
 
127
- - you are running from source
128
- - you are developing on this repository
129
76
  - you want to place the helper in a custom location
130
77
  - you are not using the Apple Silicon Homebrew distribution path
131
78
 
132
- The Homebrew release asset is expected to be named
133
- `envrcctl-macos-auth-arm64.tar.gz` and to contain an executable named
134
- `envrcctl-macos-auth`.
135
-
136
- If you are building the helper yourself, place the binary at either:
79
+ If you are building the helper yourself, use Apple Silicon (`arm64`) macOS and place the binary at either:
137
80
 
138
81
  - `src/envrcctl/envrcctl-macos-auth`
139
82
  - or a custom path set via `ENVRCCTL_MACOS_AUTH_HELPER`
140
83
 
141
- Example build flow:
84
+ Example build flow on Apple Silicon macOS:
142
85
 
143
86
  ```sh
144
87
  swiftc -O -framework LocalAuthentication -framework Security \
@@ -147,20 +90,6 @@ swiftc -O -framework LocalAuthentication -framework Security \
147
90
  chmod +x src/envrcctl/envrcctl-macos-auth
148
91
  ```
149
92
 
150
- You can also use the repository build script:
151
-
152
- ```sh
153
- sh scripts/build_macos_auth_helper.sh
154
- ```
155
-
156
- If you want to write the helper to a custom location, pass the source and output paths explicitly:
157
-
158
- ```sh
159
- sh scripts/build_macos_auth_helper.sh \
160
- scripts/macos/envrcctl-macos-auth.swift \
161
- /usr/local/bin/envrcctl-macos-auth
162
- ```
163
-
164
93
  If you install the helper elsewhere, set:
165
94
 
166
95
  ```sh
@@ -407,12 +336,7 @@ uv run python scripts/generate_completions.py
407
336
  - Audit integrity is based on a hash chain and can be checked with `envrcctl audit verify`
408
337
  - The tool refuses to write to world-writable `.envrc`
409
338
 
410
- ## Development
411
339
 
412
- ```sh
413
- uv sync
414
- .venv/bin/envrcctl --help
415
- ```
416
340
 
417
341
  ## Acknowledgements
418
342
 
@@ -0,0 +1,67 @@
1
+ [project]
2
+ name = "envrcctl"
3
+ version = "0.2.3"
4
+ description = "Manage .envrc with managed blocks and OS-backed secrets."
5
+ readme = { file = "README.md", content-type = "text/markdown" }
6
+ license = "MIT"
7
+ requires-python = ">=3.14"
8
+ authors = [
9
+ { name = "Rio Fujita", email = "rio_github@rio.st" },
10
+ ]
11
+ dependencies = [
12
+ "typer>=0.24.1",
13
+ ]
14
+
15
+ [tool.hatch.build.targets.wheel]
16
+ packages = ["src/envrcctl"]
17
+
18
+ [tool.hatch.build.targets.sdist]
19
+ include = [
20
+ "src/**",
21
+ "tests/**",
22
+ "scripts/**",
23
+ "completions/**",
24
+ "README.md",
25
+ "LICENSE",
26
+ "pyproject.toml",
27
+ ]
28
+ exclude = [
29
+ "src/envrcctl/envrcctl-macos-auth",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/rioriost/envrcctl"
34
+ Issues = "https://github.com/rioriost/envrcctl/issues"
35
+ Repository = "https://github.com/rioriost/envrcctl"
36
+
37
+ [project.scripts]
38
+ envrcctl = "envrcctl.main:main"
39
+
40
+ [project.optional-dependencies]
41
+ test = [
42
+ "pytest>=9",
43
+ "pytest-cov>=7",
44
+ "bandit>=1.7.10",
45
+ ]
46
+
47
+ [dependency-groups]
48
+ dev = [
49
+ "build>=1.2.2",
50
+ "twine>=6.1.0",
51
+ "ruff>=0.12.0",
52
+ ]
53
+
54
+ [build-system]
55
+ requires = ["hatchling>=1.25.0"]
56
+ build-backend = "hatchling.build"
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
60
+ python_files = ["test_*.py"]
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py314"
65
+
66
+ [tool.ruff.lint]
67
+ select = ["E", "F", "I", "B", "UP"]
@@ -11,6 +11,11 @@ if [ "$(uname -s)" != "Darwin" ]; then
11
11
  exit 1
12
12
  fi
13
13
 
14
+ if [ "$(uname -m)" != "arm64" ]; then
15
+ echo "This helper only supports Apple Silicon (arm64) macOS." >&2
16
+ exit 1
17
+ fi
18
+
14
19
  if ! command -v swiftc >/dev/null 2>&1; then
15
20
  echo "swiftc not found. Install Xcode Command Line Tools first." >&2
16
21
  exit 1
@@ -24,7 +29,7 @@ fi
24
29
 
25
30
  mkdir -p "$(dirname "$OUTPUT_PATH")"
26
31
 
27
- echo "Building macOS auth helper..."
32
+ echo "Building macOS auth helper for Apple Silicon (arm64)..."
28
33
  echo " source: $SWIFT_SOURCE"
29
34
  echo " output: $OUTPUT_PATH"
30
35
 
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ echo "scripts/package_for_ship.sh is obsolete." >&2
5
+ echo "Run 'make release-artifacts' from the repository root instead." >&2
6
+ exit 1
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import hashlib
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ import tarfile
10
+ import tempfile
11
+ from pathlib import Path
12
+
13
+
14
+ def run(cmd: list[str], *, cwd: Path) -> None:
15
+ print("+", " ".join(cmd))
16
+ subprocess.run(cmd, cwd=cwd, check=True)
17
+
18
+
19
+ def sha256_file(path: Path) -> str:
20
+ digest = hashlib.sha256()
21
+ with path.open("rb") as fh:
22
+ for chunk in iter(lambda: fh.read(1024 * 1024), b""):
23
+ digest.update(chunk)
24
+ return digest.hexdigest()
25
+
26
+
27
+ def project_root() -> Path:
28
+ return Path(__file__).resolve().parents[1]
29
+
30
+
31
+ def project_version(pyproject_path: Path) -> str:
32
+ for line in pyproject_path.read_text(encoding="utf-8").splitlines():
33
+ stripped = line.strip()
34
+ if stripped.startswith("version = "):
35
+ value = stripped.split("=", 1)[1].strip()
36
+ if value.startswith('"') and value.endswith('"'):
37
+ return value[1:-1]
38
+ if value.startswith("'") and value.endswith("'"):
39
+ return value[1:-1]
40
+ raise RuntimeError(f"Could not find project version in {pyproject_path}")
41
+
42
+
43
+ def require_command(name: str) -> None:
44
+ if shutil.which(name) is None:
45
+ raise RuntimeError(f"Required command not found in PATH: {name}")
46
+
47
+
48
+ def ensure_macos_arm64() -> None:
49
+ if sys.platform != "darwin":
50
+ raise RuntimeError("This script must run on macOS.")
51
+ machine = getattr(__import__("os"), "uname")().machine
52
+ if machine != "arm64":
53
+ raise RuntimeError("This script only supports Apple Silicon (arm64) macOS.")
54
+
55
+
56
+ def dist_dir(repo_root: Path) -> Path:
57
+ return repo_root / "dist"
58
+
59
+
60
+ def helper_output_path(repo_root: Path) -> Path:
61
+ return repo_root / "src" / "envrcctl" / "envrcctl-macos-auth"
62
+
63
+
64
+ def generate_completions(repo_root: Path) -> None:
65
+ require_command("uv")
66
+ run(["uv", "run", "python", "scripts/generate_completions.py"], cwd=repo_root)
67
+
68
+ expected = [
69
+ repo_root / "completions" / "envrcctl.bash",
70
+ repo_root / "completions" / "envrcctl.zsh",
71
+ repo_root / "completions" / "envrcctl.fish",
72
+ ]
73
+ missing = [path for path in expected if not path.exists()]
74
+ if missing:
75
+ joined = ", ".join(str(path) for path in missing)
76
+ raise RuntimeError(f"Completion generation did not create expected files: {joined}")
77
+
78
+
79
+ def sync_dev_environment(repo_root: Path) -> None:
80
+ require_command("uv")
81
+ run(["uv", "sync", "--extra", "test", "--group", "dev"], cwd=repo_root)
82
+
83
+
84
+ def clean_dist(repo_root: Path) -> None:
85
+ out = dist_dir(repo_root)
86
+ if out.exists():
87
+ shutil.rmtree(out)
88
+ out.mkdir(parents=True, exist_ok=True)
89
+
90
+
91
+ def build_python_artifacts(repo_root: Path) -> tuple[Path, Path]:
92
+ require_command("uv")
93
+ run(["uv", "build"], cwd=repo_root)
94
+
95
+ version = project_version(repo_root / "pyproject.toml")
96
+ sdist = dist_dir(repo_root) / f"envrcctl-{version}.tar.gz"
97
+ wheel = dist_dir(repo_root) / f"envrcctl-{version}-py3-none-any.whl"
98
+
99
+ if not sdist.exists():
100
+ raise RuntimeError(f"Expected sdist was not created: {sdist}")
101
+ if not wheel.exists():
102
+ raise RuntimeError(f"Expected wheel was not created: {wheel}")
103
+
104
+ return sdist, wheel
105
+
106
+
107
+ def build_helper_binary(repo_root: Path) -> Path:
108
+ ensure_macos_arm64()
109
+ run(["sh", "scripts/build_macos_auth_helper.sh"], cwd=repo_root)
110
+
111
+ helper_path = helper_output_path(repo_root)
112
+ if not helper_path.exists():
113
+ raise RuntimeError(f"Expected helper binary was not created: {helper_path}")
114
+ if not helper_path.is_file():
115
+ raise RuntimeError(f"Helper path is not a file: {helper_path}")
116
+ return helper_path
117
+
118
+
119
+ def package_helper_archive(repo_root: Path, version: str, helper_binary: Path) -> Path:
120
+ archive_path = dist_dir(repo_root) / f"envrcctl-macos-auth-{version}-arm64.tar.gz"
121
+
122
+ with tempfile.TemporaryDirectory() as tmpdir:
123
+ stage_dir = Path(tmpdir)
124
+ staged_binary = stage_dir / "envrcctl-macos-auth"
125
+ shutil.copy2(helper_binary, staged_binary)
126
+ staged_binary.chmod(0o755)
127
+
128
+ with tarfile.open(archive_path, "w:gz") as tf:
129
+ tf.add(staged_binary, arcname="envrcctl-macos-auth")
130
+
131
+ if not archive_path.exists():
132
+ raise RuntimeError(f"Expected helper archive was not created: {archive_path}")
133
+
134
+ return archive_path
135
+
136
+
137
+ def formula_content(
138
+ *,
139
+ version: str,
140
+ source_sha256: str,
141
+ helper_sha256: str,
142
+ homepage: str,
143
+ license_name: str,
144
+ ) -> str:
145
+ release_base = f"{homepage}/releases/download/{version}"
146
+ source_url = f"{release_base}/envrcctl-{version}.tar.gz"
147
+ helper_url = f"{release_base}/envrcctl-macos-auth-{version}-arm64.tar.gz"
148
+
149
+ return f"""class Envrcctl < Formula
150
+ include Language::Python::Virtualenv
151
+
152
+ desc "Manage .envrc with managed blocks and OS-backed secrets"
153
+ homepage "{homepage}"
154
+ url "{source_url}"
155
+ sha256 "{source_sha256}"
156
+ license "{license_name}"
157
+
158
+ depends_on "python@3.12"
159
+
160
+ on_macos do
161
+ on_arm do
162
+ resource "envrcctl-macos-auth-arm64" do
163
+ url "{helper_url}"
164
+ sha256 "{helper_sha256}"
165
+ end
166
+ end
167
+ end
168
+
169
+ def install
170
+ venv = virtualenv_create(libexec, "python3.12")
171
+ venv.pip_install buildpath
172
+
173
+ bin.install_symlink libexec/"bin/envrcctl"
174
+
175
+ if OS.mac? && Hardware::CPU.arm?
176
+ resource("envrcctl-macos-auth-arm64").stage do
177
+ bin.install "envrcctl-macos-auth"
178
+ end
179
+ end
180
+
181
+ bash_completion.install "completions/envrcctl.bash" => "envrcctl"
182
+ zsh_completion.install "completions/envrcctl.zsh" => "_envrcctl"
183
+ fish_completion.install "completions/envrcctl.fish"
184
+ end
185
+
186
+ test do
187
+ assert_predicate bin/"envrcctl", :exist?
188
+ assert_match version.to_s, shell_output("#{bin}/envrcctl --version")
189
+ if OS.mac? && Hardware::CPU.arm?
190
+ assert_predicate bin/"envrcctl-macos-auth", :exist?
191
+ end
192
+ end
193
+ end
194
+ """
195
+
196
+
197
+ def write_formula(repo_root: Path, formula_text: str, formula_dir: Path | None) -> Path:
198
+ target_dir = formula_dir or (repo_root.parent / "homebrew-tap" / "Formula")
199
+ target_dir.mkdir(parents=True, exist_ok=True)
200
+ formula_path = target_dir / "envrcctl.rb"
201
+ formula_path.write_text(formula_text, encoding="utf-8")
202
+ return formula_path
203
+
204
+
205
+ def parse_args() -> argparse.Namespace:
206
+ parser = argparse.ArgumentParser(
207
+ description=(
208
+ "Build envrcctl release artifacts: sync dev dependencies, generate completions, "
209
+ "build Python artifacts, build the Apple Silicon helper, package the helper tarball, "
210
+ "and write a Homebrew formula. This is the canonical script entrypoint behind "
211
+ "`make release-artifacts`."
212
+ )
213
+ )
214
+ parser.add_argument(
215
+ "--homepage",
216
+ default="https://github.com/rioriost/envrcctl",
217
+ help="Project homepage / GitHub repository URL.",
218
+ )
219
+ parser.add_argument(
220
+ "--license",
221
+ dest="license_name",
222
+ default="MIT",
223
+ help="Homebrew formula license identifier.",
224
+ )
225
+ parser.add_argument(
226
+ "--formula-dir",
227
+ type=Path,
228
+ default=None,
229
+ help="Directory to write envrcctl.rb into. Defaults to ../homebrew-tap/Formula.",
230
+ )
231
+ return parser.parse_args()
232
+
233
+
234
+ def main() -> int:
235
+ args = parse_args()
236
+ repo_root = project_root()
237
+ version = project_version(repo_root / "pyproject.toml")
238
+
239
+ print(f"Building release artifacts for envrcctl {version}")
240
+ print(f"Repository root: {repo_root}")
241
+
242
+ sdist_path = dist_dir(repo_root) / f"envrcctl-{version}.tar.gz"
243
+ wheel_path = dist_dir(repo_root) / f"envrcctl-{version}-py3-none-any.whl"
244
+ helper_archive = dist_dir(repo_root) / f"envrcctl-macos-auth-{version}-arm64.tar.gz"
245
+
246
+ if not sdist_path.exists() or not wheel_path.exists() or not helper_archive.exists():
247
+ sync_dev_environment(repo_root)
248
+ generate_completions(repo_root)
249
+ clean_dist(repo_root)
250
+ sdist_path, wheel_path = build_python_artifacts(repo_root)
251
+ helper_binary = build_helper_binary(repo_root)
252
+ helper_archive = package_helper_archive(repo_root, version, helper_binary)
253
+ else:
254
+ print("Reusing existing release artifacts from dist/")
255
+
256
+ source_sha256 = sha256_file(sdist_path)
257
+ helper_sha256 = sha256_file(helper_archive)
258
+
259
+ formula_path = write_formula(
260
+ repo_root,
261
+ formula_content(
262
+ version=version,
263
+ source_sha256=source_sha256,
264
+ helper_sha256=helper_sha256,
265
+ homepage=args.homepage,
266
+ license_name=args.license_name,
267
+ ),
268
+ args.formula_dir,
269
+ )
270
+
271
+ print()
272
+ print("Artifacts built successfully:")
273
+ print(f"- sdist: {sdist_path}")
274
+ print(f"- wheel: {wheel_path}")
275
+ print(f"- helper: {helper_archive}")
276
+ print(f"- formula: {formula_path}")
277
+ print()
278
+ print("SHA256:")
279
+ print(f"- envrcctl-{version}.tar.gz: {source_sha256}")
280
+ print(f"- envrcctl-macos-auth-{version}-arm64.tar.gz: {helper_sha256}")
281
+
282
+ return 0
283
+
284
+
285
+ if __name__ == "__main__":
286
+ raise SystemExit(main())