diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index 76e5c04de..a5a5071fa 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -47,15 +47,17 @@ jobs: - name: Run Unit tests run: | uv run --project api bash dev/pytest/pytest_unit_tests.sh + + - name: Coverage Summary + run: | + set -x # Extract coverage percentage and create a summary TOTAL_COVERAGE=$(python -c 'import json; print(json.load(open("coverage.json"))["totals"]["percent_covered_display"])') # Create a detailed coverage summary echo "### Test Coverage Summary :test_tube:" >> $GITHUB_STEP_SUMMARY echo "Total Coverage: ${TOTAL_COVERAGE}%" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - uv run --project api coverage report >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + uv run --project api coverage report --format=markdown >> $GITHUB_STEP_SUMMARY - name: Run dify config tests run: uv run --project api dev/pytest/pytest_config_tests.py diff --git a/api/configs/app_config.py b/api/configs/app_config.py index 3a3ad35ee..20f8c4042 100644 --- a/api/configs/app_config.py +++ b/api/configs/app_config.py @@ -1,8 +1,11 @@ import logging +from pathlib import Path from typing import Any from pydantic.fields import FieldInfo -from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict +from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, TomlConfigSettingsSource + +from libs.file_utils import search_file_upwards from .deploy import DeploymentConfig from .enterprise import EnterpriseFeatureConfig @@ -99,4 +102,12 @@ class DifyConfig( RemoteSettingsSourceFactory(settings_cls), dotenv_settings, file_secret_settings, + TomlConfigSettingsSource( + settings_cls=settings_cls, + toml_file=search_file_upwards( + base_dir_path=Path(__file__).parent, + target_file_name="pyproject.toml", + max_search_parent_depth=2, + ), + ), ) diff --git a/api/configs/packaging/__init__.py b/api/configs/packaging/__init__.py index dddf71c09..298574d9f 100644 --- a/api/configs/packaging/__init__.py +++ b/api/configs/packaging/__init__.py @@ -1,8 +1,9 @@ from pydantic import Field -from pydantic_settings import BaseSettings + +from configs.packaging.pyproject import PyProjectConfig, PyProjectTomlConfig -class PackagingInfo(BaseSettings): +class PackagingInfo(PyProjectTomlConfig): """ Packaging build information """ diff --git a/api/configs/packaging/pyproject.py b/api/configs/packaging/pyproject.py new file mode 100644 index 000000000..90b1ecba0 --- /dev/null +++ b/api/configs/packaging/pyproject.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel, Field +from pydantic_settings import BaseSettings + + +class PyProjectConfig(BaseModel): + version: str = Field(description="Dify version", default="") + + +class PyProjectTomlConfig(BaseSettings): + """ + configs in api/pyproject.toml + """ + + project: PyProjectConfig = Field( + description="configs in the project section of pyproject.toml", + default=PyProjectConfig(), + ) diff --git a/api/controllers/console/version.py b/api/controllers/console/version.py index 7dea8e554..447cc358f 100644 --- a/api/controllers/console/version.py +++ b/api/controllers/console/version.py @@ -18,7 +18,7 @@ class VersionApi(Resource): check_update_url = dify_config.CHECK_UPDATE_URL result = { - "version": dify_config.CURRENT_VERSION, + "version": dify_config.project.version, "release_date": "", "release_notes": "", "can_auto_update": False, diff --git a/api/controllers/service_api/index.py b/api/controllers/service_api/index.py index d24c4597e..9bb5df4c4 100644 --- a/api/controllers/service_api/index.py +++ b/api/controllers/service_api/index.py @@ -9,7 +9,7 @@ class IndexApi(Resource): return { "welcome": "Dify OpenAPI", "api_version": "v1", - "server_version": dify_config.CURRENT_VERSION, + "server_version": dify_config.project.version, } diff --git a/api/extensions/ext_app_metrics.py b/api/extensions/ext_app_metrics.py index b7d412d68..56a69a186 100644 --- a/api/extensions/ext_app_metrics.py +++ b/api/extensions/ext_app_metrics.py @@ -12,14 +12,14 @@ def init_app(app: DifyApp): @app.after_request def after_request(response): """Add Version headers to the response.""" - response.headers.add("X-Version", dify_config.CURRENT_VERSION) + response.headers.add("X-Version", dify_config.project.version) response.headers.add("X-Env", dify_config.DEPLOY_ENV) return response @app.route("/health") def health(): return Response( - json.dumps({"pid": os.getpid(), "status": "ok", "version": dify_config.CURRENT_VERSION}), + json.dumps({"pid": os.getpid(), "status": "ok", "version": dify_config.project.version}), status=200, content_type="application/json", ) diff --git a/api/extensions/ext_otel.py b/api/extensions/ext_otel.py index 6dcfa7bec..23cf4c5ca 100644 --- a/api/extensions/ext_otel.py +++ b/api/extensions/ext_otel.py @@ -49,7 +49,7 @@ def init_app(app: DifyApp): logging.getLogger().addHandler(exception_handler) def init_flask_instrumentor(app: DifyApp): - meter = get_meter("http_metrics", version=dify_config.CURRENT_VERSION) + meter = get_meter("http_metrics", version=dify_config.project.version) _http_response_counter = meter.create_counter( "http.server.response.count", description="Total number of HTTP responses by status code, method and target", @@ -163,7 +163,7 @@ def init_app(app: DifyApp): resource = Resource( attributes={ ResourceAttributes.SERVICE_NAME: dify_config.APPLICATION_NAME, - ResourceAttributes.SERVICE_VERSION: f"dify-{dify_config.CURRENT_VERSION}-{dify_config.COMMIT_SHA}", + ResourceAttributes.SERVICE_VERSION: f"dify-{dify_config.project.version}-{dify_config.COMMIT_SHA}", ResourceAttributes.PROCESS_PID: os.getpid(), ResourceAttributes.DEPLOYMENT_ENVIRONMENT: f"{dify_config.DEPLOY_ENV}-{dify_config.EDITION}", ResourceAttributes.HOST_NAME: socket.gethostname(), diff --git a/api/extensions/ext_sentry.py b/api/extensions/ext_sentry.py index 3a74aace6..82aed0d98 100644 --- a/api/extensions/ext_sentry.py +++ b/api/extensions/ext_sentry.py @@ -35,6 +35,6 @@ def init_app(app: DifyApp): traces_sample_rate=dify_config.SENTRY_TRACES_SAMPLE_RATE, profiles_sample_rate=dify_config.SENTRY_PROFILES_SAMPLE_RATE, environment=dify_config.DEPLOY_ENV, - release=f"dify-{dify_config.CURRENT_VERSION}-{dify_config.COMMIT_SHA}", + release=f"dify-{dify_config.project.version}-{dify_config.COMMIT_SHA}", before_send=before_send, ) diff --git a/api/libs/file_utils.py b/api/libs/file_utils.py new file mode 100644 index 000000000..982b2cc1a --- /dev/null +++ b/api/libs/file_utils.py @@ -0,0 +1,30 @@ +from pathlib import Path + + +def search_file_upwards( + base_dir_path: Path, + target_file_name: str, + max_search_parent_depth: int, +) -> Path: + """ + Find a target file in the current directory or its parent directories up to a specified depth. + :param base_dir_path: Starting directory path to search from. + :param target_file_name: Name of the file to search for. + :param max_search_parent_depth: Maximum number of parent directories to search upwards. + :return: Path of the file if found, otherwise None. + """ + current_path = base_dir_path.resolve() + for _ in range(max_search_parent_depth): + candidate_path = current_path / target_file_name + if candidate_path.is_file(): + return candidate_path + parent_path = current_path.parent + if parent_path == current_path: # reached the root directory + break + else: + current_path = parent_path + + raise ValueError( + f"File '{target_file_name}' not found in the directory '{base_dir_path.resolve()}' or its parent directories" + f" in depth of {max_search_parent_depth}." + ) diff --git a/api/pyproject.toml b/api/pyproject.toml index 425100805..c498336b7 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dify-api" -dynamic = ["version"] +version = "1.4.3" requires-python = ">=3.11,<3.13" dependencies = [ diff --git a/api/services/account_service.py b/api/services/account_service.py index 14d238467..3fdbda48a 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -889,7 +889,7 @@ class RegisterService: TenantService.create_owner_tenant_if_not_exist(account=account, is_setup=True) - dify_setup = DifySetup(version=dify_config.CURRENT_VERSION) + dify_setup = DifySetup(version=dify_config.project.version) db.session.add(dify_setup) db.session.commit() except Exception as e: diff --git a/api/uv.lock b/api/uv.lock index 6b9deffa0..140cc4e3d 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1198,6 +1198,7 @@ wheels = [ [[package]] name = "dify-api" +version = "1.4.3" source = { virtual = "." } dependencies = [ { name = "authlib" },