diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 000000000..2fba6041d --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,20 @@ +on: + push: + branches: + - main + +permissions: + contents: write + issues: write + pull-requests: write + +name: release-please + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }} + release-type: simple diff --git a/diracx-cli/src/diracx/cli/internal/config.py b/diracx-cli/src/diracx/cli/internal/config.py index 9e0eaf2a2..6d6348a32 100644 --- a/diracx-cli/src/diracx/cli/internal/config.py +++ b/diracx-cli/src/diracx/cli/internal/config.py @@ -26,8 +26,10 @@ def get_repo_path(config_repo_str: str) -> Path: config_repo = TypeAdapter(ConfigSourceUrl).validate_python(config_repo_str) - if config_repo.scheme != "git+file" or config_repo.path is None: - raise NotImplementedError("Only git+file:// URLs are supported") + if config_repo.scheme not in ("git+file", "file") or config_repo.path is None: + raise NotImplementedError( + "Only git+file:// and file:// URLs are supported for local config repositories" + ) repo_path = Path(config_repo.path) @@ -35,7 +37,9 @@ def get_repo_path(config_repo_str: str) -> Path: def get_config_from_repo_path(repo_path: Path) -> Config: - return ConfigSource.create_from_url(backend_url=repo_path).read_config() + return ConfigSource.create_from_url( + backend_url=f"git+file://{repo_path}" + ).read_config() @app.command() diff --git a/diracx-core/src/diracx/core/config/__init__.py b/diracx-core/src/diracx/core/config/__init__.py index 37ff457ca..b07dc57d7 100644 --- a/diracx-core/src/diracx/core/config/__init__.py +++ b/diracx-core/src/diracx/core/config/__init__.py @@ -6,7 +6,6 @@ from .sources import ( ConfigSource, ConfigSourceUrl, - LocalGitConfigSource, RemoteGitConfigSource, is_running_in_async_context, ) @@ -15,7 +14,6 @@ "Config", "ConfigSource", "ConfigSourceUrl", - "LocalGitConfigSource", "RemoteGitConfigSource", "is_running_in_async_context", ) diff --git a/diracx-core/src/diracx/core/config/sources.py b/diracx-core/src/diracx/core/config/sources.py index 1cf9cc622..7ae8956c8 100644 --- a/diracx-core/src/diracx/core/config/sources.py +++ b/diracx-core/src/diracx/core/config/sources.py @@ -12,13 +12,12 @@ from datetime import datetime, timezone from pathlib import Path from tempfile import TemporaryDirectory -from typing import Annotated from urllib.parse import urlparse, urlunparse import sh import yaml from cachetools import Cache, LRUCache -from pydantic import AnyUrl, BeforeValidator, TypeAdapter, UrlConstraints +from pydantic import AnyUrl, TypeAdapter, UrlConstraints from ..exceptions import BadConfigurationVersionError from ..extensions import select_from_extension @@ -43,18 +42,11 @@ def is_running_in_async_context(): return False -def _apply_default_scheme(value: str) -> str: - """Applies the default git+file:// scheme if not present.""" - if isinstance(value, str) and "://" not in value: - value = f"git+file://{value}" - return value - - class AnyUrlWithoutHost(AnyUrl): _constraints = UrlConstraints(host_required=False) -ConfigSourceUrl = Annotated[AnyUrlWithoutHost, BeforeValidator(_apply_default_scheme)] +ConfigSourceUrl = AnyUrlWithoutHost class ConfigSource(metaclass=ABCMeta): @@ -233,40 +225,13 @@ def get_git_branch_from_url(self, backend_url: ConfigSourceUrl) -> str: return dict(backend_url.query_params()).get("branch", DEFAULT_GIT_BRANCH) -class LocalGitConfigSource(BaseGitConfigSource): - """The configuration is stored on a local git repository - When running on multiple servers, the filesystem must be shared. - """ - - scheme = "git+file" - - def __init__(self, *, backend_url: ConfigSourceUrl) -> None: - super().__init__(backend_url=backend_url) - if not backend_url.path: - raise ValueError("Empty path for LocalGitConfigSource") - - self.repo_location = Path(backend_url.path) - # Check if it's a valid git repository - try: - sh.git( - "rev-parse", - "--git-dir", - _cwd=self.repo_location, - _tty_out=False, - _async=False, - ) - except sh.ErrorReturnCode as e: - raise ValueError( - f"{self.repo_location} is not a valid git repository" - ) from e - sh.git.checkout(self.git_branch, _cwd=self.repo_location, _async=False) - - def __hash__(self): - return hash(self.repo_location) - - class RemoteGitConfigSource(BaseGitConfigSource): - """Use a remote directory as a config source.""" + """Use a remote directory as a config source. + + This supports both remote repositories (git+https://) and local repositories + (git+file://). For local repositories, it clones them into a temporary directory + to avoid the file permission issues that LocalGitConfigSource encountered. + """ scheme = "git+https" @@ -292,3 +257,14 @@ def latest_revision(self) -> tuple[str, datetime]: logger.exception(err) return super().latest_revision() + + +class FileGitConfigSource(RemoteGitConfigSource): + """Alias for RemoteGitConfigSource that handles git+file:// URLs. + + This class uses the same implementation as RemoteGitConfigSource, + cloning the local repository into a temporary directory to avoid + file permission issues in containerized environments. + """ + + scheme = "git+file" diff --git a/docs/admin/explanations/configuration.md b/docs/admin/explanations/configuration.md index 2ed2a6d4f..1e9da2548 100644 --- a/docs/admin/explanations/configuration.md +++ b/docs/admin/explanations/configuration.md @@ -5,10 +5,10 @@ This is in contrast to "Settings" which are only available on the server side. Confidential information (such as passwords) is only handled in Settings, see the DiracX helm chart for details. The DiracX configuration is stored as a single YAML file. -We recommend that this is stored within a Git repository, and DiracX provides two git-based backends can be used by servers: +We recommend that this is stored within a Git repository, and DiracX provides git-based backends that can be used by servers: -- `git+file`: Refers to a local git repository. This must be stored on a shared volume which is made available to all DiracX servers. -- `git+https`: Refers to a remote git repository that can be stored on any standard git host. +- `git+https`: Refers to a remote git repository that can be stored on any standard git host. This is the recommended approach for production deployments. +- `git+file`: Refers to a local git repository. This clones the repository into a temporary directory to avoid file permission issues in containerized environments. For production use with local repositories, `git+https` with a local git server is recommended instead. ## Structure of the CS