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: b132c80
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
sourcery-ai[bot]

This comment was marked as resolved.

SourceryAI

This comment was marked as resolved.

Copy link
Contributor

github-actions bot commented Aug 23, 2025

Title Coverage Tests Skipped Failures Errors Time

@allthingslinux allthingslinux deleted a comment from github-actions bot Aug 23, 2025
@allthingslinux allthingslinux deleted a comment from github-actions bot Aug 23, 2025
@allthingslinux allthingslinux deleted a comment from github-actions bot Aug 23, 2025
- Changed the import statement for CaseType from tux.database.models.moderation to tux.database.models for improved clarity and organization.
- Ensured consistency in import paths across the codebase.
- Changed the import statement for Case from tux.database.models.moderation to tux.database.models for improved clarity and consistency.
- Enhanced the organization of import paths in the exceptions module.
- Changed the import statements for CaseType and Case from tux.database.models.moderation to tux.database.models for improved clarity and consistency across moderation modules.
- Enhanced organization of import paths in moderation-related files.
- Changed import statements for various models (AFK, Case, GuildConfig, Levels, Reminder, Snippet, Starboard, StarboardMessage) from specific submodules to a unified import from tux.database.models for improved clarity and consistency across the database controllers.
- Enhanced organization of import paths in the database controller files.
- Updated the migration scripts in env.py to improve handling of both synchronous and asynchronous database connections, ensuring compatibility with pytest-alembic.
- Refactored Alembic configuration setup in runner.py to include additional options for better migration management and logging.
- Removed the obsolete initial baseline migration file to streamline the migration history.
- Improved organization of import paths for clarity and consistency across migration files.
This commit modifies the alias for the `clearafk` command from `removeafk` to `unafk`, enhancing clarity and consistency in command naming. The change aims to improve user experience by providing a more intuitive command alias.
…se-driven decorators

This commit introduces a fully dynamic permission system that replaces the previous hardcoded permission levels with a database-driven approach. The `requires_command_permission` decorator is added to facilitate dynamic permission checks for commands, ensuring that permission requirements are configurable per guild. The legacy `ConditionChecker` and associated permission level enums are removed to streamline the codebase. Additionally, the permission system architecture is updated to utilize numeric ranks (0-100) instead of fixed levels, enhancing flexibility and scalability for server-specific configurations.
…ollers

This commit removes the GuildBlacklistController and GuildWhitelistController from the DatabaseCoordinator class, streamlining the code by eliminating unused properties and associated methods. This cleanup enhances code clarity and maintainability by focusing on currently utilized components.
…ture

This commit adds a new base model and mixins for enhanced serialization and timestamp management, along with enums for permission and case types. The existing models are updated to utilize these new structures, streamlining the codebase and improving maintainability. Additionally, the old complex permission model is removed in favor of a simpler dynamic permission system, aligning with recent architectural changes.
…cture

This commit modifies the imports in the migration environment file to include new permission-related models: GuildCommandPermission, GuildPermissionAssignment, and GuildPermissionRank. This change aligns with the recent updates to the permission system, ensuring that the database schema reflects the current architecture and improves maintainability.
This commit adds functionality to the EventHandler class to register all guilds the bot is in upon startup. It includes logging to track the registration process and handles potential errors if a guild is already registered. This enhancement improves the bot's initialization process and ensures that all guilds are accounted for in the database.
… permission system

This commit introduces a suite of integration and unit tests for the newly implemented dynamic permission system. It includes tests for permission rank management, command permission requirements, and error handling. Additionally, utility functions for error detail extraction are tested to ensure robustness. The tests enhance coverage and reliability of the permission system, ensuring it functions correctly across various scenarios.
…ermissionDeniedError

This commit modifies the existing permission error messages to use "rank" instead of "level" for clarity. Additionally, it introduces a new exception class, TuxPermissionDeniedError, which provides detailed messages when a user lacks the required permission rank to execute a command, enhancing error handling in the dynamic permission system.
…system

This commit adds a new Config cog that allows server administrators to manage guild configurations, including permission ranks, role assignments, command permissions, and log channels. The system features a structured command interface for easy configuration and provides an overview of the current settings. Additionally, it replaces the previous permission management module, streamlining the permission system into a more dynamic and user-friendly format.
…ion system changes

This commit revises the README.md to replace references to the `ConditionChecker` with the new `Permission System`, clarifying its role in dynamic permission validation. It updates method decorators from `@require_moderator()` to `@requires_command_permission()` across various moderation commands, ensuring consistency with the new permission architecture. Additionally, documentation issues regarding service count and naming have been resolved, enhancing clarity and accuracy.
…arity and organization

This commit significantly restructures the PostgreSQL configuration file, enhancing readability and maintainability. It replaces the previous Tux-specific comments with a more standardized format, providing detailed explanations for various settings. Key parameters such as connection settings, memory configuration, and logging options have been clearly documented, ensuring better understanding for future modifications. Additionally, unnecessary Tux-specific optimizations have been removed, streamlining the configuration for broader use.
…ion assignment tests

This commit enhances the integration tests for the guild permission assignment controller by adding assertions to ensure that the created permission ranks have valid IDs. These checks improve the reliability of the tests by confirming that ranks are correctly instantiated before being assigned to roles.
…ding methods

This commit renames the GuildPermissionController to GuildPermissionRankController to better reflect its purpose in managing permission ranks. Additionally, it introduces new onboarding-specific methods in the GuildConfigController for updating and retrieving onboarding status, enhancing the guild configuration management capabilities. The onboarding stage is also defined as an enum for better clarity and structure in the database models.
…uration

This commit introduces a comprehensive onboarding system for guilds, featuring an interactive setup wizard that guides users through essential configuration steps. The wizard includes functionality for initializing permission ranks, selecting log channels, and assigning roles to permission levels. Additionally, it provides a completion step that summarizes the setup status and ensures a smooth onboarding experience for new guilds. Various UI components and callback handlers have been implemented to facilitate user interactions throughout the setup process.
…rmission ranks

This commit introduces a new method, `remove_role_assignment`, to the PermissionSystem class, allowing for the removal of permission rank assignments from roles. The method clears the cache for the specified guild and logs the removal action, enhancing the management of role permissions within the system.
…configuration

This commit updates the PostgreSQL image from version 15-alpine to 17-alpine in the Docker Compose configuration. Additionally, it modifies the health check script to reference the updated configuration structure, ensuring that the bot token check aligns with the new environment variable naming convention.
…ontent directly

This commit refactors the CogLoader class to check for the presence of a setup function by reading the file content instead of importing the module. This change improves error handling and avoids potential import issues, enhancing the reliability of the cog loading process.
This commit refactors the logging configuration by removing the file logging setup from the `configure_logging` function. The `enable_file_logging` parameter and related file logging methods have been eliminated, simplifying the logging configuration process and focusing on console logging only.
…input

This commit refactors the `deepfry` method in the Deepfry class to require an image attachment as input, simplifying the validation process. The image URL extraction logic has been updated to directly use the provided attachment, enhancing clarity and reducing unnecessary checks for image sources. This change improves the overall reliability of the image processing functionality.
This commit adds the GuildOnboardingService to the EventHandler class, initializing onboarding for new guilds when they join. The onboarding process is triggered in the on_guild_join method, enhancing the setup experience for new communities. A TODO comment has also been added to define a data expiration policy for guilds in the future.
ADMINER_DEFAULT_USERNAME: ${POSTGRES_USER:-tuxuser}
ADMINER_DEFAULT_PASSWORD: ${POSTGRES_PASSWORD:-ChangeThisToAStrongPassword123!}
ADMINER_AUTO_LOGIN: "${ADMINER_AUTO_LOGIN:-true}"
ADMINER_PLUGINS: "backward-keys tables-filter dump-date dump-json dump-xml dump-zip edit-calendar enum-option foreign-system json-column pretty-json-column table-indexes-structure table-structure"
Copy link
Contributor

Choose a reason for hiding this comment

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

[yamllint] reported by reviewdog 🐶
[warning] line too long (202 > 185 characters) (line-length)

electron271 and others added 8 commits October 6, 2025 23:11
…CRUD and Query controllers

This commit enhances the CRUD and Query controllers by adding instance expunging after records are retrieved or updated. This change ensures that instances can be reused across different sessions, improving session management and resource handling. The UpsertController is also updated to expunge the merged instance, maintaining consistency across the database interaction methods.
This commit introduces a comprehensive configuration system for guild management, replacing the previous implementation. The new structure includes separate modules for management, overview, and an interactive setup wizard, enhancing organization and maintainability. Key features include commands for managing permission ranks, role assignments, and command permissions, along with an overview command to display the current configuration status. This refactor aims to improve the user experience and streamline configuration processes for guild administrators.
This commit deletes the onboarding service package, including its decorators, utility functions, and the main service class. The onboarding functionality has been streamlined, and the setup wizard has been updated to directly handle permission initialization and onboarding stages without relying on the removed service. This change simplifies the onboarding process and reduces code complexity.
…tics

This commit introduces a new command, `membercount`, to the MemberCount cog, which provides the total number of members, humans, and bots in the server. The command is designed to enhance user engagement by offering insights into the server's composition through an embedded message format.
…set onboarding stage

This commit modifies the on_guild_join method in the EventHandler class to remove the GuildOnboardingService and instead directly update the onboarding stage for new guilds. This change simplifies the onboarding process by focusing on basic guild data initialization, enhancing the setup experience for new communities.
This commit introduces a new priority level for the "config" cog in the COG_PRIORITIES dictionary, setting its loading priority to 85. This change enhances the organization of cog loading by clearly defining the order of initialization for different components.
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.

3 participants