Fabio Geraci
11/24/2025, 1:50 PMMarvin
11/24/2025, 1:50 PMMarvin
11/24/2025, 1:55 PMfrom prefect import flow
from prefect.runner.storage import GitRepository
from prefect_gitlab import GitLabCredentials
repo = GitRepository(
url="<https://gitlab.com/org/repo.git>",
credentials=GitLabCredentials.load("my-gitlab-creds"),
)
flow.from_source(
source=repo,
entrypoint="flows.py:my_flow",
).deploy(name="my-deployment", work_pool_name="my-pool")
Helpful docs:
- Prefect GitLab integration: prefect-gitlab docs
- Storing/retrieving flow code (flow.from_source + GitRepository): Store flow code guide
- flow.from_source reference: flow.from_source API
- Runner storage (GitRepository) reference: Runner storage API
If you’re trying to push from inside a Prefect flow/worker, you still don’t need prefect-gitlab—just ensure the runtime environment has Git credentials configured (SSH key or HTTPS with PAT/deploy token). Want guidance for that setup or are you asking about pulling code for deployments?Fabio Geraci
11/24/2025, 2:00 PMimport os
import tempfile
from pathlib import Path
from typing import Optional, Tuple
from loguru import logger
from .ssh_key_utils import normalize_private_key_text
class PrefectSecretManager:
"""
Handles loading SSH credentials from Prefect secrets and setting up
environment variables for Git authentication.
"""
def __init__(self):
self.ssh_key_path: Optional[str] = None
self.ssh_key_secret_used: Optional[str] = None
self._temp_key_path: Optional[str] = None
def load_ssh_credentials(
self,
key_secret_names: Tuple[str, ...] = None,
passphrase_secret_names: Tuple[str, ...] = None,
) -> bool:
"""
Load SSH credentials from Prefect secrets and configure environment.
Args:
key_secret_names: Tuple of Prefect secret names to try for SSH key
passphrase_secret_names: Tuple of Prefect secret names to try for passphrase
Returns:
True if SSH key was loaded successfully, False otherwise
"""
try:
from prefect.blocks.system import Secret
def _load_secret(secret_name: str) -> str:
"""Helper to load a single Prefect secret."""
try:
value = Secret.load(secret_name).get()
value = (value or "").strip()
if value:
logger.debug(f"Loaded Prefect secret '{secret_name}'")
return value
logger.debug(f"Prefect secret '{secret_name}' is empty or whitespace")
except Exception as secret_error:
logger.debug(
f"Prefect secret '{secret_name}' unavailable: {secret_error}"
)
return ""
# Load SSH private key
ssh_key_content = ""
for secret_name in key_secret_names:
ssh_key_content = _load_secret(secret_name)
if ssh_key_content:
self.ssh_key_secret_used = secret_name
break
if not ssh_key_content:
logger.debug(
f"No Prefect SSH key secret found in {key_secret_names}; "
"relying on environment."
)
return False
# Create temporary file for SSH key
fd, ssh_key_path = tempfile.mkstemp(
prefix="prefect_ssh_", suffix=".key"
)
try:
ssh_key_content_normalized = normalize_private_key_text(ssh_key_content)
with os.fdopen(fd, "w") as f:
f.write(ssh_key_content_normalized)
os.chmod(ssh_key_path, 0o600)
# Set environment variables
os.environ["GIT_SSH_KEY_PATH"] = ssh_key_path
os.environ["_TEMP_PREFECT_SSH_KEY_PATH"] = ssh_key_path
self.ssh_key_path = ssh_key_path
self._temp_key_path = ssh_key_path
logger.info(
f"SSH key loaded from Prefect secret '{self.ssh_key_secret_used}' "
f"into {ssh_key_path}"
)
logger.debug(f"SSH key length: {len(ssh_key_content)} characters")
# Validate key format
if ssh_key_content_normalized.startswith("-----BEGIN"):
logger.debug("SSH key has proper header format")
else:
logger.warning(
f"SSH key may be malformed - doesn't start with '-----BEGIN', "
f"starts with: {ssh_key_content_normalized[:50]}"
)
# Count actual newlines in the normalized key
newline_count = ssh_key_content_normalized.count("\n")
logger.debug(f"SSH key has {newline_count} actual newlines")
except Exception as e:
logger.warning(f"Failed to write SSH key to file: {e}")
if os.path.exists(ssh_key_path):
os.remove(ssh_key_path)
raise
finally:
# Clear sensitive data from memory
ssh_key_content = ""
# Load passphrase if available
ssh_passphrase_value = ""
ssh_passphrase_secret_used = ""
for secret_name in passphrase_secret_names:
ssh_passphrase_value = _load_secret(secret_name)
if ssh_passphrase_value:
ssh_passphrase_secret_used = secret_name
break
if ssh_passphrase_value:
os.environ["SSH_KEY_PASSPHRASE"] = ssh_passphrase_value
logger.info(
f"SSH key passphrase loaded from Prefect secret "
f"'{ssh_passphrase_secret_used}'"
)
else:
logger.debug("No SSH passphrase secret available (optional).")
return True
except Exception as e:
logger.debug(f"Failed to load Prefect SSH credentials: {e}")
return False
def cleanup(self) -> None:
"""Clean up temporary SSH key file if it was created."""
if self._temp_key_path and os.path.exists(self._temp_key_path):
try:
os.remove(self._temp_key_path)
logger.debug(f"Cleaned up temporary SSH key: {self._temp_key_path}")
except Exception as e:
logger.warning(f"Failed to clean up temporary SSH key: {e}")
finally:
self._temp_key_path = None
def __del__(self):
"""Ensure cleanup happens when object is destroyed."""
self.cleanup()
def __enter__(self):
"""Context manager entry."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit with cleanup."""
self.cleanup()
return False