diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index ad635d3..ca0aa05 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -204,6 +204,7 @@ jobs: with: fetch-depth: 0 ref: main + token: ${{ secrets.ADMIN_PAT }} - name: Ensure latest main (with bump) run: git pull origin main @@ -212,29 +213,34 @@ jobs: uses: actions/download-artifact@v4 with: name: real-binary - path: python/teams_lib_pzsp2_z1/bin/ + path: /tmp/real-binary - name: Prepare Client Directory run: | - chmod +x python/teams_lib_pzsp2_z1/bin/* - mkdir python_release cp -r python/* python_release/ - mkdir -p python_release/.github/workflows + # binaries + TARGET_BIN_DIR="python_release/teams_lib_pzsp2_z1/bin/" + mkdir -p $TARGET_BIN_DIR + find /tmp/real-binary -type f -exec cp {} $TARGET_BIN_DIR \; + chmod +x $TARGET_BIN_DIR/* + # publish workflow + mkdir -p python_release/.github/workflows cp .github/workflows/publish.yml python_release/.github/workflows/ + # license file if [ -f "LICENSE" ]; then cp LICENSE python_release/ fi + # clean up unneeded files rm -rf python_release/tests find python_release -type d -name "__pycache__" -exec rm -rf {} + rm -rf python_release/.venv - rm -rf python_release/uv.lock - ls -R python_release/teams_lib_pzsp2_z1/bin + ls -R python_release - name: Commit and Push to Release Branch run: | @@ -243,9 +249,9 @@ jobs: git checkout --orphan python-release git rm -rf . - cp -r python_release/* . + cp -a python_release/. . rm -rf python_release - git add . + git add . git commit -m "Release build: ${{ github.sha }}" git push -f origin python-release diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d60e2ce..caa9c12 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,12 +27,8 @@ jobs: - name: Setup Python run: uv python install 3.12 - - name: Install build tools - run: | - uv pip install --system build - - name: Build the package - run: uv run python -m build + run: uv run --with build python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index d4d4235..bc4ccf1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ python/teams_lib_pzsp2_z1.egg-info python/teams_lib_pzsp2_z1/bin/* .env example.py +.vscode \ No newline at end of file diff --git a/python/pyproject.toml b/python/pyproject.toml index 74dfadb..b4460b6 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -5,14 +5,14 @@ description = "Bridge to Go client for Teams API" readme = "README.md" requires-python = ">=3.12" dependencies = [ - "dotenv>=0.9.9", - "pytest-httpserver>=1.1.3", + "python-dotenv>=1.0.0", ] [dependency-groups] dev = [ "pytest>=9.0.0", "ruff>=0.14.4", + "pytest-httpserver>=1.1.3", ] [tool.ruff] diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py index 584d1f1..055ac45 100644 --- a/python/teams_lib_pzsp2_z1/client.py +++ b/python/teams_lib_pzsp2_z1/client.py @@ -13,6 +13,7 @@ class TeamsClient: def __init__( self, auto_init: bool = True, + env_path: str | None = None, cache_enabled: bool = False, cache_path: str | None = None, ): @@ -27,6 +28,7 @@ def __init__( bufsize=1, ) + self.env_path = env_path self.channels = ChannelsService(self) if auto_init: @@ -47,7 +49,7 @@ def init_client( self, cache_enabled: bool = False, cache_path: str | None = None ) -> Any: sender_config = config.SenderConfig() - auth_config = config.load_auth_config() + auth_config = config.load_auth_config(self.env_path) return self.execute( cmd_type="init", config={ diff --git a/python/teams_lib_pzsp2_z1/config.py b/python/teams_lib_pzsp2_z1/config.py index 77440e9..286aa89 100644 --- a/python/teams_lib_pzsp2_z1/config.py +++ b/python/teams_lib_pzsp2_z1/config.py @@ -1,10 +1,13 @@ import os -import sys from dataclasses import dataclass from dotenv import load_dotenv +class AuthConfigurationError(Exception): + pass + + @dataclass class SenderConfig: max_retries: int = 3 @@ -21,17 +24,14 @@ class AuthConfig: auth_method: str -def load_auth_config() -> AuthConfig: - load_dotenv() +def load_auth_config(env_path: str | None = None) -> AuthConfig: + load_dotenv(env_path) cfg = AuthConfig( client_id=get_env("CLIENT_ID", ""), tenant=get_env("TENANT_ID", ""), email=get_env("EMAIL", ""), - scopes=get_env( - "SCOPES", - "https://graph.microsoft.com/.default" - ).split(","), + scopes=get_env("SCOPES", "https://graph.microsoft.com/.default").split(","), auth_method=get_env("AUTH_METHOD", "DEVICE_CODE"), ) @@ -44,18 +44,20 @@ def get_env(key: str, fallback: str) -> str: def validate(cfg: AuthConfig): + missing = [] if not cfg.client_id: - print("Missing CLIENT ID", file=sys.stderr) - sys.exit(1) - + missing.append("CLIENT_ID") if not cfg.tenant: - print("Missing TENANT ID", file=sys.stderr) - sys.exit(1) - + missing.append("TENANT_ID") if not cfg.email: - print("Missing EMAIL", file=sys.stderr) - sys.exit(1) + missing.append("EMAIL") + + if missing: + raise AuthConfigurationError( + f"Missing required environment variables: {', '.join(missing)}" + ) if cfg.auth_method not in ("DEVICE_CODE", "INTERACTIVE"): - print("AUTH METHOD must be either DEVICE_CODE or INTERACTIVE", file=sys.stderr) - sys.exit(1) + raise AuthConfigurationError( + f"Invalid AUTH_METHOD: {cfg.auth_method}. Must be DEVICE_CODE or INTERACTIVE" + ) diff --git a/python/uv.lock b/python/uv.lock index bc82c5f..37ed113 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -11,17 +11,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] -[[package]] -name = "dotenv" -version = "0.9.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dotenv" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/b7/545d2c10c1fc15e48653c91efde329a790f2eecfbbf2bd16003b5db2bab0/dotenv-0.9.9-py2.py3-none-any.whl", hash = "sha256:29cf74a087b31dafdb5a446b6d7e11cbce8ed2741540e2339c69fbef92c94ce9", size = 1892, upload-time = "2025-02-19T22:15:01.647Z" }, -] - [[package]] name = "iniconfig" version = "2.3.0" @@ -189,25 +178,23 @@ name = "teams-lib-pzsp2-z1" version = "0.1.0" source = { virtual = "." } dependencies = [ - { name = "dotenv" }, - { name = "pytest-httpserver" }, + { name = "python-dotenv" }, ] [package.dev-dependencies] dev = [ { name = "pytest" }, + { name = "pytest-httpserver" }, { name = "ruff" }, ] [package.metadata] -requires-dist = [ - { name = "dotenv", specifier = ">=0.9.9" }, - { name = "pytest-httpserver", specifier = ">=1.1.3" }, -] +requires-dist = [{ name = "python-dotenv", specifier = ">=1.0.0" }] [package.metadata.requires-dev] dev = [ { name = "pytest", specifier = ">=9.0.0" }, + { name = "pytest-httpserver", specifier = ">=1.1.3" }, { name = "ruff", specifier = ">=0.14.4" }, ]