This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/main by this push:
new f38dac1 Add a migration to use a curated secrets state subdirectory
f38dac1 is described below
commit f38dac136f5e48dffd44d090bb6fcc20fa94a9fb
Author: Sean B. Palmer <[email protected]>
AuthorDate: Mon Jan 19 18:55:58 2026 +0000
Add a migration to use a curated secrets state subdirectory
---
atr/config.py | 51 ++++++++++++++++++++++++++++++++++-----------------
atr/server.py | 3 +++
2 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/atr/config.py b/atr/config.py
index 4430757..3ef06c3 100644
--- a/atr/config.py
+++ b/atr/config.py
@@ -28,26 +28,43 @@ _RAT_VERSION: Final = "0.17"
def _config_secrets(key: str, state_dir: str, default: str | None = None,
cast: type = str) -> str | None:
- secrets_path = os.path.join(state_dir, "secrets.ini")
+ secrets_path = os.path.join(state_dir, "secrets", "curated", "secrets.ini")
+
+ # This code is deprecated and will be removed
+ # TODO: Remove this once migrations are no longer likely2026-
+ deprecated_path = os.path.join(state_dir, "secrets.ini")
+ if os.path.exists(deprecated_path):
+ if os.path.exists(secrets_path):
+ raise RuntimeError(f"Conflicting secrets files exist:
{deprecated_path} and {secrets_path}")
+ return _config_secrets_get(deprecated_path, key, default, cast,
allow_not_found=False)
+
+ return _config_secrets_get(secrets_path, key, default, cast)
+
+
+def _config_secrets_get(
+ secrets_path: str, key: str, default: str | None = None, cast: type = str,
allow_not_found: bool = True
+) -> str | None:
try:
repo_ini = decouple.RepositoryIni(secrets_path)
- config_obj = decouple.Config(repo_ini)
- sentinel = object()
- # Using a cast here would also cast the default sentinel value
- value = config_obj.get(key, default=sentinel)
- if value is sentinel:
- if default is None:
- # We must return None separately because otherwise it may be
cast
- return decouple.config(key, default=None)
- return decouple.config(key, default=default, cast=cast)
- if isinstance(value, str) or (value is None):
- return value
- return None
except FileNotFoundError:
- if default is None:
- # We must return None separately because otherwise it may be cast
- return decouple.config(key, default=None)
- return decouple.config(key, default=default, cast=cast)
+ if allow_not_found is False:
+ raise
+ else:
+ if key in repo_ini:
+ value = repo_ini[key]
+ return cast(value)
+
+ # There is no secrets file, or it does not contain the key
+ # Try getting the value from environment variables
+ sentinel = object()
+ # We do not use the cast keyword argument here
+ # If we did, it would also be applied to the default sentinel value
+ value = decouple.config(key, default=sentinel)
+ if value is sentinel:
+ return default
+ if not isinstance(value, str):
+ raise ValueError(f"Secret value for {key} is not a string")
+ return cast(value)
class AppConfig:
diff --git a/atr/server.py b/atr/server.py
index 4c96c0a..e960539 100644
--- a/atr/server.py
+++ b/atr/server.py
@@ -78,6 +78,8 @@ _MIGRATIONS: Final[list[tuple[str, str]]] = [
("atr-worker-error.log", "logs/atr-worker-error.log"),
("keys_import.log", "logs/keys-import.log"),
("route-performance.log", "logs/route-performance.log"),
+ # Secrets
+ ("secrets.ini", "secrets/curated/secrets.ini"),
]
_SWAGGER_UI_TEMPLATE: Final[str] = """<!DOCTYPE html>
@@ -144,6 +146,7 @@ def _app_dirs_setup(state_dir_str: str, hot_reload: bool)
-> None:
pathlib.Path(state_dir_str) / "external",
pathlib.Path(state_dir_str) / "logs",
pathlib.Path(state_dir_str) / "runtime",
+ pathlib.Path(state_dir_str) / "secrets" / "curated",
util.get_downloads_dir(),
util.get_finished_dir(),
util.get_tmp_dir(),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]