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: 2 additions & 4 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ jobs:
python-version: '3.12'

- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v8.2.0

- name: Create virtual environment
run: uv venv

- name: Install requirements
run: |
uv pip install typer-invoke
uv run inv pip install dev
run: uv sync

- name: Linting
run: uv run inv lint all
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
python-version: '${{ matrix.python-version }}'

- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v8.2.0

- name: Create virtual environment
run: uv venv
Expand Down
164 changes: 0 additions & 164 deletions admin/pip.py

This file was deleted.

151 changes: 0 additions & 151 deletions admin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from dataclasses import dataclass
from enum import StrEnum
from itertools import chain
from pathlib import Path
from typing import Annotated

import typer
Expand All @@ -19,20 +18,6 @@
"""Sentinel object to represent an empty string."""


class Environment(StrEnum):
LOCAL = 'local'
"""Run in local machine, no Docker."""

DEV = 'dev'
"""Run in Docker in local machine."""

STAGING = 'staging'
"""Run in Docker in AWS (staging)."""

PROD = 'prod'
"""Run in Docker in AWS (prod)."""


class OS(StrEnum):
"""Operating System."""

Expand Down Expand Up @@ -81,11 +66,6 @@ def strip(self, text: str) -> str:
return text


EnvironmentAnnotation = Annotated[
Environment | None,
typer.Argument(help='Environment to use.', show_default=False),
]

LogLevelAnnotation = Annotated[
LogLevel,
typer.Option(
Expand All @@ -105,112 +85,6 @@ def strip(self, text: str) -> str:
]


def read_env_file_from_path(env_path: Path) -> dict[str, str]:
"""
Read a `.env` file. Minimal parser, no dependencies.
Does not update environment.

Rules:

- Ignores blank lines and comments.
- Supports `export KEY=VALUE`.
- Strips surrounding single/double quotes.
"""
if not env_path.exists():
raise FileNotFoundError(f'Env file not found: {env_path}')

env = {}
for raw_line in env_path.read_text(encoding='utf-8').splitlines():
line = raw_line.strip()
if not line or line.startswith('#'):
continue

if line.startswith('export '):
line = line.removeprefix('export ').lstrip()

key, sep, value = line.partition('=')
if not sep:
continue

key = key.strip()
value = value.strip()
if (value.startswith("'") and value.endswith("'")) or (
value.startswith('"') and value.endswith('"')
):
value = value[1:-1]

env[key] = value

return env


def get_env_file_path(environment: Environment) -> Path:
return PROJECT_ROOT / f'.env.{environment.value}'


def read_env_file(environment: Environment) -> dict[str, str]:
return read_env_file_from_path(get_env_file_path(environment))


# Another version, using `python-dotenv`
# from dotenv import dotenv_values
# def load_env_file(env_file: Path) -> dict[str, str | None]:
# """Load environment variables from the specified .env file."""
# if not env_file.exists():
# logger.error(f'Environment file not found: {env_file}')
# raise typer.Exit(1)
#
# env_vars = dotenv_values(env_file)
# logger.info(f'Loaded environment from: {env_file}')
# return env_vars


def select_environment(
environment: Environment | None = None,
set_env: bool = True,
**select_enum_kwargs,
) -> Environment:
"""
Select an environment and load its `.env` file into `os.environ`.

This project used to rely on `python-dotenv` for this. We keep the behavior (populate
`os.environ`) without depending on `python-dotenv`.
"""

from textual_searchable_selectionlist.options import SelectionStrategy
from textual_searchable_selectionlist.select import select_enum

if environment:
env = Environment(environment)
else:
try:
selected = select_enum(
Environment,
selection_strategy=SelectionStrategy.ONE,
title='Environment',
select_by='value',
**select_enum_kwargs,
)
except Exception as e:
logger.error(f'Error selecting environment: {type(e).__name__}: {e}')
raise typer.Exit(1)

if not selected:
logger.error('No environment selected.')
raise typer.Exit(1)

env = selected[0]

if set_env:
logger.debug(f'Setting environment [b]{env.value}[/b]')
env_values = read_env_file(env)
os.environ.update(env_values)
else:
logger.debug(f'Selected environment [b]{env.value}[/b]')

return env


def get_os() -> OS:
"""
Similar to ``sys.platform`` and ``platform.system()``, but less ambiguous by returning an Enum
Expand Down Expand Up @@ -333,31 +207,6 @@ def is_package_installed(package_name: str) -> bool:
return False


def install_package(
package: str,
package_install: str | None = None,
exit_if_install: bool = True,
dry: bool = False,
):
"""
Install a Python package if not already installed.

:param package: Name of the package to check/install.
:param package_install: Name of the package to install, if different from the name to check.
:param exit_if_install: Exit the program if the package is installed and `dry` is False.
:param dry: Show the command that would be run without running it.
"""
if is_package_installed(package):
logger.debug(f'Package `{package}` is already installed.')
return

run(sys.executable, '-m', 'pip', 'install', package_install or package, dry=dry)

if exit_if_install and not dry:
logger.info(f'Package `{package}` installed successfully.\nRe-run the command.')
raise typer.Exit(1)


def multiple_parameters(parameter: str, *options) -> list[str]:
return list(chain.from_iterable(zip([parameter] * len(options), map(str, options))))

Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,12 @@ dev = [
'packaging',
'pytest',
'ruff',
'textual_searchable_selectionlist',
'typer-invoke',
]

[tool.typer-invoke]
modules = [
'admin.build',
'admin.lint',
'admin.pip',
'admin.test',
]
Loading