secretsloader 0.1.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,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: secretsloader
3
+ Version: 0.1.0
4
+ Summary: Load secrets from Infisical into environment variables
5
+ Author-email: Welington Souza <welington.souza@mprj.mp.br>
6
+ License: MIT
7
+ Project-URL: Homepage, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git
8
+ Project-URL: Repository, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git
9
+ Project-URL: Issues, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git/issues
10
+ Keywords: infisical,secrets,environment,configuration
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Requires-Python: >=3.12.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: python-dotenv
23
+ Requires-Dist: infisical-python>=2.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: build; extra == "dev"
28
+ Requires-Dist: twine; extra == "dev"
29
+
30
+ # secretsloader
31
+
32
+ Pacote para carregamento centralizado de secrets do Infisical em variáveis de ambiente.
33
+
34
+ ## Instalação
35
+
36
+ ### Usando pip
37
+
38
+ ```bash
39
+ pip install secretsloader
40
+ ```
41
+
42
+ ### Usando uv
43
+
44
+ ```bash
45
+ uv add secretsloader
46
+ ```
47
+
48
+ ### Usando poetry
49
+
50
+ ```bash
51
+ poetry add secretsloader
52
+ ```
53
+
54
+ ## Uso Básico
55
+
56
+ ```python
57
+ from secretsloader import load_secrets
58
+
59
+ # Carregar secrets do Infisical para as variáveis de ambiente
60
+ load_secrets()
61
+
62
+ # Agora você pode acessar os secrets via os.environ
63
+ import os
64
+
65
+ db_password = os.getenv("DB_PASSWORD")
66
+ ```
67
+
68
+ ## Configuração
69
+
70
+ Configure as seguintes variáveis de ambiente (pode usar um arquivo `.env`):
71
+
72
+ ```env
73
+ INFISICAL_SITE_URL=https://app.infisical.com
74
+ INFISICAL_TOKEN=seu_token_aqui
75
+ INFISICAL_PROJECT_ID=seu_project_id
76
+ INFISICAL_ENVIRONMENT_SLUG=dev
77
+ ```
78
+
79
+ ## Funcionalidades
80
+
81
+ - ✅ Carregamento automático de secrets do Infisical
82
+ - ✅ Retry automático em caso de falha
83
+ - ✅ Suporte a cache TTL
84
+ - ✅ Validação de variáveis obrigatórias
85
+ - ✅ Logging detalhado
86
+
87
+ ## Documentação Completa
88
+
89
+ Para documentação detalhada, consulte [SECRETSLOADER_DOCUMENTATION.md](SECRETSLOADER_DOCUMENTATION.md)
@@ -0,0 +1,60 @@
1
+ # secretsloader
2
+
3
+ Pacote para carregamento centralizado de secrets do Infisical em variáveis de ambiente.
4
+
5
+ ## Instalação
6
+
7
+ ### Usando pip
8
+
9
+ ```bash
10
+ pip install secretsloader
11
+ ```
12
+
13
+ ### Usando uv
14
+
15
+ ```bash
16
+ uv add secretsloader
17
+ ```
18
+
19
+ ### Usando poetry
20
+
21
+ ```bash
22
+ poetry add secretsloader
23
+ ```
24
+
25
+ ## Uso Básico
26
+
27
+ ```python
28
+ from secretsloader import load_secrets
29
+
30
+ # Carregar secrets do Infisical para as variáveis de ambiente
31
+ load_secrets()
32
+
33
+ # Agora você pode acessar os secrets via os.environ
34
+ import os
35
+
36
+ db_password = os.getenv("DB_PASSWORD")
37
+ ```
38
+
39
+ ## Configuração
40
+
41
+ Configure as seguintes variáveis de ambiente (pode usar um arquivo `.env`):
42
+
43
+ ```env
44
+ INFISICAL_SITE_URL=https://app.infisical.com
45
+ INFISICAL_TOKEN=seu_token_aqui
46
+ INFISICAL_PROJECT_ID=seu_project_id
47
+ INFISICAL_ENVIRONMENT_SLUG=dev
48
+ ```
49
+
50
+ ## Funcionalidades
51
+
52
+ - ✅ Carregamento automático de secrets do Infisical
53
+ - ✅ Retry automático em caso de falha
54
+ - ✅ Suporte a cache TTL
55
+ - ✅ Validação de variáveis obrigatórias
56
+ - ✅ Logging detalhado
57
+
58
+ ## Documentação Completa
59
+
60
+ Para documentação detalhada, consulte [SECRETSLOADER_DOCUMENTATION.md](SECRETSLOADER_DOCUMENTATION.md)
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "secretsloader"
7
+ version = "0.1.0"
8
+ description = "Load secrets from Infisical into environment variables"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Welington Souza", email = "welington.souza@mprj.mp.br"}
14
+ ]
15
+ keywords = ["infisical", "secrets", "environment", "configuration"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ ]
27
+ dependencies = [
28
+ "python-dotenv",
29
+ "infisical-python>=2.0.0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = ["pytest", "ruff", "build", "twine"]
34
+
35
+ [project.urls]
36
+ Homepage = "https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git"
37
+ Repository = "https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git"
38
+ Issues = "https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git/issues"
39
+
40
+ [tool.setuptools]
41
+ py-modules = ["secretsloader"]
42
+
43
+ [dependency-groups]
44
+ dev = [
45
+ "build>=1.4.0",
46
+ "twine>=6.2.0",
47
+ ]
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: secretsloader
3
+ Version: 0.1.0
4
+ Summary: Load secrets from Infisical into environment variables
5
+ Author-email: Welington Souza <welington.souza@mprj.mp.br>
6
+ License: MIT
7
+ Project-URL: Homepage, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git
8
+ Project-URL: Repository, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git
9
+ Project-URL: Issues, https://gitlab-dti.mprj.mp.br/gadg/desenvolvimento/etl/secretsloader.git/issues
10
+ Keywords: infisical,secrets,environment,configuration
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Requires-Python: >=3.12.10
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: python-dotenv
23
+ Requires-Dist: infisical-python>=2.0.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == "dev"
26
+ Requires-Dist: ruff; extra == "dev"
27
+ Requires-Dist: build; extra == "dev"
28
+ Requires-Dist: twine; extra == "dev"
29
+
30
+ # secretsloader
31
+
32
+ Pacote para carregamento centralizado de secrets do Infisical em variáveis de ambiente.
33
+
34
+ ## Instalação
35
+
36
+ ### Usando pip
37
+
38
+ ```bash
39
+ pip install secretsloader
40
+ ```
41
+
42
+ ### Usando uv
43
+
44
+ ```bash
45
+ uv add secretsloader
46
+ ```
47
+
48
+ ### Usando poetry
49
+
50
+ ```bash
51
+ poetry add secretsloader
52
+ ```
53
+
54
+ ## Uso Básico
55
+
56
+ ```python
57
+ from secretsloader import load_secrets
58
+
59
+ # Carregar secrets do Infisical para as variáveis de ambiente
60
+ load_secrets()
61
+
62
+ # Agora você pode acessar os secrets via os.environ
63
+ import os
64
+
65
+ db_password = os.getenv("DB_PASSWORD")
66
+ ```
67
+
68
+ ## Configuração
69
+
70
+ Configure as seguintes variáveis de ambiente (pode usar um arquivo `.env`):
71
+
72
+ ```env
73
+ INFISICAL_SITE_URL=https://app.infisical.com
74
+ INFISICAL_TOKEN=seu_token_aqui
75
+ INFISICAL_PROJECT_ID=seu_project_id
76
+ INFISICAL_ENVIRONMENT_SLUG=dev
77
+ ```
78
+
79
+ ## Funcionalidades
80
+
81
+ - ✅ Carregamento automático de secrets do Infisical
82
+ - ✅ Retry automático em caso de falha
83
+ - ✅ Suporte a cache TTL
84
+ - ✅ Validação de variáveis obrigatórias
85
+ - ✅ Logging detalhado
86
+
87
+ ## Documentação Completa
88
+
89
+ Para documentação detalhada, consulte [SECRETSLOADER_DOCUMENTATION.md](SECRETSLOADER_DOCUMENTATION.md)
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ secretsloader.py
4
+ secretsloader.egg-info/PKG-INFO
5
+ secretsloader.egg-info/SOURCES.txt
6
+ secretsloader.egg-info/dependency_links.txt
7
+ secretsloader.egg-info/requires.txt
8
+ secretsloader.egg-info/top_level.txt
@@ -0,0 +1,8 @@
1
+ python-dotenv
2
+ infisical-python>=2.0.0
3
+
4
+ [dev]
5
+ pytest
6
+ ruff
7
+ build
8
+ twine
@@ -0,0 +1 @@
1
+ secretsloader
@@ -0,0 +1,243 @@
1
+ """
2
+ Module for loading secrets from Infisical into environment variables.
3
+
4
+ This module provides functionality to fetch secrets from Infisical and inject them
5
+ into the process environment variables, with proper error handling and retry logic.
6
+ """
7
+
8
+ import os
9
+ import logging
10
+ import time
11
+ from typing import Optional, Dict, Any
12
+ from dotenv import load_dotenv
13
+ from infisical_sdk import InfisicalSDKClient
14
+
15
+ load_dotenv()
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Flag to ensure secrets are loaded only once
20
+ _secrets_loaded = False
21
+
22
+ # Required environment variables
23
+ REQUIRED_ENV_VARS = [
24
+ "INFISICAL_SITE_URL",
25
+ "INFISICAL_TOKEN",
26
+ "INFISICAL_PROJECT_ID",
27
+ "INFISICAL_ENVIRONMENT_SLUG",
28
+ ]
29
+
30
+
31
+ class SecretsLoaderError(Exception):
32
+ """Base exception for secrets loader errors."""
33
+ pass
34
+
35
+
36
+ class MissingEnvironmentVariableError(SecretsLoaderError):
37
+ """Raised when required environment variables are missing."""
38
+ pass
39
+
40
+
41
+ class InfisicalConnectionError(SecretsLoaderError):
42
+ """Raised when connection to Infisical fails."""
43
+ pass
44
+
45
+
46
+ def validate_environment_variables() -> None:
47
+ """
48
+ Validate that all required environment variables are set.
49
+
50
+ Raises:
51
+ MissingEnvironmentVariableError: If any required environment variable is missing.
52
+ """
53
+ missing_vars = [var for var in REQUIRED_ENV_VARS if not os.getenv(var)]
54
+
55
+ if missing_vars:
56
+ error_msg = f"Missing required environment variables: {', '.join(missing_vars)}"
57
+ logger.error(error_msg)
58
+ raise MissingEnvironmentVariableError(error_msg)
59
+
60
+ logger.debug("All required environment variables are present")
61
+
62
+
63
+ def fetch_secrets_from_infisical(
64
+ max_retries: int = 3,
65
+ retry_delay: float = 1.0,
66
+ cache_ttl: Optional[int] = None
67
+ ) -> Dict[str, str]:
68
+ """
69
+ Fetch secrets from Infisical with retry logic.
70
+
71
+ Args:
72
+ max_retries: Maximum number of retry attempts (default: 3)
73
+ retry_delay: Delay in seconds between retries (default: 1.0)
74
+ cache_ttl: Cache TTL in seconds, None to disable caching (default: None)
75
+
76
+ Returns:
77
+ Dictionary mapping secret keys to secret values
78
+
79
+ Raises:
80
+ InfisicalConnectionError: If connection to Infisical fails after all retries
81
+ """
82
+ last_exception = None
83
+
84
+ for attempt in range(1, max_retries + 1):
85
+ try:
86
+ logger.info(f"Attempting to connect to Infisical (attempt {attempt}/{max_retries})")
87
+
88
+ client = InfisicalSDKClient(
89
+ host=os.getenv("INFISICAL_SITE_URL"),
90
+ token=os.getenv("INFISICAL_TOKEN"),
91
+ cache_ttl=cache_ttl,
92
+ )
93
+
94
+ secrets_response = client.secrets.list_secrets(
95
+ project_id=os.getenv("INFISICAL_PROJECT_ID"),
96
+ environment_slug=os.getenv("INFISICAL_ENVIRONMENT_SLUG"),
97
+ secret_path="/",
98
+ ).to_dict()
99
+
100
+ secrets_dict = {}
101
+ for secret in secrets_response.get("secrets", []):
102
+ key = secret.get("secretKey")
103
+ value = secret.get("secretValue")
104
+ if key and value is not None:
105
+ secrets_dict[key] = value
106
+
107
+ logger.info(f"Successfully fetched {len(secrets_dict)} secrets from Infisical")
108
+ return secrets_dict
109
+
110
+ except Exception as e:
111
+ last_exception = e
112
+ logger.warning(
113
+ f"Failed to fetch secrets from Infisical (attempt {attempt}/{max_retries}): {e}"
114
+ )
115
+
116
+ if attempt < max_retries:
117
+ logger.info(f"Retrying in {retry_delay} seconds...")
118
+ time.sleep(retry_delay)
119
+
120
+ error_msg = f"Failed to connect to Infisical after {max_retries} attempts"
121
+ logger.error(f"{error_msg}: {last_exception}")
122
+ raise InfisicalConnectionError(error_msg) from last_exception
123
+
124
+
125
+ def load_secrets(
126
+ override_existing: bool = False,
127
+ max_retries: int = 3,
128
+ retry_delay: float = 1.0,
129
+ cache_ttl: Optional[int] = None,
130
+ force_reload: bool = False
131
+ ) -> int:
132
+ """
133
+ Load secrets from Infisical into environment variables.
134
+
135
+ This function fetches secrets from Infisical and injects them into os.environ.
136
+ By default, it will only load secrets once per process lifetime.
137
+
138
+ Args:
139
+ override_existing: If True, override existing environment variables (default: False)
140
+ max_retries: Maximum number of retry attempts for API calls (default: 3)
141
+ retry_delay: Delay in seconds between retries (default: 1.0)
142
+ cache_ttl: Cache TTL in seconds, None to disable caching (default: None)
143
+ force_reload: If True, bypass the single-load protection (default: False)
144
+
145
+ Returns:
146
+ Number of secrets loaded into environment
147
+
148
+ Raises:
149
+ MissingEnvironmentVariableError: If required environment variables are missing
150
+ InfisicalConnectionError: If connection to Infisical fails
151
+ """
152
+ global _secrets_loaded
153
+
154
+ if _secrets_loaded and not force_reload:
155
+ logger.info("Secrets already loaded, skipping reload")
156
+ return 0
157
+
158
+ logger.info("Starting secrets loading process")
159
+
160
+ # Validate environment variables
161
+ validate_environment_variables()
162
+
163
+ # Fetch secrets from Infisical
164
+ secrets_dict = fetch_secrets_from_infisical(
165
+ max_retries=max_retries,
166
+ retry_delay=retry_delay,
167
+ cache_ttl=cache_ttl
168
+ )
169
+
170
+ # Load secrets into environment
171
+ loaded_count = 0
172
+ skipped_count = 0
173
+
174
+ for key, value in secrets_dict.items():
175
+ if key in os.environ and not override_existing:
176
+ logger.debug(f"Skipping secret '{key}' (already exists in environment)")
177
+ skipped_count += 1
178
+ else:
179
+ os.environ[key] = value
180
+ loaded_count += 1
181
+ logger.debug(f"Loaded secret '{key}' into environment")
182
+
183
+ _secrets_loaded = True
184
+
185
+ logger.info(
186
+ f"Secrets loading complete: {loaded_count} loaded, {skipped_count} skipped"
187
+ )
188
+
189
+ return loaded_count
190
+
191
+
192
+ def get_credentials(
193
+ override_existing: bool = False,
194
+ max_retries: int = 3,
195
+ retry_delay: float = 1.0,
196
+ cache_ttl: Optional[int] = None
197
+ ) -> None:
198
+ """
199
+ Legacy function for backward compatibility.
200
+
201
+ Loads secrets from Infisical into environment variables with fallback to local variables.
202
+ This function catches all exceptions and logs warnings instead of raising them.
203
+
204
+ Args:
205
+ override_existing: If True, override existing environment variables (default: False)
206
+ max_retries: Maximum number of retry attempts for API calls (default: 3)
207
+ retry_delay: Delay in seconds between retries (default: 1.0)
208
+ cache_ttl: Cache TTL in seconds, None to disable caching (default: None)
209
+ """
210
+ try:
211
+ load_secrets(
212
+ override_existing=override_existing,
213
+ max_retries=max_retries,
214
+ retry_delay=retry_delay,
215
+ cache_ttl=cache_ttl
216
+ )
217
+ except Exception as e:
218
+ logger.warning(f"Could not fetch secrets from Infisical: {e}")
219
+ logger.warning("Using local environment variables only")
220
+
221
+
222
+ if __name__ == "__main__":
223
+ # Configure logging for CLI usage
224
+ logging.basicConfig(
225
+ level=logging.INFO,
226
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
227
+ )
228
+
229
+ try:
230
+ load_secrets()
231
+ # Example: print a specific environment variable
232
+ pg_host = os.environ.get("PG_HOST")
233
+ ora_user = os.environ.get("ORA_USER")
234
+
235
+ if pg_host:
236
+ print(f"PG_HOST: {pg_host}")
237
+ print(f"ORA_USER: {ora_user}")
238
+
239
+ else:
240
+ print("PG_HOST not found in environment")
241
+ except Exception as e:
242
+ logger.error(f"Failed to load secrets: {e}")
243
+ exit(1)
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+