diff --git a/.github/workflows/build-and-push-image.yaml b/.github/workflows/build-and-push-image.yaml index 64a3199..8d19c00 100644 --- a/.github/workflows/build-and-push-image.yaml +++ b/.github/workflows/build-and-push-image.yaml @@ -34,6 +34,32 @@ jobs: - name: Check out commit # docs: https://github.com/actions/checkout uses: actions/checkout@v4 + # Extract the version number from the GitHub release tag and write it into + # the `pyproject.toml` file that will be included in the container image. + # + # References: + # - https://jcgoran.github.io/2021/02/07/bash-string-trimming.html#h-string-removal (for stripping leading 'v') + # - https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter + # + - name: Derive version identifier from Git tag or commit hash + id: extract_version + run: | + if [ "${{ github.event_name }}" = "release" ]; then + VERSION_RAW="${{ github.event.release.tag_name }}" # e.g. "v1.2.3" + VERSION_SANITIZED="${VERSION_RAW#v}" # strips leading 'v' if present + else + COMMIT_HASH="${{ github.sha }}" + COMMIT_HASH_SHORT=$(echo "${COMMIT_HASH}" | cut -c1-8) # e.g. "abcd1234" + VERSION_SANITIZED="0.dev-${COMMIT_HASH_SHORT}" # e.g. "0.dev-abcd1234" + fi + echo "Version: ${VERSION_SANITIZED}" + echo "version=${VERSION_SANITIZED}" >> "${GITHUB_OUTPUT}" # makes it available to subsequent steps + - name: Update version in `pyproject.toml` + run: | + echo "Version: ${{ steps.extract_version.outputs.version }}" + sed -i 's/^version = "0.0.0"/version = "${{ steps.extract_version.outputs.version }}"/' ./pyproject.toml + cat ./pyproject.toml | grep '^version = ' # sanity check + # Note: These steps are about building and publishing the container image. - name: Authenticate with container registry uses: docker/login-action@v3 diff --git a/pyproject.toml b/pyproject.toml index 231401c..f48027a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,14 @@ build-backend = "setuptools.build_meta" [project] name = "bertron" -version = "0.1.0" +# Project version identifier. +# +# Note: The GitHub Actions workflow in `.github/workflows/build-and-push-image.yaml` +# will replace this version identifier when building a container image. The +# replacement will be the name of the Git tag associated with the GitHub Release +# whose publishing triggered the GitHub Actions workflow run. +# +version = "0.0.0" authors = [ {name = "Chuck Parker", email = "ctparker@lbl.gov"}, ] diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..a3be001 --- /dev/null +++ b/src/README.md @@ -0,0 +1,11 @@ +# src + +## Contents + + + +- `bertron/`: (Undocumented) +- `bertron_client.py`: A Python library client applications can use to access the BERtron API +- `lib/`: Library of helper functions, constants, etc. +- `README.md`: This document +- `server.py`: The BERtron API diff --git a/src/lib/helpers.py b/src/lib/helpers.py new file mode 100644 index 0000000..825a94c --- /dev/null +++ b/src/lib/helpers.py @@ -0,0 +1,18 @@ +from importlib.metadata import PackageNotFoundError, version +from typing import Optional + + +def get_package_version(package_name: str) -> Optional[str]: + r""" + Returns the version identifier (e.g., "1.2.3") of the package having the specified name. + + Args: + package_name: The name of the package + + Returns: + The version identifier of the package, or `None` if package not found + """ + try: + return version(package_name) + except PackageNotFoundError: + return None diff --git a/src/models.py b/src/models.py new file mode 100644 index 0000000..0ea55df --- /dev/null +++ b/src/models.py @@ -0,0 +1,32 @@ +from pydantic import BaseModel, Field +from typing import Optional + + +class HealthResponse(BaseModel): + r"""A response containing system health information.""" + + web_server: bool = Field( + ..., + title="Web server health", + description="Whether the web server is up and running", + ) + database: bool = Field( + ..., + title="Database health", + description="Whether the web server can access the database server", + ) + + +class VersionResponse(BaseModel): + r"""A response containing system version information.""" + + api: Optional[str] = Field( + ..., + title="API version", + description="The version identifier of the API", + ) + bertron_schema: Optional[str] = Field( + ..., + title="BERtron schema version", + description="The version identifier of the BERtron schema", + ) diff --git a/src/server.py b/src/server.py index 3545577..61d3b4f 100644 --- a/src/server.py +++ b/src/server.py @@ -1,11 +1,15 @@ +import logging +from typing import Optional, Dict, Any + from fastapi import FastAPI, HTTPException, Query -import uvicorn from fastapi.responses import RedirectResponse from pymongo import MongoClient -from typing import Optional, Dict, Any from pydantic import BaseModel, Field from schema.datamodel import bertron_schema_pydantic -import logging +import uvicorn + +from lib.helpers import get_package_version +from models import HealthResponse, VersionResponse # Set up logging logger = logging.getLogger(__name__) @@ -24,10 +28,24 @@ def get_root(): @app.get("/health") -def get_health(): - r"""Get API health information.""" +def get_health() -> HealthResponse: + r"""Get system health information.""" is_database_healthy = len(mongo_client.list_database_names()) > 0 - return {"web_server": "ok", "database": is_database_healthy} + return HealthResponse( + web_server=True, + database=is_database_healthy, + ) + + +@app.get("/version") +def get_version() -> VersionResponse: + r"""Get system version information.""" + api_package_name = "bertron" + bertron_schema_package_name = "bertron-schema" + return VersionResponse( + api=get_package_version(api_package_name), + bertron_schema=get_package_version(bertron_schema_package_name), + ) @app.get("/bertron") diff --git a/uv.lock b/uv.lock index 444534e..7c417b9 100644 --- a/uv.lock +++ b/uv.lock @@ -99,7 +99,7 @@ wheels = [ [[package]] name = "bertron" -version = "0.1.0" +version = "0.0.0" source = { editable = "." } dependencies = [ { name = "bertron-schema" },