fujin-secrets-doppler 0.16.0__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.
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: fujin-secrets-doppler
|
|
3
|
+
Version: 0.16.0
|
|
4
|
+
Summary: Doppler secret adapter for Fujin
|
|
5
|
+
Author: Tobi
|
|
6
|
+
Author-email: Tobi <tobidegnon@proton.me>
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: fujin-cli>=0.16
|
|
14
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Project-URL: Homepage, https://github.com/Tobi-De/fujin
|
|
17
|
+
Project-URL: Issues, https://github.com/Tobi-De/fujin/issues
|
|
18
|
+
Project-URL: Source, https://github.com/Tobi-De/fujin
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Fujin Secrets - Doppler
|
|
22
|
+
|
|
23
|
+
Doppler secret adapter for Fujin deployment tool.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install fujin-secrets-doppler
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or with uv:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv pip install fujin-secrets-doppler
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
Download and install the [Doppler CLI](https://docs.doppler.com/docs/cli) and sign in to your account.
|
|
40
|
+
|
|
41
|
+
Move to your project root directory and run `doppler setup` to configure your project.
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
Add the following to your `fujin.toml` file:
|
|
46
|
+
|
|
47
|
+
```toml
|
|
48
|
+
[secrets]
|
|
49
|
+
adapter = "doppler"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
In your environment file (`.env` or configured via `envfile` in `fujin.toml`), prefix secret values with `$`:
|
|
55
|
+
|
|
56
|
+
```env
|
|
57
|
+
DEBUG=False
|
|
58
|
+
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
|
|
59
|
+
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The `$` sign indicates to Fujin that it is a secret that should be resolved using the configured adapter.
|
|
63
|
+
|
|
64
|
+
## How it Works
|
|
65
|
+
|
|
66
|
+
The adapter:
|
|
67
|
+
1. Uses the Doppler CLI configuration from your project setup
|
|
68
|
+
2. Resolves all secrets concurrently using `doppler run --command "echo $<name>"`
|
|
69
|
+
3. Returns the resolved environment variables
|
|
70
|
+
|
|
71
|
+
## Related
|
|
72
|
+
|
|
73
|
+
- [Fujin Documentation](https://github.com/Tobi-De/fujin)
|
|
74
|
+
- [Doppler CLI Documentation](https://docs.doppler.com/docs/cli)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Fujin Secrets - Doppler
|
|
2
|
+
|
|
3
|
+
Doppler secret adapter for Fujin deployment tool.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install fujin-secrets-doppler
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or with uv:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv pip install fujin-secrets-doppler
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
Download and install the [Doppler CLI](https://docs.doppler.com/docs/cli) and sign in to your account.
|
|
20
|
+
|
|
21
|
+
Move to your project root directory and run `doppler setup` to configure your project.
|
|
22
|
+
|
|
23
|
+
## Configuration
|
|
24
|
+
|
|
25
|
+
Add the following to your `fujin.toml` file:
|
|
26
|
+
|
|
27
|
+
```toml
|
|
28
|
+
[secrets]
|
|
29
|
+
adapter = "doppler"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
In your environment file (`.env` or configured via `envfile` in `fujin.toml`), prefix secret values with `$`:
|
|
35
|
+
|
|
36
|
+
```env
|
|
37
|
+
DEBUG=False
|
|
38
|
+
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
|
|
39
|
+
AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The `$` sign indicates to Fujin that it is a secret that should be resolved using the configured adapter.
|
|
43
|
+
|
|
44
|
+
## How it Works
|
|
45
|
+
|
|
46
|
+
The adapter:
|
|
47
|
+
1. Uses the Doppler CLI configuration from your project setup
|
|
48
|
+
2. Resolves all secrets concurrently using `doppler run --command "echo $<name>"`
|
|
49
|
+
3. Returns the resolved environment variables
|
|
50
|
+
|
|
51
|
+
## Related
|
|
52
|
+
|
|
53
|
+
- [Fujin Documentation](https://github.com/Tobi-De/fujin)
|
|
54
|
+
- [Doppler CLI Documentation](https://docs.doppler.com/docs/cli)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "uv_build"
|
|
3
|
+
requires = [ "uv-build>=0.9.18,<0.10" ]
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fujin-secrets-doppler"
|
|
7
|
+
version = "0.16.0"
|
|
8
|
+
description = "Doppler secret adapter for Fujin"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name = "Tobi", email = "tobidegnon@proton.me" },
|
|
12
|
+
]
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"fujin-cli>=0.16",
|
|
24
|
+
"python-dotenv>=1.0.1",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
urls.Homepage = "https://github.com/Tobi-De/fujin"
|
|
28
|
+
urls.Issues = "https://github.com/Tobi-De/fujin/issues"
|
|
29
|
+
urls.Source = "https://github.com/Tobi-De/fujin"
|
|
30
|
+
entry-points."fujin.secrets".doppler = "fujin_secrets_doppler:doppler"
|
|
31
|
+
|
|
32
|
+
[tool.uv.sources]
|
|
33
|
+
fujin-cli = { workspace = true }
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Doppler secret adapter for Fujin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
7
|
+
from contextlib import closing
|
|
8
|
+
from io import StringIO
|
|
9
|
+
|
|
10
|
+
from dotenv import dotenv_values
|
|
11
|
+
from fujin.config import SecretConfig
|
|
12
|
+
from fujin.errors import SecretResolutionError
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def doppler(env_content: str, secret_config: SecretConfig) -> str:
|
|
18
|
+
"""Doppler secret adapter.
|
|
19
|
+
|
|
20
|
+
Retrieves secrets from Doppler using the `doppler` CLI.
|
|
21
|
+
Handles concurrent resolution of multiple secrets for performance.
|
|
22
|
+
|
|
23
|
+
Requires `doppler setup` to be run in project root first.
|
|
24
|
+
|
|
25
|
+
Configuration:
|
|
26
|
+
adapter = "doppler"
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
env_content: Raw environment file content
|
|
30
|
+
secret_config: Secret configuration (unused for doppler)
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Resolved environment content with secrets replaced
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
SecretResolutionError: If secret not found or CLI fails
|
|
37
|
+
"""
|
|
38
|
+
# Parse env file
|
|
39
|
+
with closing(StringIO(env_content)) as buffer:
|
|
40
|
+
env_dict = dotenv_values(stream=buffer)
|
|
41
|
+
|
|
42
|
+
# Identify secrets (values starting with $)
|
|
43
|
+
secrets = {
|
|
44
|
+
key: value for key, value in env_dict.items() if value and value.startswith("$")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if not secrets:
|
|
48
|
+
return env_content
|
|
49
|
+
|
|
50
|
+
# Resolve secrets concurrently
|
|
51
|
+
resolved_secrets = {}
|
|
52
|
+
with ThreadPoolExecutor() as executor:
|
|
53
|
+
future_to_key = {
|
|
54
|
+
executor.submit(_read_secret, secret[1:]): key
|
|
55
|
+
for key, secret in secrets.items()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for future in as_completed(future_to_key):
|
|
59
|
+
key = future_to_key[future]
|
|
60
|
+
try:
|
|
61
|
+
resolved_secrets[key] = future.result()
|
|
62
|
+
except Exception as e:
|
|
63
|
+
raise SecretResolutionError(
|
|
64
|
+
f"Failed to retrieve secret for {key}: {e}",
|
|
65
|
+
adapter="doppler",
|
|
66
|
+
key=key,
|
|
67
|
+
) from e
|
|
68
|
+
|
|
69
|
+
# Merge resolved secrets back into env dict
|
|
70
|
+
env_dict.update(resolved_secrets)
|
|
71
|
+
|
|
72
|
+
return "\n".join(f'{key}="{value}"' for key, value in env_dict.items())
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _read_secret(name: str) -> str:
|
|
76
|
+
"""Read a single secret from Doppler.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
name: Secret/variable name
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Secret value
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
SecretResolutionError: If secret not found
|
|
86
|
+
"""
|
|
87
|
+
result = subprocess.run(
|
|
88
|
+
["doppler", "run", "--command", f"echo ${name}"],
|
|
89
|
+
capture_output=True,
|
|
90
|
+
text=True,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if result.returncode != 0:
|
|
94
|
+
raise SecretResolutionError(result.stderr, adapter="doppler", key=name)
|
|
95
|
+
|
|
96
|
+
return result.stdout.strip()
|
|
File without changes
|