From 618b8c149b433b01146cf21c6a3f57e5210caff6 Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 21 Apr 2024 17:20:58 +0100 Subject: [PATCH] Switch to automatic versioning --- .env.default | 5 +++- .github/workflows/ci.yml | 14 ++++----- .github/workflows/docker.yml | 19 ------------ .github/workflows/release.yml | 56 +++++++++++++++++++++++++++++++++++ Dockerfile | 3 ++ app/config.py | 48 ++++++++++++++++++++++++++++++ app/main.py | 17 +++++++++-- pyproject.toml | 1 + requirements.txt | 1 + tests/test_config.py | 24 +++++++++++++++ 10 files changed, 157 insertions(+), 31 deletions(-) delete mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/release.yml create mode 100644 app/config.py create mode 100644 tests/test_config.py diff --git a/.env.default b/.env.default index 9a77fab..88eb9e9 100644 --- a/.env.default +++ b/.env.default @@ -1 +1,4 @@ -WEBEX_API_KEY="" \ No newline at end of file +APP_LIFECYCLE="dev" +SENTRY_ENABLED="False" +SENTRY_DSN="" +WEBEX_API_KEY="" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a22552..0927b1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,14 @@ name: CI on: - push: - branches: [ main ] pull_request: types: [opened, synchronize, reopened] paths-ignore: - - 'README.md' - - 'LICENSE.md' - - '.gitignore' - - '.github/CODEOWNERS' - - '.github/renovate.json' - - '.github/dependabot.yml' + - "README.md" + - "LICENSE.md" + - ".gitignore" + - ".github/CODEOWNERS" + - ".github/renovate.json" + - ".github/dependabot.yml" jobs: pythonci: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index b4a6e91..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Build -on: - push: - branches: [main] - -jobs: - build: - name: GitHub Container Registry - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - - name: Login to GitHub Container Registry - run: echo ${{ secrets.GHCR_ACCESS_TOKEN }} | docker login ghcr.io -u luketainton --password-stdin - - name: Build image for GitHub Package Registry - run: docker build . --file Dockerfile --tag ghcr.io/luketainton/webexmemebot:${{ github.sha }} --tag ghcr.io/luketainton/webexmemebot:latest - - name: Push image to GitHub Package Registry - run: | - docker push ghcr.io/luketainton/webexmemebot:latest - docker push ghcr.io/luketainton/webexmemebot:${{ github.sha }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b1210cd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Build +on: + push: + branches: [main] + +jobs: + release: + name: Release + runs-on: ubuntu-latest + outputs: + new_tag: ${{ steps.tag_version.outputs.new_tag }} + steps: + - uses: actions/checkout@v4 + - name: Bump version and push tag + id: tag_version + uses: mathieudutour/github-tag-action@v6.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + default_bump: minor + - name: Create a GitHub release + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.tag_version.outputs.new_tag }} + name: ${{ steps.tag_version.outputs.new_tag }} + body: ${{ steps.tag_version.outputs.changelog }} + + publish: + name: GitHub Container Registry + runs-on: ubuntu-latest + needs: release + steps: + - uses: actions/checkout@v4 + - name: Login to GitHub Container Registry + run: echo ${{ secrets.GHCR_ACCESS_TOKEN }} | docker login ghcr.io -u luketainton --password-stdin + - name: Build image for GitHub Package Registry + run: | + docker build . --file Dockerfile \ + --build-arg "version=${{ needs.release.outputs.new_tag }}" \ + --tag ghcr.io/luketainton/webexmemebot:${{ needs.release.outputs.new_tag }} \ + --tag ghcr.io/luketainton/webexmemebot:latest + - name: Push image to GitHub Package Registry + run: | + docker push ghcr.io/luketainton/webexmemebot:latest + docker push ghcr.io/luketainton/webexmemebot:${{ needs.release.outputs.new_tag }} + + deploy: + name: Update Portainer Deployment + runs-on: ubuntu-latest + needs: publish + steps: + - uses: fjogeleit/http-request-action@v1 + with: + url: ${{ secrets.PORTAINER_WEBHOOK_URL }} + method: POST + timeout: 60000 + preventFailureOnNoResponse: "true" diff --git a/Dockerfile b/Dockerfile index 78918d3..e34ac36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,7 @@ RUN pip install --no-cache-dir -r requirements.txt ENTRYPOINT ["python3", "-B", "-m", "app.main"] +ARG version="dev" +ENV APP_VERSION=$version + COPY app /run/app diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..0c4d476 --- /dev/null +++ b/app/config.py @@ -0,0 +1,48 @@ +"""Configuration module.""" + +import os + + +class Config: + """Configuration module.""" + def __init__(self) -> None: + """Configuration module.""" + self.__environment: str = os.environ.get("APP_LIFECYCLE", "DEV").upper() + self.__version: str = os.environ["APP_VERSION"] + self.__webex_token: str = os.environ["WEBEX_API_KEY"] + self.__sentry_dsn: str = os.environ.get("SENTRY_DSN", "") + self.__sentry_enabled: bool = bool( + os.environ.get("SENTRY_ENABLED", "False").upper() == "TRUE" + and self.__sentry_dsn != "" + ) + + @property + def environment(self) -> str: + """Returns the current app lifecycle.""" + return self.__environment + + @property + def version(self) -> str: + """Returns the current app version.""" + return self.__version + + @property + def sentry_enabled(self) -> bool: + """Returns True if Sentry SDK is enabled, else False.""" + return self.__sentry_enabled + + @property + def sentry_dsn(self) -> str: + """Returns the Sentry DSN value if Sentry SDK is enabled AND + Sentry DSN is set, else blank string.""" + if not self.__sentry_enabled: + return "" + return self.__sentry_dsn + + @property + def webex_token(self) -> str: + """Returns the Webex API key.""" + return self.__webex_token + + +config: Config = Config() diff --git a/app/main.py b/app/main.py index a3fc1f8..feb74dc 100644 --- a/app/main.py +++ b/app/main.py @@ -1,18 +1,29 @@ #!/usr/local/bin/python3 -import os +import sentry_sdk +from sentry_sdk.integrations.stdlib import StdlibIntegration from webex_bot.webex_bot import WebexBot from app import close, meme +from app.config import config -WBX_API_KEY: str = os.environ["WEBEX_API_KEY"] + +if config.sentry_enabled: + apm = sentry_sdk.init( + dsn=config.sentry_dsn, + enable_tracing=True, + environment=config.environment, + release=config.version, + integrations=[StdlibIntegration()], + spotlight=True + ) def create_bot() -> WebexBot: """Create a Bot object.""" bot = WebexBot( - teams_bot_token=WBX_API_KEY, + teams_bot_token=config.webex_token, approved_domains=["cisco.com"], bot_name="MemeBot", include_demo_commands=False, diff --git a/pyproject.toml b/pyproject.toml index 3d96c91..2daf1cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ readme = "README.md" python = "^3.11.2" webex-bot = "^0.4.0" pillow = "^10.3.0" +sentry-sdk = "^1.45.0" [tool.poetry.group.dev.dependencies] black = "^24.4.0" diff --git a/requirements.txt b/requirements.txt index 3b8f97d..75f4219 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ pyjwt==2.8.0 pyreadline3==3.4.1 ; sys_platform == "win32" and python_full_version == "3.11.2" requests-toolbelt==1.0.0 requests==2.31.0 +sentry-sdk==1.45.0 urllib3==2.2.1 webex-bot==0.4.1 webexteamssdk==1.6.1 diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..e389636 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,24 @@ +"""Provides test cases for app/config.py.""" + +import os + + +vars: dict = { + "APP_VERSION": "dev", + "WEBEX_API_KEY": "testing", + "SENTRY_ENABLED": "false", + "SENTRY_DSN": "http://localhost" +} + + +for var, value in vars.items(): + os.environ[var] = value + +# needs to be imported AFTER environment variables are set +from app.config import config # pragma: no cover + + +def test_config() -> None: + assert config.webex_token == vars["WEBEX_API_KEY"] + assert config.version == vars["APP_VERSION"] + assert config.sentry_enabled == bool(vars["SENTRY_ENABLED"].lower() == "true") -- 2.45.2