Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add mypy to ci #76

Merged
merged 7 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ run = ["uv run py.test"]
run = ["uv run mypy ."]

[tasks."ci"]
depends = ["lint", "format", "test"]
depends = ["lint", "format", "test", "mypy"]

[tasks."helm:lint"]
run = "helm install --dry-run --debug -f charts/gitops/values.yaml debug charts/gitops"

[tasks.release]
description = "Bump release versions across all files"
run = "python release.py"
run = "python release.py"
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.2.0
rev: v0.6.3
hooks:
- id: ruff
args:
- --fix
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.2.0
rev: v0.6.3
hooks:
- id: ruff-format
- repo: https://github.com/commitizen-tools/commitizen
Expand Down
21 changes: 0 additions & 21 deletions Makefile

This file was deleted.

2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ This is a git repository that you set up, where you list out all of your applica

Secrets should be placed in `secrets.env`. The example file `secrets.example.env` has the environment variables you will need to supply.

Gitops has a helm chart defining its deployment. Invoke scripts are provided to make deployment painless. See `tasks.py`.

Add `export GITOPS_APPS_DIRECTORY=~/<cluster-apps-folder>` to invoke gitops from any directory.

Ensure that gitops has `edit` access to the namespace it is deploying to. An example RoleBinding is:
Expand Down
1 change: 0 additions & 1 deletion gitops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import sys
from pathlib import Path

from . import monkeypatches # NOQA
from .utils.cli import success, warning

__version__ = "0.11.0"
Expand Down
2 changes: 1 addition & 1 deletion gitops/common/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def __init__(self, definition: dict | str):
# for backwards compat, any chart definition which is a string, is a git repo
self.type = "git"
self.git_sha = None
self.git_repo_url = definition
self.git_repo_url = definition or None
elif isinstance(definition, dict):
self.type = definition["type"]
self.git_sha = definition.get("git_sha")
Expand Down
2 changes: 1 addition & 1 deletion gitops/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def git_push(cluster_path: str, retry: int = 3):
attempts = 0
while attempts <= retry:
result = run(f"cd {cluster_path}; git push", warn=True)
if result.exited == 1 and "remote contains work" in result.stderr:
if result and result.exited == 1 and "remote contains work" in result.stderr:
attempts += 1
run(f"cd {cluster_path}; git pull --rebase=true")
else:
Expand Down
2 changes: 1 addition & 1 deletion gitops/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import time

import boto3
import dsnparse
import dsnparse # type: ignore[import]
from invoke import run, task

from .utils import kube
Expand Down
2 changes: 1 addition & 1 deletion gitops/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
namespace.add_task(task)

# Namespace the rarer ones.
namespace.add_collection(db)
namespace.add_collection(db) # type: ignore

program = Program(namespace=namespace, version=version)
8 changes: 0 additions & 8 deletions gitops/monkeypatches.py

This file was deleted.

8 changes: 6 additions & 2 deletions gitops/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ def gen_secret(length: int = 64) -> str:


def get_account_id() -> str:
# TODO REMOVE THIS FUNCTION and account_id in general

if "ACCOUNT_ID" not in CACHE:
# This is not ideal, as it makes an assumption that the account_id of interest is the
# one the user is currently sitting in. Ideally, should ask the cluster (though that is
# messy in its own right, since we don't necessarily want the cluster that's in the
# current context either).
caller_identity = run("aws sts get-caller-identity", hide=True).stdout.strip()
CACHE["ACCOUNT_ID"] = json.loads(caller_identity)["Account"]
result = run("aws sts get-caller-identity", hide=True)
if result:
caller_identity = result.stdout.strip()
CACHE["ACCOUNT_ID"] = json.loads(caller_identity)["Account"]
return CACHE["ACCOUNT_ID"]


Expand Down
4 changes: 2 additions & 2 deletions gitops/utils/apps.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sys
from pathlib import PosixPath
from pathlib import Path

from colorama import Fore
from tabulate import tabulate
Expand All @@ -15,7 +15,7 @@
from .tags import colour_tags, validate_tags


def is_valid_app_directory(directory: PosixPath) -> bool:
def is_valid_app_directory(directory: Path) -> bool:
files = ["deployment.yml", "secrets.yml"]
file_paths = [(directory / file).is_file() for file in files]
return all(file_paths)
Expand Down
1 change: 1 addition & 0 deletions gitops/utils/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections.abc import Callable

from colorama import Fore


Expand Down
2 changes: 1 addition & 1 deletion gitops/utils/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def colour_image(image_tag: str) -> str:
return colourise(bits[0], color_hash(bits[0]))


def color_hash(bit: str) -> Fore: # type: ignore[no-untyped-def]
def color_hash(bit: str) -> str:
return [
Fore.RED,
Fore.GREEN,
Expand Down
16 changes: 8 additions & 8 deletions gitops/utils/kube.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import yaml
from colorama import Fore
from invoke import run
from invoke.exceptions import UnexpectedExit
from invoke.exceptions import Failure, UnexpectedExit

from gitops.common.app import App
from gitops.settings import get_apps_directory
Expand Down Expand Up @@ -55,8 +55,8 @@ async def run_job(
"image": app.image,
"serviceAccountName": app.service_account_name,
}
extra_labels = {}
container_resources = {}
extra_labels: dict[str, str] = {}
container_resources: ContainerResources | None = None

if cpu and memory:
cpu_request = f"{cpu}m" if cpu else ""
Expand Down Expand Up @@ -275,10 +275,10 @@ async def _find_pod() -> str:
_, _, output_log = await async_run(cmd)
logs += output_log

except Exception as e:
except Failure as e:
if "current phase is Succeeded" not in str(e.result.stdout):
raise e
except Exception as e:
except Failure as e:
if "current phase is Succeeded" not in str(e.result.stdout):
raise e
finally:
Expand All @@ -292,9 +292,9 @@ async def wait_for_pod(context: str, namespace: str, pod: str) -> str | None:
while True:
cmd = "kubectl get pod" f" --context {context}" f" -n {namespace}" ' -o jsonpath="{.status.phase}"' f" {pod}"
stdout, _, _ = await async_run(cmd)
stdout = stdout.decode().lower()
if stdout != "pending":
return stdout
output = stdout.decode().lower()
if output != "pending":
return output


def retry(*args: object, max_attempts: int = 3, delay: int = 1): # noqa: C901
Expand Down
8 changes: 4 additions & 4 deletions gitops/utils/yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ def increase_indent(self, flow=False, indentless=False):
if pyyaml.safe_dump is pyyaml.dump:
# PyYAML v4.1
SafeDumper = pyyaml.dumper.Dumper
DangerDumper = pyyaml.dumper.DangerDumper
DangerDumper = pyyaml.dumper.DangerDumper # type: ignore
else:
SafeDumper = pyyaml.dumper.SafeDumper
SafeDumper = pyyaml.dumper.SafeDumper # type: ignore
DangerDumper = pyyaml.dumper.Dumper

SafeDumper.increase_indent = increase_indent
SafeDumper.increase_indent = increase_indent # type: ignore
DangerDumper.increase_indent = increase_indent

pyyaml.add_representer(dict, map_representer, Dumper=SafeDumper)
Expand All @@ -43,4 +43,4 @@ def increase_indent(self, flow=False, indentless=False):
# Merge PyYAML namespace into ours.
# This allows users a drop-in replacement:
# import utils.yaml as yaml
from yaml import * # noqa isort:skip
from yaml import * # type: ignore # noqa isort:skip
13 changes: 0 additions & 13 deletions gitops_server/logging_config.py

This file was deleted.

1 change: 0 additions & 1 deletion gitops_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from gitops_server import settings
from gitops_server.app import app
from gitops_server.logging_config import * # noqa
from gitops_server.workers import DeploymentStatusWorker, DeployQueueWorker

logging.basicConfig(level=logging.INFO)
Expand Down
2 changes: 1 addition & 1 deletion gitops_server/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async def run(command, suppress_errors=False) -> RunOutput:
)

stdout, stderr = await proc.communicate()
exit_code = proc.returncode
exit_code = proc.returncode or 1
if exit_code == 0:
return RunOutput(exit_code=exit_code, output=stdout.decode())
else:
Expand Down
3 changes: 1 addition & 2 deletions gitops_server/utils/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
GITHUB_OAUTH_TOKEN = os.environ.get("GITHUB_OAUTH_TOKEN")


class IssueNotFound(Exception):
...
class IssueNotFound(Exception): ...


class STATUSES:
Expand Down
9 changes: 5 additions & 4 deletions gitops_server/utils/slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ async def post(message: str) -> None:
logger.info("POSTING TO SLACK")
data = {"text": message}
async with httpx.AsyncClient() as client:
response = await client.post(SLACK_URL, json=data)
if response.status_code >= 300:
logger.warning("Failed to post a message to slack (see below):")
logger.error(f"{message}", exc_info=True)
if SLACK_URL:
response = await client.post(SLACK_URL, json=data)
if response.status_code >= 300:
logger.warning("Failed to post a message to slack (see below):")
logger.error(f"{message}", exc_info=True)


async def find_commiter_slack_user(name: str, email: str) -> SlackUser | None:
Expand Down
11 changes: 6 additions & 5 deletions gitops_server/workers/deployer/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ async def post_init_summary(source, username, added_apps, updated_apps, removed_

async def post_result(app: App, result: UpdateAppResult, deployer: "Deployer", **kwargs):
if result["exit_code"] != 0:
result = await handle_failed_deploy(app, result, deployer)

deploy_result = await handle_failed_deploy(app, result, deployer)
message = (
result.get("slack_message")
deploy_result["slack_message"]
or f"Failed to deploy app `{result['app_name']}` for cluster `{settings.CLUSTER_NAME}`:\n>>>{result['output']}"
)

Expand Down Expand Up @@ -139,7 +138,8 @@ async def uninstall_app(self, app: App) -> UpdateAppResult:
async with self.semaphore:
logger.info(f"Uninstalling app {app.name!r}.")
result = await run(f"helm uninstall {app.name} -n {app.namespace}", suppress_errors=True)
update_result = UpdateAppResult(app_name=app.name, **result)
if result:
update_result = UpdateAppResult(app_name=app.name, slack_message="", **result)
await post_result(
app=app,
result=update_result,
Expand All @@ -156,6 +156,7 @@ async def update_app_deployment(self, app: App) -> UpdateAppResult | None:
async with self.semaphore:
logger.info(f"Deploying app {app.name!r}.")
if app.chart.type == "git":
assert app.chart.git_repo_url
async with temp_repo(app.chart.git_repo_url, sha=app.chart.git_sha) as chart_folder_path:
await run(f"cd {chart_folder_path}; helm dependency build")
with tempfile.NamedTemporaryFile(suffix=".yml") as cfg:
Expand Down Expand Up @@ -195,7 +196,7 @@ async def update_app_deployment(self, app: App) -> UpdateAppResult | None:
logger.warning("Local is not implemented yet")
return None

update_result = UpdateAppResult(app_name=app.name, **result)
update_result = UpdateAppResult(app_name=app.name, slack_message="", **result)

await post_result(app=app, result=update_result, deployer=self)
return update_result
Expand Down
4 changes: 2 additions & 2 deletions gitops_server/workers/deployer/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from gitops_server import settings
from gitops_server.types import UpdateAppResult
from gitops_server.utils import github
from gitops_server.utils.slack import SlackGroup, find_commiter_slack_user
from gitops_server.utils.slack import SlackGroup, SlackUser, find_commiter_slack_user

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -74,7 +74,7 @@ async def handle_failed_deploy(app: App, result: UpdateAppResult, deployer) -> U
email = deployer.author_email

if "devops" in email.lower() or "tickforge" in email.lower():
slack_user = DEFAULT_USER_GROUP
slack_user: SlackGroup | SlackUser = DEFAULT_USER_GROUP
else:
slack_user = (
await find_commiter_slack_user(name=deployer.author_name, email=deployer.author_email) or DEFAULT_USER_GROUP
Expand Down
6 changes: 3 additions & 3 deletions gitops_server/workers/status_updater/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import asyncio
import logging

import kubernetes_asyncio
import kubernetes_asyncio.client
import kubernetes_asyncio.config
import kubernetes_asyncio # type:ignore[import-untyped]
import kubernetes_asyncio.client # type:ignore[import-untyped]
import kubernetes_asyncio.config # type:ignore[import-untyped]

from gitops_server.settings import CLUSTER_NAMESPACE
from gitops_server.utils import github
Expand Down
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ build-backend = "hatchling.build"
dev-dependencies = [
"pytest>=8.3.2",
"ruff>=0.6.3",
"types-colorama>=0.4.15.20240311",
"mypy>=1.11.2",
"types-pyyaml>=6.0.12.20240808",
"types-tabulate>=0.9.0.20240106",
"boto3-stubs>=1.35.11",
]


Expand All @@ -50,3 +55,6 @@ style = [
'py://nitpick/resources/any/commitizen',
]
cache = "never"

[tool.mypy]
exclude = ["tests"]
Loading
Loading