Skip to content
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
6 changes: 4 additions & 2 deletions .github/workflows/python-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ name: CI/CD for simple-http-checker

on:
push:
branches: ['main']
branches: ['main', 'dev']
pull_request:
branches: ['main', 'dev']
workflow_dispatch:

jobs:
Expand Down Expand Up @@ -56,6 +58,7 @@ jobs:
run: |
pytest
release:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs:
- tests
Expand Down Expand Up @@ -83,4 +86,3 @@ jobs:
run: |
semantic-release version
semantic-release publish

File renamed without changes
24 changes: 24 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Writing Guide
# https://packaging.python.org/en/latest/guides/writing-pyproject-toml/

[build-system]
requires = ["setuptools>=77.0"]
build-backend = "setuptools.build_meta"
Expand All @@ -9,6 +12,27 @@ description = "A simple CLI tool to check the status of URLs."
readme = "README.md"
requires-python = ">=3.9"
dependencies = ["click>=8.0,<9.0", "requests>=2.28,<3.0"]
authors = [{ name = "Pedro Lima" }, { email = "bizak.dev@outlook.com" }]
license = { text = "MIT" }
classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Natural Language :: Portuguese (Brazilian)",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries :: Python Modules",
]

[project.urls]
Homepage = "https://github.com/PedroBizachi/python-devops-cicd-project"
Documentation = "https://github.com/lm-academy/python-devops-cicd-project/blob/main/README.md"
Repository = "https://github.com/PedroBizachi/python-devops-cicd-project"
Issues = "https://github.com/PedroBizachi/python-devops-cicd-project/issues"
Changelog = "https://github.com/PedroBizachi/python-devops-cicd-project/blob/main/CHANGELOG.md"

[project.optional-dependencies]
# Run `pip install .[dev]` to install newer dev dependencies
Expand Down
9 changes: 5 additions & 4 deletions src/simple_http_checker/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ def check_urls(urls: Collection[str], timeout: int = 5) -> dict[str, str]:
"""Checks a list of urls and returns their status.

Args:
urls (list[str]): A list of URLs to check.
urls (Collection[str]): A collection of URLs to check.
timeout (int, optional): The timeout in seconds for each URL check. Defaults to 5.

Returns:
dict[str, str]: A dictionary mapping URLs to their status.
"""

url_count = len(urls)
url_context = "URL" if url_count == 1 else "URLs"
logger.info(
f"Starting check for {len(urls)} URLs with a timeout of {timeout} seconds."
f"Starting check for {url_count} {url_context} with a timeout of {timeout} seconds."
)

results: dict[str, str] = {}
for url in urls:
status = "UNKNOWN"
try:
logger.debug(f"Checking URL: {url}")
response = requests.get(url, timeout=timeout)
Expand All @@ -39,7 +40,7 @@ def check_urls(urls: Collection[str], timeout: int = 5) -> dict[str, str]:
logger.warning(f"Connection error for URL: {url}")
except requests.exceptions.RequestException as e:
status = f"REQUEST_ERROR: {type(e).__name__}"
logger.exception(
logger.error(
f"An unexpected error occurred for {url}:",
)

Expand Down
39 changes: 32 additions & 7 deletions src/simple_http_checker/cli.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
import logging
from collections.abc import Collection
from typing import ClassVar

import click

from simple_http_checker.checker import check_urls

logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s] %(levelname)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",

class ColoredLevelFormatter(logging.Formatter):
LEVEL_COLORS: ClassVar[dict[int, str]] = {
logging.DEBUG: "cyan",
logging.INFO: "green",
logging.WARNING: "yellow",
logging.ERROR: "red",
logging.CRITICAL: "magenta",
}

def format(self, record: logging.LogRecord) -> str:
original_levelname = record.levelname
color = self.LEVEL_COLORS.get(record.levelno)
if color:
record.levelname = click.style(record.levelname, fg=color)

try:
return super().format(record)
finally:
record.levelname = original_levelname


handler = logging.StreamHandler()
handler.setFormatter(
ColoredLevelFormatter(
fmt="[%(asctime)s] %(levelname)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
)

logging.basicConfig(level=logging.INFO, handlers=[handler])

logger = logging.getLogger(__name__)


Expand All @@ -32,13 +59,11 @@ def main(urls: Collection[str], timeout: int, verbose: bool):
click.echo("Usage: check-urls <url1> <url2> ...")
return

logger.info(f"Starting check for {len(urls)} URLs...")

results = check_urls(urls, timeout)

click.echo("\n --- Results ---")
for url, status in results.items():
if "OK" in status:
if status.endswith("OK"):
fg_color = "green"
else:
fg_color = "red"
Expand Down
Loading