Skip to content

Conversation

kzndotsh
Copy link
Contributor

@kzndotsh kzndotsh commented Aug 10, 2025

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context. List any dependencies that are required for this change. If this change fixes any issues please put "Fixes #XX" in the description. Please also ensure to add the appropriate labels to the PR.

Guidelines

  • My code follows the style guidelines of this project (formatted with Ruff)

  • I have performed a self-review of my own code

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation if needed

  • My changes generate no new warnings

  • I have tested this change

  • Any dependent changes have been merged and published in downstream modules

  • I have added all appropriate labels to this PR

  • I have followed all of these guidelines.

How Has This Been Tested? (if applicable)

Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable)

Screenshots (if applicable)

Please add screenshots to help explain your changes.

Additional Information

Please add any other information that is important to this PR.

Summary by Sourcery

Migrate project from Poetry to Uv (Hatch) as the dependency and build tool, restructure code under src/, introduce a dependency injection container with BaseCog for service resolution, centralize Sentry integration via a new SentryManager, and overhaul tracing/error handling instrumentation. Update CLI commands, Docker configurations, CI workflows, documentation, and tests to use Uv, add unit/integration/e2e test markers with isolation, and bump version to v0.1.0.

New Features:

  • Add a ServiceContainer and ServiceRegistry for dependency injection with IBotService, IConfigService, and IDatabaseService protocols
  • Introduce SentryManager to centralize Sentry SDK initialization, event capturing, before‐send filtering, and performance tracing
  • Implement BaseCog to automatically inject services into cogs

Enhancements:

  • Migrate build system and dependency management from Poetry to Uv (Hatchling) and update pyproject.toml accordingly
  • Reorganize code layout under src/tux with separate core, services, shared, modules, and custom_modules packages
  • Replace manual Sentry calls and inline spans with decorators and context managers for automatic command and span instrumentation

Build:

  • Switch build backend to Hatchling, add uv.lock, and configure project scripts for Uv CLI

CI:

  • Update GitHub workflows to install and use Uv, adjust cache keys, and replace Poetry commands with Uv equivalents

Documentation:

  • Revise README, CONTRIBUTING, docs content, and Docker guides to reference Uv instead of Poetry
  • Update test documentation to include unit/integration/e2e markers, network blocking, and deterministic environment setup

Tests:

  • Enhance pytest configuration with --run-integration, --run-e2e, and --allow-network flags
  • Block outbound network by default for unit tests and better isolate filesystem using tmp_path

Chores:

  • Bump project version to 0.1.0 across pyproject.toml and metadata files

@kzndotsh kzndotsh changed the title V0.1.0 DRAFT: v0.1.0 Aug 10, 2025
Copy link

cloudflare-workers-and-pages bot commented Aug 10, 2025

Deploying tux with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5c2f948
Status:🚫  Build failed.

View logs

@kzndotsh kzndotsh self-assigned this Aug 10, 2025
Copy link
Contributor

github-actions bot commented Aug 10, 2025

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

@kzndotsh
Copy link
Contributor Author

@sourcery-ai review

@kzndotsh kzndotsh marked this pull request as ready for review August 11, 2025 12:50
Copy link
Contributor

sourcery-ai bot commented Aug 11, 2025

Reviewer's Guide

This PR modernizes the Tux codebase by reorganizing source files under a src/ layout, introducing a dependency injection container for core services, migrating build and dependency tooling from Poetry to Uv/Hatch, centralizing Sentry integration with a dedicated manager and tracing utilities, and enhancing test configuration with pytest fixtures and markers.

Entity relationship diagram for database controller module reorganization

erDiagram
    AfkController ||--o{ DatabaseController : provides
    CaseController ||--o{ DatabaseController : provides
    GuildController ||--o{ DatabaseController : provides
    GuildConfigController ||--o{ DatabaseController : provides
    LevelsController ||--o{ DatabaseController : provides
    NoteController ||--o{ DatabaseController : provides
    ReminderController ||--o{ DatabaseController : provides
    SnippetController ||--o{ DatabaseController : provides
    StarboardController ||--o{ DatabaseController : provides
    StarboardMessageController ||--o{ DatabaseController : provides
    DatabaseController }o--|| IDatabaseService : implements
Loading

Class diagram for Tux bot and dependency injection changes

classDiagram
    class Tux {
        +ServiceContainer container
        +SentryManager sentry_manager
        +EmojiManager emoji_manager
        +DatabaseService db
        +setup()
        +_setup_container()
        +_validate_container()
        +_raise_container_validation_error()
        +_cleanup_container()
    }
    class ServiceContainer {
        +get_optional(interface)
    }
    class ServiceRegistry {
        +configure_container(bot)
        +validate_container(container)
        +get_registered_services(container)
    }
    class SentryManager {
        +setup()
        +capture_exception()
        +finish_transaction_on_error()
        +set_command_context()
        +flush_async()
        +is_initialized
    }
    Tux --> ServiceContainer
    Tux --> SentryManager
    Tux --> EmojiManager
    Tux --> DatabaseService
    ServiceRegistry --> ServiceContainer
    ServiceContainer --> SentryManager
    ServiceContainer --> EmojiManager
    ServiceContainer --> DatabaseService
Loading

Class diagram for ConfigSet* UI views with DI changes

classDiagram
    class ConfigSetPrivateLogs {
        +__init__(timeout, bot, db_service)
        -db: GuildConfigController
    }
    class ConfigSetPublicLogs {
        +__init__(timeout, bot, db_service)
        -db: GuildConfigController
    }
    class ConfigSetChannels {
        +__init__(timeout, bot, db_service)
        -db: GuildConfigController
    }
    class IDatabaseService {
        +get_controller()
    }
    class GuildConfigController {
    }
    ConfigSetPrivateLogs --> IDatabaseService
    ConfigSetPublicLogs --> IDatabaseService
    ConfigSetChannels --> IDatabaseService
    IDatabaseService --> GuildConfigController
Loading

Class diagram for Setup cog refactor to use DI and base class

classDiagram
    class Setup {
        +__init__(bot)
        -config: GuildConfigController
    }
    class BaseCog {
        +__init__(bot)
        -db: IDatabaseService
    }
    class IDatabaseService {
        +get_controller()
    }
    class GuildConfigController {
    }
    Setup --|> BaseCog
    BaseCog --> IDatabaseService
    IDatabaseService --> GuildConfigController
Loading

File-Level Changes

Change Details Files
Migration from Poetry to Uv/Hatch for dependency and build tooling
  • Replaced Poetry configuration in pyproject.toml with Hatch/Uv sections and dependency groups
  • Renamed lock file from poetry.lock to uv.lock and updated related scripts
  • Updated Dockerfile, GitHub Actions, and docker-compose files to install and invoke Uv instead of Poetry
pyproject.toml
Dockerfile
.github/actions/setup-python/action.yml
docker-compose.yml
docker-compose.dev.yml
.github/workflows/tests.yml
.github/workflows/ci.yml
.github/workflows/security.yml
shell.nix
Introduction of dependency injection container and service registry
  • Added ServiceContainer, ServiceDescriptor, and lifetimes in core/container.py
  • Created ServiceRegistry to configure and validate core services
  • Defined service interfaces (core/interfaces.py) and concrete implementations (core/services.py)
  • Extended Bot to initialize and use the container and register services
src/tux/core/container.py
src/tux/core/service_registry.py
src/tux/core/services.py
src/tux/core/interfaces.py
src/tux/core/types.py
src/tux/core/base_cog.py
src/tux/core/bot.py
Project structure reorganization and namespacing
  • Moved code into src/tux/{core,services,shared,modules} hierarchy
  • Renamed tux/cogs to tux/modules and updated CLI, imports, and README paths
  • Consolidated shared utilities under tux/shared, handlers under tux/services/handlers, and tracing under tux/services/tracing
src/tux
README.md
docs/content
tux/cog_loader.py
tests/README.md
Centralized Sentry integration and tracing overhaul
  • Introduced SentryManager for init, context management, and event capturing
  • Created tracing utilities with @transaction/@span decorators and enhanced context managers
  • Replaced inline sentry_sdk calls in error handlers with SentryManager and tracing helpers
  • Added SentryHandler cog for automatic error status and context tagging
src/tux/services/sentry_manager.py
src/tux/services/tracing.py
src/tux/services/handlers/sentry.py
src/tux/services/handlers/error.py
Enhanced pytest configuration and test isolation
  • Added pytest_addoption markers for integration, e2e, and network control
  • Implemented session-wide environment defaults and unit-test filesystem/network isolation
  • Updated tests/README.md with suite structure and CLI commands
  • Configured skip logic for integration/e2e tests and tightened warnings policy
tests/conftest.py
tests/README.md
.pre-commit-config.yaml

Possibly linked issues

  • #1: The PR implements robust error handling, Sentry integration, and a new DI system, all key parts of the v0.1.0 roadmap.
  • #0: PR implements extensive architectural refactor: new src/tux structure, dependency injection, and uv migration, aligning with issue's goals.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kzndotsh - I've reviewed your changes and they look great!

Blocking issues:

  • By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'. (link)
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/tux/services/tracing.py:80` </location>
<code_context>
+# --- Common Helpers ---
+
+
+def safe_set_name(obj: Any, name: str) -> None:
+    """
+    Safely set the name on a span or transaction object.
</code_context>

<issue_to_address>
safe_set_name may fail silently if set_name is not callable.

Add a check to ensure set_name is callable before invoking it to prevent silent failures.
</issue_to_address>

### Comment 2
<location> `src/tux/services/tracing.py:598` </location>
<code_context>
+            raise
+
+
+def instrument_bot_commands(bot: commands.Bot) -> None:
+    """
+    Automatically instruments all bot commands with Sentry transactions.
</code_context>

<issue_to_address>
Instrumenting bot commands by replacing callbacks may interfere with other decorators.

Directly replacing the command callback can disrupt existing decorators or attributes. Using a wrapper or middleware would help maintain original metadata.
</issue_to_address>

### Comment 3
<location> `src/tux/core/container.py:244` </location>
<code_context>
+                return descriptor.instance
+
+        # Create new instance
+        self._resolution_stack.add(service_type)
+
+        try:
</code_context>

<issue_to_address>
Using a set for _resolution_stack may not preserve dependency order for error reporting.

Consider replacing the set with a list or stack to maintain resolution order, which will improve error messages for circular dependencies.

Suggested implementation:

```python
        # Create new instance
        self._resolution_stack.append(service_type)

```

```python
        try:

```

```python
        except Exception as e:
            stack_trace = " -> ".join([t.__name__ for t in self._resolution_stack])
            logger.error(f"Failed to resolve {service_type.__name__}: {e}\nResolution stack: {stack_trace}")
            error_msg = f"Cannot resolve {service_type.__name__} (resolution stack: {stack_trace})"
            raise ServiceResolutionError(error_msg) from e

```

```python
        else:
            resolution_time = time.perf_counter() - start_time
            # Only log if resolution takes longer than expected or fails
            if resolution_time > 0.001:  # Log if takes more than 1ms
                logger.debug(f"Slow resolution: {service_type.__name__} took {resolution_time:.4f}s")
            self._resolution_stack.pop()

```

You will also need to:
1. Change the initialization of `self._resolution_stack` from a set to a list (e.g., `self._resolution_stack = []`).
2. Update any other code that checks membership (`in`) or removes items from `_resolution_stack` to use list semantics.
3. If you have circular dependency checks, you can still use `if service_type in self._resolution_stack:` for lists.
</issue_to_address>

### Comment 4
<location> `src/tux/core/container.py:299` </location>
<code_context>
+            # Resolve the dependency
+            dependency = self._resolve_service(param_type)
+
+            if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
+                if param.default is inspect.Parameter.empty:
+                    args.append(dependency)
+                else:
+                    kwargs[param.name] = dependency
+            elif param.kind == inspect.Parameter.KEYWORD_ONLY:
+                kwargs[param.name] = dependency
+
</code_context>

<issue_to_address>
Does not handle VAR_POSITIONAL or VAR_KEYWORD constructor parameters.

Currently, *args and **kwargs in service constructors are not processed. Please add explicit handling for VAR_POSITIONAL and VAR_KEYWORD parameters, either by raising an error or documenting their unsupported status.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
            # Resolve the dependency
            dependency = self._resolve_service(param_type)

            if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
                if param.default is inspect.Parameter.empty:
                    args.append(dependency)
                else:
                    kwargs[param.name] = dependency
            elif param.kind == inspect.Parameter.KEYWORD_ONLY:
                kwargs[param.name] = dependency
=======
            # Resolve the dependency
            dependency = self._resolve_service(param_type)

            if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
                if param.default is inspect.Parameter.empty:
                    args.append(dependency)
                else:
                    kwargs[param.name] = dependency
            elif param.kind == inspect.Parameter.KEYWORD_ONLY:
                kwargs[param.name] = dependency
            elif param.kind == inspect.Parameter.VAR_POSITIONAL:
                raise ServiceResolutionError(
                    f"Constructor parameter '*{param.name}' in {impl_type.__name__} is not supported by the DI container"
                )
            elif param.kind == inspect.Parameter.VAR_KEYWORD:
                raise ServiceResolutionError(
                    f"Constructor parameter '**{param.name}' in {impl_type.__name__} is not supported by the DI container"
                )
>>>>>>> REPLACE

</suggested_fix>

### Comment 5
<location> `docs/content/dev/local_development.md:37` </location>
<code_context>
 The project includes a hot-reloading utility (`tux/utils/hot_reload.py`).

-When the bot is running locally via `poetry run tux --dev start`, this utility watches for changes in the `tux/cogs/` directory. It attempts to automatically reload modified cogs or cogs affected by changes in watched utility files without requiring a full bot restart.
+When the bot is running locally via `uv run tux --dev start`, this utility watches for changes in the `tux/cogs/` directory. It attempts to automatically reload modified cogs or cogs affected by changes in watched utility files without requiring a full bot restart.

-This significantly speeds up development for cog-related changes. Note that changes outside the watched directories (e.g., core bot logic, dependencies) may still require a manual restart (`Ctrl+C` and run the start command again).
</code_context>

<issue_to_address>
Update directory and terminology to match new module structure.

Please update all directory and terminology references to align with the new module structure for consistency.
</issue_to_address>

## Security Issues

### Issue 1
<location> `Dockerfile:259` </location>

<issue_to_address>
**security (dockerfile.security.missing-user):** By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'.

```suggestion
USER non-root
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
```

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

# --- Common Helpers ---


def safe_set_name(obj: Any, name: str) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): safe_set_name may fail silently if set_name is not callable.

Add a check to ensure set_name is callable before invoking it to prevent silent failures.

raise


def instrument_bot_commands(bot: commands.Bot) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Instrumenting bot commands by replacing callbacks may interfere with other decorators.

Directly replacing the command callback can disrupt existing decorators or attributes. Using a wrapper or middleware would help maintain original metadata.

return descriptor.instance

# Create new instance
self._resolution_stack.add(service_type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Using a set for _resolution_stack may not preserve dependency order for error reporting.

Consider replacing the set with a list or stack to maintain resolution order, which will improve error messages for circular dependencies.

Suggested implementation:

        # Create new instance
        self._resolution_stack.append(service_type)
        try:
        except Exception as e:
            stack_trace = " -> ".join([t.__name__ for t in self._resolution_stack])
            logger.error(f"Failed to resolve {service_type.__name__}: {e}\nResolution stack: {stack_trace}")
            error_msg = f"Cannot resolve {service_type.__name__} (resolution stack: {stack_trace})"
            raise ServiceResolutionError(error_msg) from e
        else:
            resolution_time = time.perf_counter() - start_time
            # Only log if resolution takes longer than expected or fails
            if resolution_time > 0.001:  # Log if takes more than 1ms
                logger.debug(f"Slow resolution: {service_type.__name__} took {resolution_time:.4f}s")
            self._resolution_stack.pop()

You will also need to:

  1. Change the initialization of self._resolution_stack from a set to a list (e.g., self._resolution_stack = []).
  2. Update any other code that checks membership (in) or removes items from _resolution_stack to use list semantics.
  3. If you have circular dependency checks, you can still use if service_type in self._resolution_stack: for lists.

Comment on lines 296 to 305
# Resolve the dependency
dependency = self._resolve_service(param_type)

if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
if param.default is inspect.Parameter.empty:
args.append(dependency)
else:
kwargs[param.name] = dependency
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
kwargs[param.name] = dependency
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Does not handle VAR_POSITIONAL or VAR_KEYWORD constructor parameters.

Currently, *args and **kwargs in service constructors are not processed. Please add explicit handling for VAR_POSITIONAL and VAR_KEYWORD parameters, either by raising an error or documenting their unsupported status.

Suggested change
# Resolve the dependency
dependency = self._resolve_service(param_type)
if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
if param.default is inspect.Parameter.empty:
args.append(dependency)
else:
kwargs[param.name] = dependency
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
kwargs[param.name] = dependency
# Resolve the dependency
dependency = self._resolve_service(param_type)
if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
if param.default is inspect.Parameter.empty:
args.append(dependency)
else:
kwargs[param.name] = dependency
elif param.kind == inspect.Parameter.KEYWORD_ONLY:
kwargs[param.name] = dependency
elif param.kind == inspect.Parameter.VAR_POSITIONAL:
raise ServiceResolutionError(
f"Constructor parameter '*{param.name}' in {impl_type.__name__} is not supported by the DI container"
)
elif param.kind == inspect.Parameter.VAR_KEYWORD:
raise ServiceResolutionError(
f"Constructor parameter '**{param.name}' in {impl_type.__name__} is not supported by the DI container"
)

The project includes a hot-reloading utility (`tux/utils/hot_reload.py`).

When the bot is running locally via `poetry run tux --dev start`, this utility watches for changes in the `tux/cogs/` directory. It attempts to automatically reload modified cogs or cogs affected by changes in watched utility files without requiring a full bot restart.
When the bot is running locally via `uv run tux --dev start`, this utility watches for changes in the `tux/cogs/` directory. It attempts to automatically reload modified cogs or cogs affected by changes in watched utility files without requiring a full bot restart.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Update directory and terminology to match new module structure.

Please update all directory and terminology references to align with the new module structure for consistency.

Dockerfile Outdated
# WORKFLOW: Regenerates Prisma client and starts the bot in development mode
# This ensures the database client is always up-to-date with schema changes
CMD ["sh", "-c", "poetry run prisma generate && exec poetry run tux --dev start"]
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (dockerfile.security.missing-user): By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'.

Suggested change
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
USER non-root
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]

Source: opengrep

Comment on lines 5 to 6
# Keep E2E minimal and deterministic; expand with CLI or HTTP flows later
assert True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Remove assert True statements (remove-assert-true)

Suggested change
# Keep E2E minimal and deterministic; expand with CLI or HTTP flows later
assert True
pass

Comment on lines 5 to 6
# Example of an integration placeholder; expand with real IO later
assert 1 + 1 == 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

Suggested change
# Example of an integration placeholder; expand with real IO later
assert 1 + 1 == 2
pass


@pytest.mark.unit
def test_smoke() -> None:
assert True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Remove assert True statements (remove-assert-true)

Suggested change
assert True
pass

sourcery-ai[bot]
sourcery-ai bot previously requested changes Aug 11, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kzndotsh - I've reviewed your changes and they look great!

Blocking issues:

  • By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'. (link)
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/tux/services/sentry_manager.py:582` </location>
<code_context>
+        for key, value in tags.items():
+            scope.set_tag(key, value)
+
+    def set_user_context(self, user: discord.User | discord.Member) -> None:
+        """
+        Sets the user context for the current Sentry scope.
</code_context>

<issue_to_address>
set_user_context may expose sensitive user information.

Review the user and member fields being attached to Sentry to ensure only essential, non-sensitive information is included, particularly for production use.
</issue_to_address>

### Comment 2
<location> `src/tux/core/container.py:57` </location>
<code_context>
+        """Initialize an empty service container."""
+        self._services: dict[type, ServiceDescriptor] = {}
+        self._singleton_instances: dict[type, Any] = {}
+        self._resolution_stack: set[type] = set()
+
+    def register_singleton(self, service_type: type[T], implementation: type[T] | None = None) -> "ServiceContainer":
</code_context>

<issue_to_address>
Using a set for the resolution stack may obscure the actual dependency chain in circular dependency errors.

Using a list for the resolution stack will preserve the order of dependencies, making circular dependency errors easier to trace.

Suggested implementation:

```python
        self._resolution_stack: list[type] = []

```

You will need to update all usages of `_resolution_stack` throughout the file:
- Replace `.add(x)` with `.append(x)`
- Replace `.remove(x)` with `.pop()` (if popping the last item) or `del` (if removing by index)
- If checking for membership, `x in self._resolution_stack` still works
- When reporting circular dependencies, you can now print the stack in order for better error messages
</issue_to_address>

### Comment 3
<location> `src/tux/core/container.py:235` </location>
<code_context>
+        descriptor = self._services[service_type]
+
+        # Return existing instance for singletons
+        if descriptor.lifetime == ServiceLifetime.SINGLETON:
+            if service_type in self._singleton_instances:
+                return self._singleton_instances[service_type]
+
+            # If we have a pre-registered instance, return it
+            if descriptor.instance is not None:
+                return descriptor.instance
+
</code_context>

<issue_to_address>
Singleton instance caching logic may be redundant if both instance and _singleton_instances are used.

Consolidate singleton instance handling to ensure a single source of truth and prevent precedence ambiguity.

Suggested implementation:

```python
        # Return existing instance for singletons
        if descriptor.lifetime == ServiceLifetime.SINGLETON:
            if service_type in self._singleton_instances:
                return self._singleton_instances[service_type]

```

To fully implement this, you must ensure that any pre-registered singleton instance (previously stored in `descriptor.instance`) is instead stored in `_singleton_instances` at registration time. This may require updating your service registration logic elsewhere in the codebase to move any instance assignment to `_singleton_instances`.
</issue_to_address>

### Comment 4
<location> `src/tux/core/context.py:95` </location>
<code_context>
+        A dictionary with standardized context keys like `user_id`,
+        `command_name`, `guild_id`, `command_type`, etc.
+    """
+    user = source.user if isinstance(source, Interaction) else source.author
+
+    # Base context is common to both types
</code_context>

<issue_to_address>
Assumes that both Context and Interaction have user/author attributes, which may not always be true.

Add error handling or a fallback to prevent AttributeError if the expected attribute is missing.
</issue_to_address>

## Security Issues

### Issue 1
<location> `Dockerfile:259` </location>

<issue_to_address>
**security (dockerfile.security.missing-user):** By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'.

```suggestion
USER non-root
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
```

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

for key, value in tags.items():
scope.set_tag(key, value)

def set_user_context(self, user: discord.User | discord.Member) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 suggestion (security): set_user_context may expose sensitive user information.

Review the user and member fields being attached to Sentry to ensure only essential, non-sensitive information is included, particularly for production use.

"""Initialize an empty service container."""
self._services: dict[type, ServiceDescriptor] = {}
self._singleton_instances: dict[type, Any] = {}
self._resolution_stack: set[type] = set()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Using a set for the resolution stack may obscure the actual dependency chain in circular dependency errors.

Using a list for the resolution stack will preserve the order of dependencies, making circular dependency errors easier to trace.

Suggested implementation:

        self._resolution_stack: list[type] = []

You will need to update all usages of _resolution_stack throughout the file:

  • Replace .add(x) with .append(x)
  • Replace .remove(x) with .pop() (if popping the last item) or del (if removing by index)
  • If checking for membership, x in self._resolution_stack still works
  • When reporting circular dependencies, you can now print the stack in order for better error messages

Comment on lines 235 to 240
if descriptor.lifetime == ServiceLifetime.SINGLETON:
if service_type in self._singleton_instances:
return self._singleton_instances[service_type]

# If we have a pre-registered instance, return it
if descriptor.instance is not None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Singleton instance caching logic may be redundant if both instance and _singleton_instances are used.

Consolidate singleton instance handling to ensure a single source of truth and prevent precedence ambiguity.

Suggested implementation:

        # Return existing instance for singletons
        if descriptor.lifetime == ServiceLifetime.SINGLETON:
            if service_type in self._singleton_instances:
                return self._singleton_instances[service_type]

To fully implement this, you must ensure that any pre-registered singleton instance (previously stored in descriptor.instance) is instead stored in _singleton_instances at registration time. This may require updating your service registration logic elsewhere in the codebase to move any instance assignment to _singleton_instances.

A dictionary with standardized context keys like `user_id`,
`command_name`, `guild_id`, `command_type`, etc.
"""
user = source.user if isinstance(source, Interaction) else source.author
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Assumes that both Context and Interaction have user/author attributes, which may not always be true.

Add error handling or a fallback to prevent AttributeError if the expected attribute is missing.

Dockerfile Outdated
# WORKFLOW: Regenerates Prisma client and starts the bot in development mode
# This ensures the database client is always up-to-date with schema changes
CMD ["sh", "-c", "poetry run prisma generate && exec poetry run tux --dev start"]
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (dockerfile.security.missing-user): By not specifying a USER, a program in the container may run as 'root'. This is a security hazard. If an attacker can control a process running as root, they may have control over the container. Ensure that the last USER in a Dockerfile is a USER other than 'root'.

Suggested change
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]
USER non-root
CMD ["sh", "-c", "uv run prisma generate && exec uv run tux --dev start"]

Source: opengrep

Comment on lines 5 to 6
# Keep E2E minimal and deterministic; expand with CLI or HTTP flows later
assert True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Remove assert True statements (remove-assert-true)

Suggested change
# Keep E2E minimal and deterministic; expand with CLI or HTTP flows later
assert True
pass

Comment on lines 5 to 6
# Example of an integration placeholder; expand with real IO later
assert 1 + 1 == 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

Suggested change
# Example of an integration placeholder; expand with real IO later
assert 1 + 1 == 2
pass


@pytest.mark.unit
def test_smoke() -> None:
assert True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Remove assert True statements (remove-assert-true)

Suggested change
assert True
pass

sourcery-ai[bot]

This comment was marked as resolved.

SourceryAI

This comment was marked as resolved.

@allthingslinux allthingslinux deleted a comment from cursor bot Aug 12, 2025
@kzndotsh kzndotsh requested a review from SourceryAI August 14, 2025 12:35
sourcery-ai[bot]

This comment was marked as resolved.

SourceryAI

This comment was marked as resolved.

@allthingslinux allthingslinux deleted a comment from cursor bot Aug 15, 2025
cursoragent and others added 14 commits August 18, 2025 22:09
Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
… modules

Co-authored-by: admin <admin@kaizen.wtf>
…gging

Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
…models

Co-authored-by: admin <admin@kaizen.wtf>
Co-authored-by: admin <admin@kaizen.wtf>
kzndotsh and others added 30 commits September 20, 2025 05:29
…tion

- Introduced new fixtures for database management, including `pglite_async_manager`, `db_service`, and controllers for guild configurations.
- Added Sentry-related mock fixtures to facilitate testing of Discord interactions and error handling.
- Updated the test fixtures package to import all new fixtures for easier access during testing.
- Removed unnecessary cleanup functionality from PGlite fixtures due to upstream library improvements.
- Replaced instances of specific database service fixtures with a unified `db_service` parameter across multiple integration test files for consistency.
- Updated test methods to utilize the new parameter, enhancing readability and maintainability of the test code.
- Introduced a new integration test file for database error handling, covering various scenarios including connection errors and transaction rollbacks.
- Improved error handling tests to ensure robust coverage of database interactions and Sentry integration.
- Introduced comprehensive unit tests for the ErrorHandler and ErrorHandlerMixin classes, covering various error scenarios and ensuring proper logging and context setting.
- Added tests for Sentry performance tracking, including command start and end tracking, to validate successful and failed command executions.
- Implemented tests for Sentry service functions, ensuring robust error capturing and context management for Discord interactions.
- Enhanced overall test coverage for error handling mechanisms, improving reliability and maintainability of the codebase.
- Introduced a new test file for end-to-end integration tests focused on the error handling flow within the Discord bot.
- Implemented tests to verify user responses for command errors and app command errors, ensuring proper error handling and messaging.
- Enhanced test coverage for the ErrorHandler class, validating its functionality in various error scenarios.
- Reduced the complexity of the conftest.py file by importing all fixtures from the fixtures directory.
- Removed extensive cleanup and monitoring functions for PGlite processes, streamlining the test setup.
- Focused on a minimalistic approach to enhance maintainability and clarity in test configurations.
- Eliminated the `capture_database_error` calls from both `AsyncDatabaseService` and `SyncDatabaseService` connection methods to streamline error handling.
- Focused on improving clarity in error logging without external dependencies for database connection errors.
…modularization

- Simplified the `setup` method by extracting setup steps into a new `_run_setup_steps` method for better organization and clarity.
- Improved error handling for database connection and migration failures, providing clearer logging messages and reducing redundancy.
- Streamlined the setup process by maintaining tracing and status tagging throughout the execution of setup steps.
- Introduced a new Role Count plugin specifically designed for the All Things Linux Discord server, featuring hardcoded role IDs.
- Implemented functionality to display the number of users in various roles, categorized by distribution, language, desktop environment/window manager, editors, and vanity roles.
- Added command handling and embed creation for user-friendly interaction responses.
- Emphasized that this plugin is server-specific and should not be used on other Discord servers.
…reation

- Added try-except blocks to improve error handling in the `Config` and `Setup` classes, providing user-friendly feedback on failures.
- Updated the `CreateSnippet` class to handle database errors gracefully, ensuring robust error messaging for snippet creation.
- Improved error handling in the `EncodeDecode` and `Ping` classes to capture specific exceptions and log errors appropriately.
- Streamlined the `Poll` class to include error handling for poll ban checks, ensuring smoother user experience during poll creation.
- Updated the default value of the 'dirty' option in the DocsCLI class from True to False, ensuring that all files are rebuilt by default during the documentation serving process.
…n component styling

- Implemented smooth scrolling behavior across the site for improved navigation.
- Added custom scrollbar styles for a more modern look, including thin scrollbars and color adjustments.
- Updated header, footer, and button styles to align with the Tokyo Night theme.
- Enhanced typography and list formatting for better readability.
- Introduced new styles for hero sections, feature grids, and navigation components to improve overall layout and user experience.
- Introduced a new MkDocs plugin for Tux bot documentation that utilizes AST parsing to extract command information.
- Implemented configuration options for module paths and command enabling.
- Added functionality to process command blocks in markdown and generate documentation based on command definitions.
- Enhanced command documentation with details such as usage, parameters, and permission levels.
- Added a new accent color (sky) to the theme for improved visual appeal.
- Introduced the Tux plugin configuration to enable command documentation generation.
- Restructured the navigation to include detailed user, development, administration, and community documentation sections, enhancing accessibility and organization.
- Updated markdown extensions to include emoji support and tabbed content for better user experience.
Update the README to reflect changes in CLI tools and plugin
system. Replace `click` with `typer` for CLI and update the
extensions system to a plugin system. Provide detailed instructions
for using the new CLI commands with `uv`.

Add new documentation files for contributing, FAQ, support, database
patterns, error handling, Sentry integration, and an admin guide.
These documents provide comprehensive guidelines for development,
error tracking, and administration of the Tux bot.

The changes aim to improve developer experience by providing clear
guidelines and tools for contributing and maintaining the project.
The new documentation supports better onboarding, troubleshooting,
and system management, ensuring consistency and reliability in
development and production environments.

docs: add comprehensive developer and user guides

Introduce detailed developer and user guides to enhance
contributor and user experience. The developer guide provides
step-by-step instructions for setting up a development environment,
contributing to the project, and understanding the architecture.
The user guide offers instructions for server administrators and
members on how to install, configure, and use Tux.

These guides aim to streamline the onboarding process for new
contributors and users, ensuring they have all necessary information
to effectively engage with the project. The documentation also
includes API references and CLI tools to support advanced usage
and development workflows.
Introduce a new entry point for the MkDocs plugin, allowing the
integration of the TuxPlugin. This change facilitates the use of
custom documentation features provided by the TuxPlugin, enhancing
the documentation generation process.
…eveloper documentation

Refactor the markdown formatting across multiple documentation files to improve readability and consistency. Changes include removing unnecessary text formatting tags and ensuring proper code block syntax. This update enhances the overall presentation of the documentation, making it easier for contributors and users to follow the guidelines and instructions provided.
Changed the import path for the ErrorHandler from `tux.services.handlers.error.handler` to `tux.services.handlers.error.cog` in both end-to-end and unit tests. This update ensures consistency in the codebase and aligns with the recent refactoring of the error handling module.
…ation

Add multiple setup services for bot initialization, including BaseSetupService, BotSetupService, CogSetupService, DatabaseSetupService, PermissionSetupService, and BotSetupOrchestrator. These services standardize the setup process, handle error logging, and manage dependencies for cogs, database connections, and permissions, enhancing the overall bot setup experience.
…unloading

Implement a new method in the BaseCog class to unload the cog if a specified configuration is missing. This method logs a warning and triggers an asynchronous unload task, enhancing error handling and configuration management for cogs.
…hods

Refactor the Tux bot class to enhance the setup process by introducing a dedicated method for creating the setup task in the proper event loop context. Removed obsolete methods related to setup steps, including database connection handling and permission system initialization, which are now managed by the BotSetupOrchestrator. This change simplifies the bot's initialization logic and improves maintainability.
Refactor the cog eligibility verification process in the CogLoader class to include checks for the presence of a setup function in the module. This update improves the robustness of cog loading by ensuring only valid cogs are loaded. Additionally, enhance logging to provide clearer feedback on the loading process, including the number of cogs loaded from each folder and their respective load times.
…ed parameters

Refactor the logging configuration in the logging module to streamline the setup process. The `environment` parameter is now deprecated and kept for backward compatibility, while the logging level is simplified to use a single source. Additionally, the console format function has been updated to remove environment-specific logic, enhancing clarity and maintainability.
Refactor the `get_db_service_from` function to enhance the logic for retrieving the DatabaseService. The updated implementation first checks the bot's container for the service and falls back to directly accessing the db_service attribute if not found. This change improves the robustness of the service retrieval process and maintains clearer error handling.
Add the unload_if_missing_config method to various cogs, including Git, Levels, and StatusRoles, to ensure graceful unloading when required configurations are missing. This enhancement improves error handling and configuration management across the bot's modules.
…iscord commands

Add a new ErrorHandler cog that centralizes error handling for both prefix and slash commands in the Tux Discord bot. This implementation includes enhanced logging, Sentry integration for error reporting, and user-friendly error responses. Update import paths to reflect the new structure.
…substitution and improved activity rotation

Refactor the ActivityHandler to include a new method for placeholder substitution in activity names, allowing dynamic updates based on bot statistics. Update the activity list building logic to handle empty configurations gracefully and streamline the activity rotation process. This enhancement improves the bot's presence management and user engagement.
…ebase

This commit deletes the substitutions.py file, which contained methods for handling dynamic text substitutions based on bot statistics. The removal is part of a cleanup effort to streamline the codebase and eliminate unnecessary components.
This commit introduces a new hot reload cog for file watching and automatic reloading of extensions. The cog setup function is added to facilitate integration with the bot. Additionally, the reload logic in the HotReload service and CogWatcher class is improved to prevent attempts to reload when the event loop is closed, enhancing stability during shutdown.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants