Skip to content

Conversation

@Nachiket-Roy
Copy link
Contributor

@Nachiket-Roy Nachiket-Roy commented Dec 11, 2025

Related to : #5251
This PR implements image optimization strategy by:
Converted high-impact .png images to .webp format:
dummy-user.png
bacon.png, googlelogo_color_272x92dp.png, slack_icon.png
All images in img/features/

Updating corresponding template references from .png to .webp
Keeping original .png files untouched for now (to avoid breaking links elsewhere)
Including silk package setup
Screenshot 2025-12-11 013546

Summary by CodeRabbit

  • New Features

    • Many site images and avatars switched from PNG/JPG to WebP for faster loading.
    • Added a script to batch-convert selected PNGs to WebP.
  • Bug Fixes

    • Fixed a profile UI label typo and corrected template conditionals for PR state styling.
  • Chores

    • Enabled additional developer profiling/debugging tools in development.
    • Made template loading and static asset storage environment-aware and improved static file compression/storage.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Contributor

👋 Hi @Nachiket-Roy!

This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:

  • The PR author
  • DonnieBLT
  • coderabbitai
  • copilot

Once a valid peer review is submitted, this check will pass automatically. Thank you!

@github-actions github-actions bot added files-changed: 67 PR changes 67 files needs-peer-review PR needs peer review labels Dec 11, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds django-silk and integrates Silk middleware/URL conditioned on DEBUG and not TESTING, adjusts debug tooling in settings, makes TEMPLATES and staticfiles storage environment-driven, adds a PNG→WebP conversion script, updates many templates to reference .webp assets and fixes a small template typo/conditional.

Changes

Cohort / File(s) Summary
Silk profiler & debug middleware
blt/settings.py, blt/urls.py, pyproject.toml
Adds django-silk to dev deps; when DEBUG and not TESTING adds "silk", "debug_toolbar", and "livereload" to INSTALLED_APPS; prepends silk.middleware.SilkyMiddleware to MIDDLEWARE, appends LiveReload and Debug Toolbar middlewares; sets INTERNAL_IPS = ["127.0.0.1"]; registers Silk URLs under the same DEBUG and not TESTING guard.
Templates & staticfiles storage (env‑driven)
blt/settings.py
Splits TEMPLATES into DEBUG vs non-DEBUG branches (DEBUG: APP_DIRS=True, debug=True; non-DEBUG: APP_DIRS=False with cached loader and debug=False); replaces ManifestStaticFilesStorage with whitenoise.storage.CompressedManifestStaticFilesStorage for static files in both branches.
Image conversion script
scripts/convert_selected_to_webp.py
New script exposing TARGET_FILES, convert_to_webp(png_path_str), and main() to batch-convert PNGs to WebP using Pillow (RGBA, quality=85, method=6), optional directory expansion, logging, and CLI entrypoint.
Template asset updates & minor template fixes
website/templates/...
website/templates/bacon.html, website/templates/comments2.html, website/templates/domain.html, website/templates/features.html, website/templates/home.html, website/templates/search.html, website/templates/users.html, website/templates/profile.html, website/templates/includes/*, website/templates/organization/dashboard/*, website/templates/issue*.html
Replaces many PNG/JPG asset references with .webp (notably dummy-user.pngdummy-user.webp) across templates; fixes "Follwings" → "Following" and removes stray = in PR state class conditionals. No other logic changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay extra attention to:
    • blt/settings.py: DEBUG vs TESTING guards, middleware ordering (Silky must be prepended), TEMPLATES branching and cached loader config, and staticfiles storage change.
    • blt/urls.py: Silk URL inclusion condition and namespace.
    • pyproject.toml: django-silk addition/version.
    • scripts/convert_selected_to_webp.py: path resolution, Pillow conversion params, and error logging.
    • Template changes: verify .webp asset paths exist, the "Following" text fix, and corrected conditional class syntax.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary objective of the PR: converting high-impact images to WebP format and updating template references accordingly.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the pre-commit: passed Pre-commit checks passed label Dec 11, 2025
@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
Adding permission 'Permission object (406)'
Adding permission 'Permission object (407)'
Adding permission 'Permission object (408)'
Running post-migrate handlers for application authtoken
Adding content type 'authtoken | token'
Adding content type 'authtoken | tokenproxy'
Adding permission 'Permission object (409)'
Adding permission 'Permission object (410)'
Adding permission 'Permission object (411)'
Adding permission 'Permission object (412)'
Adding permission 'Permission object (413)'
Adding permission 'Permission object (414)'
Adding permission 'Permission object (415)'
Adding permission 'Permission object (416)'
Running post-migrate handlers for application tz_detect
Running post-migrate handlers for application star_ratings
Adding content type 'star_ratings | rating'
Adding content type 'star_ratings | userrating'
Adding permission 'Permission object (417)'
Adding permission 'Permission object (418)'
Adding permission 'Permission object (419)'
Adding permission 'Permission object (420)'
Adding permission 'Permission object (421)'
Adding permission 'Permission object (422)'
Adding permission 'Permission object (423)'
Adding permission 'Permission object (424)'
Running post-migrate handlers for application captcha
Adding content type 'captcha | captchastore'
Adding permission 'Permission object (425)'
Adding permission 'Permission object (426)'
Adding permission 'Permission object (427)'
Adding permission 'Permission object (428)'
Running post-migrate handlers for application dj_rest_auth
WARNING Slack environment not set. Slack integration disabled.
WARNING Slack environment not set. Slack integration disabled.
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
    super().run_from_argv(argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 63, in handle
    failures = test_runner.run_tests(test_labels)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1098, in run_tests
    self.run_checks(databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1020, in run_checks
    call_command("check", verbosity=self.verbosity, databases=databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 194, in call_command
    return command.execute(*args, **defaults)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/check.py", line 81, in handle
    self.check(
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 492, in check
    all_issues = checks.run_checks(
                 ^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/checks/registry.py", line 89, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 243, in check_middleware
    login_required_index = _subclass_index(
                           ^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 20, in _subclass_index
    candidate_cls = import_string(path)
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 30, in import_string
    return cached_import(module_path, class_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 15, in cached_import
    module = import_module(module_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/middleware.py", line 11, in <module>
    from silk import models
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/models.py", line 68, in <module>
    class Request(models.Model):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/base.py", line 136, in __new__
    raise RuntimeError(
RuntimeError: Model class silk.models.Request doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

For more information, see the Django testing documentation.

@github-actions github-actions bot added the tests: failed Django tests failed label Dec 11, 2025
@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
Adding permission 'Permission object (406)'
Adding permission 'Permission object (407)'
Adding permission 'Permission object (408)'
Running post-migrate handlers for application authtoken
Adding content type 'authtoken | token'
Adding content type 'authtoken | tokenproxy'
Adding permission 'Permission object (409)'
Adding permission 'Permission object (410)'
Adding permission 'Permission object (411)'
Adding permission 'Permission object (412)'
Adding permission 'Permission object (413)'
Adding permission 'Permission object (414)'
Adding permission 'Permission object (415)'
Adding permission 'Permission object (416)'
Running post-migrate handlers for application tz_detect
Running post-migrate handlers for application star_ratings
Adding content type 'star_ratings | rating'
Adding content type 'star_ratings | userrating'
Adding permission 'Permission object (417)'
Adding permission 'Permission object (418)'
Adding permission 'Permission object (419)'
Adding permission 'Permission object (420)'
Adding permission 'Permission object (421)'
Adding permission 'Permission object (422)'
Adding permission 'Permission object (423)'
Adding permission 'Permission object (424)'
Running post-migrate handlers for application captcha
Adding content type 'captcha | captchastore'
Adding permission 'Permission object (425)'
Adding permission 'Permission object (426)'
Adding permission 'Permission object (427)'
Adding permission 'Permission object (428)'
Running post-migrate handlers for application dj_rest_auth
WARNING Slack environment not set. Slack integration disabled.
WARNING Slack environment not set. Slack integration disabled.
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
    super().run_from_argv(argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 63, in handle
    failures = test_runner.run_tests(test_labels)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1098, in run_tests
    self.run_checks(databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1020, in run_checks
    call_command("check", verbosity=self.verbosity, databases=databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 194, in call_command
    return command.execute(*args, **defaults)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/check.py", line 81, in handle
    self.check(
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 492, in check
    all_issues = checks.run_checks(
                 ^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/checks/registry.py", line 89, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 243, in check_middleware
    login_required_index = _subclass_index(
                           ^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 20, in _subclass_index
    candidate_cls = import_string(path)
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 30, in import_string
    return cached_import(module_path, class_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 15, in cached_import
    module = import_module(module_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/middleware.py", line 11, in <module>
    from silk import models
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/models.py", line 68, in <module>
    class Request(models.Model):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/base.py", line 136, in __new__
    raise RuntimeError(
RuntimeError: Model class silk.models.Request doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

For more information, see the Django testing documentation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
website/templates/search.html (1)

490-496: Fix project logo references: organization is undefined in the projects loop

Inside the {% for project in projects %} section, both project and project.organization logos are rendered using organization.logo, which is not defined in this scope and will either fail or accidentally reuse a stale variable from a previous loop.

You likely want to reference the project’s own logo fields:

{% if project.logo %}
-    <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
-         src="{{ organization.logo.url }}"
-         alt="{{ organization.name }} Logo"
-         height="96"
-         width="96">
+    <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
+         src="{{ project.logo.url }}"
+         alt="{{ project.name }} Logo"
+         height="96"
+         width="96">
{% else %}
    ...
{% endif %}
...
{% if project.organization %}
    {% if project.organization.logo %}
-        <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
-             src="{{ organization.logo.url }}"
-             alt="{{ organization.name }} Logo"
-             height="96"
-             width="96">
+        <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
+             src="{{ project.organization.logo.url }}"
+             alt="{{ project.organization.name }} Logo"
+             height="96"
+             width="96">
    {% endif %}
    <h3 ...>
        Organization:
        <span class="text-blue-600">{{ project.organization.name }}</span>
    </h3>
{% endif %}

This will ensure the correct images render and avoid depending on an out-of-scope variable.

Also applies to: 539-545

blt/settings.py (2)

21-23: Avoid logging DATABASE_URL and other secrets

Printing the .env path is fine, but logging DATABASE_URL risks exposing credentials in logs; this is particularly problematic in shared or production environments. Consider removing the DATABASE_URL print or guarding it behind a local-only debug flag that never runs in deployed environments.


113-136: Move Silk middleware into the DEBUG block to match INSTALLED_APPS configuration

Silk middleware is currently in MIDDLEWARE unconditionally (line 114), but the silk app is only added to INSTALLED_APPS when DEBUG=True (line 106). This mismatch causes the middleware to attempt importing a module that won't exist in production, risking import errors or unexpected behavior. Additionally, running the profiler in production creates both performance and security concerns.

Update the configuration to gate Silk middleware by DEBUG:

 MIDDLEWARE = [
-    "silk.middleware.SilkyMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     "blt.middleware.domain.DomainMiddleware",
@@ -126,7 +125,9 @@ MIDDLEWARE = [
     "blt.middleware.user_visit_tracking.VisitTrackingMiddleware",
 ]
 
 if DEBUG:
+    MIDDLEWARE = ["silk.middleware.SilkyMiddleware", *MIDDLEWARE]
     MIDDLEWARE += ("livereload.middleware.LiveReloadScript", "debug_toolbar.middleware.DebugToolbarMiddleware")
     INTERNAL_IPS = ["127.0.0.1"]

This ensures Silk runs first (per django-silk documentation) and only when DEBUG is enabled, matching the INSTALLED_APPS behavior.

🧹 Nitpick comments (8)
scripts/convert_selected_to_webp.py (1)

14-16: Verify the features directory exists before globbing.

If the features directory doesn't exist, glob("*.png") will silently return an empty list, which may not be the intended behavior.

Consider adding a check:

 # Add all PNGs from img/features/
 features_dir = Path("website/static/img/features")
 if features_dir.exists():
     TARGET_FILES += [str(p) for p in features_dir.glob("*.png")]
+else:
+    print(f"⚠️  Features directory not found: {features_dir}")
website/templates/comments2.html (1)

256-347: Comment create/update JS looks good; consider trimming input and cleaning up minor redundancy

The AJAX flows for posting and updating comments (loading states, CSRF header, error fallback) look solid and are a nice UX improvement.

Two small, optional tweaks:

  • Reject pure‑whitespace comments by trimming before the emptiness check.
  • In remove_update_label, the $('#update_comment_btn').off('click') after you’ve just renamed the id to post_comment_btn is effectively a no‑op and can be dropped.

For example:

-    let comment = document.getElementById("comment").value;
-    if (comment == "") {
+    let comment = document.getElementById("comment").value.trim();
+    if (!comment) {
         return;
     }

You can mirror the same in update_comment. The rest of the logic can remain as is.

Also applies to: 350-385

website/templates/includes/issue_right_info.html (1)

5-26: Avatar fallback switched to WebP and still preserves gravatar / custom avatar chain

The long conditional src chain correctly keeps the existing order (user avatar → gravatar → anonymous dummy-user.webp), just updating the final fallback to WebP. The rest of the block is unchanged logically.

If you touch this again later, consider breaking the img tag over multiple lines with an {% if %} block for readability, but it’s fine functionally as is.

website/templates/search.html (1)

820-871: Start‑chat fetch handler and CSRF helper look fine

The DOMContentLoaded handler that wires .start-chat buttons to fetch("/messaging/start-thread/...") with a JSON body and CSRF header from getCookie("csrftoken") is sound. Error handling via catch and a simple alert is acceptable for this context.

Optional for later: consider checking response.ok before response.json() to handle non‑JSON error responses more gracefully.

website/templates/issue.html (2)

200-227: deleteIssue fetch flow is correct; consider aligning CSRF handling with the rest of the app

The delete handler correctly:

  • Confirms with the user before proceeding.
  • Sends a POST to /delete_issue/<id>/ with X-CSRFToken and JSON content type.
  • Redirects on success and shows a notification on failure.

If you later centralize CSRF handling (e.g., via a shared getCookie("csrftoken") helper like in search.html), this would be a good place to reuse it instead of inlining {{ csrf_token }} in JS.


255-295: Markdown rendering + issue‑linkification works, but consider guarding DOMPurify and scoping the #123 regex

The DOMContentLoaded handler that:

  1. Uses markdown-it to render object.markdown_description,
  2. Rewrites #123 patterns into /issue/123 links, and
  3. Sanitizes via DOMPurify.sanitize with a tight allowlist

is a good pattern for safe rich text.

Two follow‑ups you might consider:

  • Guard DOMPurify: if the CDN script fails to load, DOMPurify will be undefined and this block will throw. A simple if (window.DOMPurify) (or try/catch) would make this more robust.
  • Regex scope: the /#(\d+)/g replacement runs on the full rendered HTML string, so any #123 inside attributes or code blocks is also rewritten. That may be acceptable, but if you want cleaner behavior you could instead walk text nodes and apply the regex only to text content.

Functionally this is fine today; these are just hardening suggestions.

blt/urls.py (1)

1245-1268: Silk URLs are correctly gated behind DEBUG; consider folding into the existing block

Adding:

if settings.DEBUG:
    urlpatterns += [
        path("silk/", include("silk.urls", namespace="silk")),
    ]

ensures the Silk UI is only exposed when DEBUG is true, which is exactly what you want for a profiler.

For tidiness, you could merge this into the existing if settings.DEBUG: block a few lines above (where you add __debug__/ and the debug API endpoints), but that’s purely stylistic.

blt/settings.py (1)

164-211: TEMPLATES DEBUG/prod split is good; consider basing it on the final DEBUG value

The separate DEBUG vs non-DEBUG TEMPLATES configs (simple vs cached loaders) are reasonable, but they’re evaluated before the "DYNO" block later that forces DEBUG = False on Heroku. If someone misconfigures DEBUG via env in a dyno environment, you could end up with DEBUG=False but still using the “debug” templates configuration.

Consider either:

  • Moving the entire if DEBUG: ... else: ... TEMPLATES block below the "DYNO" logic so it uses the final DEBUG value, or
  • Introducing a separate flag (e.g., DEBUG_TEMPLATES) derived from env and using that consistently for both DEBUG and template configuration.

Also, double-check that all previously-configured context processors and options are preserved in both branches so behavior doesn’t diverge unexpectedly across environments.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between d4f60bb and 36a556d.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • blt/settings.py (5 hunks)
  • blt/urls.py (1 hunks)
  • pyproject.toml (1 hunks)
  • scripts/convert_selected_to_webp.py (1 hunks)
  • website/templates/bacon.html (1 hunks)
  • website/templates/comments2.html (14 hunks)
  • website/templates/domain.html (8 hunks)
  • website/templates/features.html (13 hunks)
  • website/templates/home.html (26 hunks)
  • website/templates/includes/_activity.html (1 hunks)
  • website/templates/includes/_bug.html (1 hunks)
  • website/templates/includes/_new_activity.html (2 hunks)
  • website/templates/includes/issue_right_info.html (10 hunks)
  • website/templates/issue.html (5 hunks)
  • website/templates/organization/dashboard/add_slack_integration.html (1 hunks)
  • website/templates/organization/dashboard/organization_integrations.html (2 hunks)
  • website/templates/organization/dashboard/organization_team_overview.html (7 hunks)
  • website/templates/search.html (24 hunks)
  • website/templates/users.html (4 hunks)
🔇 Additional comments (28)
website/templates/organization/dashboard/add_slack_integration.html (1)

41-45: Verify WebP asset availability and browser compatibility.

The change references slack_icon.webp, but I need to confirm:

  1. The WebP file exists in website/static/images/ directory
  2. WebP browser support aligns with your target audience (older browsers may lack native support)

Consider adding a fallback mechanism (e.g., <picture> element with a PNG fallback) if broad browser compatibility is required.

website/templates/organization/dashboard/organization_integrations.html (2)

45-49: Verify WebP asset availability (Active Integrations section).

Confirm that slack_icon.webp exists in website/static/images/ and that WebP format is compatible with your target browsers. Consider a <picture> element with PNG fallback if needed for older browser support.


143-147: Verify WebP asset availability (Available Integrations section).

Confirm that slack_icon.webp exists in website/static/images/ and that WebP format is compatible with your target browsers. Consider a <picture> element with PNG fallback if needed for older browser support.

website/templates/includes/_bug.html (1)

32-32: LGTM! Default avatar fallback updated to WebP.

The fallback avatar image has been updated to use the WebP format, maintaining consistency with the project-wide asset migration.

website/templates/includes/_activity.html (1)

32-32: LGTM! Default avatar fallback updated to WebP.

Consistent with the WebP migration across avatar fallback images.

website/templates/users.html (2)

8-9: Cosmetic text reflow.

These line breaks improve readability without changing the rendered content.

Also applies to: 18-19


73-73: LGTM! Avatar fallbacks updated to WebP across all contexts.

All avatar fallback references (including onerror attributes) have been consistently updated to use WebP format.

Also applies to: 102-102, 110-110, 113-113

website/templates/includes/_new_activity.html (3)

57-57: LGTM! Avatar fallback updated to WebP.


68-84: Verify text reflow and content reorganization.

The username, domain, timestamp, URL, and comments have been restructured with different line breaks and positioning. While this appears to be intentional formatting, ensure the rendered layout displays correctly.


87-94: Verify button duplication is intentional.

A new wrapper div has been added with two buttons for status and label display. Confirm this restructuring is intentional and doesn't duplicate existing buttons elsewhere in the template.

website/templates/home.html (7)

7-9: Cosmetic text reflow in meta blocks.

Line breaks improve readability without changing rendered content.

Also applies to: 15-17


147-151: Cosmetic text reflow in content paragraphs.

Paragraph text has been reflowed across lines for better source readability without affecting the rendered output.

Also applies to: 163-167, 746-750


459-459: LGTM! Avatar fallbacks updated to WebP in leaderboard section.

Both the main avatar image and fallback in the onerror attribute now reference the WebP format.

Also applies to: 461-461


1273-1275: Cosmetic text reflow in Google supporter description.

Text reflowed for readability without changing content.


262-266: Template variable formatting changes.

Template variables and expressions have been reformatted across multiple lines for consistency. These are cosmetic changes that don't affect functionality.

Also applies to: 272-276, 282-286, 292-296, 301-303, 377-379, 591-593, 621-623, 653-655, 684-686, 788-790, 814-816, 853-855, 861-863, 906-910, 918-920, 947-949, 958-960, 970-972


1337-1345: JavaScript formatting cleanup.

Minor formatting improvements including removing an empty line and adjusting function spacing.


1266-1266: Google logo updated to WebP format.

The Google logo reference has been updated from PNG to WebP as part of the high-impact image optimization. The WebP file exists at the expected location.

website/templates/domain.html (3)

14-16: Cosmetic text and template variable reflow.

These formatting changes improve source code readability without affecting rendered output.

Also applies to: 76-77, 99-100, 206-208, 222-224, 255-257, 264-266, 269-271, 274-276, 365-367


354-354: LGTM! Avatar fallbacks updated to WebP with proper error handling.

Both avatar sources properly fall back to the WebP format in their onerror attributes.

Also applies to: 361-361


388-427: JavaScript formatting cleanup.

The chart initialization code has been reformatted with consistent indentation and spacing. No functional changes.

website/templates/bacon.html (1)

34-34: LGTM! Image format updated to WebP.

The bacon image reference has been successfully updated to use the WebP format, which provides better compression and smaller file sizes. The bacon.webp file exists at the expected location, and there are no broken references to the old image format in the active codebase.

website/templates/comments2.html (1)

190-223: Dropdown initialization and teardown are robust

The initDropDown implementation (closing other menus first and closing on outside click) plus wiring it from $(document).ready is clean and avoids multiple listener stacking.

No changes needed here.

Also applies to: 452-455

website/templates/organization/dashboard/organization_team_overview.html (1)

78-99: WebP avatar fallbacks and formatting changes look correct

Both organization members and daily report rows now fall back to images/dummy-user.webp and the surrounding username rendering is unchanged aside from line wrapping. No functional issues spotted.

Also applies to: 260-282

website/templates/includes/issue_right_info.html (1)

110-224: Multi-line {{ ... }} wrapping is safe

All the places where you’ve rewrapped Django template expressions across lines (e.g., object.created, object.get_label_display, object.cve_id, github_issues_url, email_clicks, email_events) remain valid and won’t change the rendered values.

No action needed.

website/templates/features.html (1)

37-1102: WebP asset migration in Features page looks consistent

The Features grid now consistently uses .webp under img/features/ (and images/bacon.webp), matching the stated optimization goal. All surrounding template tags and links are untouched aside from line wrapping.

Assuming the corresponding .webp files exist in static storage, this should be a safe, purely-presentational change.

Would you mind double‑checking (e.g., via collectstatic or a quick run in DEBUG) that every referenced img/features/*.webp and images/bacon.webp actually exists and serves correctly?

website/templates/issue.html (1)

117-133: Reporter avatar fallback updated to WebP and remains functionally correct

The reporter avatar still prefers a user profile avatar, then gravatar, and now falls back to images/dummy-user.webp. The logic is unchanged aside from the image format and aligns with other templates in this PR.

No changes needed.

blt/settings.py (2)

105-107: Debug-only INSTALLED_APPS additions look appropriate

Appending silk, debug_toolbar, and livereload only when DEBUG is true keeps production lean and avoids unnecessary debug dependencies in normal runs; no change needed here.


316-318: WhiteNoise CompressedManifestStaticFilesStorage switch is consistent across environments

Using whitenoise.storage.CompressedManifestStaticFilesStorage for the staticfiles storage in both the GCS/Heroku and local branches lines up with whitenoise.middleware.WhiteNoiseMiddleware and ensures compressed, hashed static assets everywhere; this looks correct.

Also applies to: 331-333

@github-project-automation github-project-automation bot moved this from Backlog to Ready in 📌 OWASP BLT Project Board Dec 11, 2025
@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
blt/settings.py (2)

114-132: Duplicate SilkyMiddleware in MIDDLEWARE list.

silk.middleware.SilkyMiddleware appears at both line 115 and line 131. This will double-process requests, causing incorrect profiling data and potential performance degradation.

Keep only one instance—typically at the top for accurate request timing.

 MIDDLEWARE = [
     "silk.middleware.SilkyMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     # ... other middleware ...
     "blt.middleware.user_visit_tracking.VisitTrackingMiddleware",
-    "silk.middleware.SilkyMiddleware",
 ]

104-107: Duplicate "silk" in INSTALLED_APPS will crash the app in DEBUG mode.

"silk" is added unconditionally at line 104 and again conditionally at line 107 when DEBUG=True. Django's app registry raises ImproperlyConfigured for duplicate entries.

Remove "silk" from either the base tuple or the DEBUG block.

 INSTALLED_APPS = (
     # ... other apps ...
-    "silk",
 )
 if DEBUG:
-    INSTALLED_APPS += ("silk", "debug_toolbar", "livereload")
+    INSTALLED_APPS += ("debug_toolbar", "livereload", "silk")
🧹 Nitpick comments (3)
scripts/convert_selected_to_webp.py (1)

24-37: Good fix on error logging!

The previous review concern about silent failures has been properly addressed with clear logging at each stage.

Consider these optional enhancements for production robustness:

  1. Skip existing files: Check if the WebP already exists and is newer than the source PNG to avoid redundant conversions.
  2. Return status: Return a boolean or status enum so callers can track success/failure counts.
  3. Verify output: After saving, verify the output file exists and has non-zero size.

Example for skipping existing files:

 def convert_to_webp(png_path_str):
     png_path = Path(png_path_str)
     if not png_path.exists():
         logger.warning(f"Not found: {png_path}")
         return
 
     webp_path = png_path.with_suffix(".webp")
+    if webp_path.exists() and webp_path.stat().st_mtime > png_path.stat().st_mtime:
+        logger.info(f"Skipped (up-to-date): {webp_path}")
+        return
+    
     try:
         img = Image.open(png_path).convert("RGBA")
         img.save(webp_path, "webp", quality=85, method=6)
         logger.info(f"Converted: {png_path} → {webp_path}")
     except Exception as e:
         logger.error(f"Failed: {png_path} — {e}")
pyproject.toml (1)

73-73: Consider moving django-silk to dev dependencies.

Silk is only activated when DEBUG=True (per blt/settings.py), so it doesn't need to be installed in production. Moving it to [tool.poetry.group.dev.dependencies] alongside django-debug-toolbar and django-livereload-server would reduce the production dependency footprint.

-django-silk = "^5.4.0"

Add to dev dependencies section instead:

[tool.poetry.group.dev.dependencies]
# ... existing entries ...
django-silk = "^5.4.0"
blt/settings.py (1)

104-137: Make Silk integration consistently DEBUG-only.

Currently, "silk" is added unconditionally to INSTALLED_APPS and SilkyMiddleware is unconditionally in MIDDLEWARE, but Silk profiling is only useful in development. For consistency and to avoid production overhead:

  1. Move "silk" to the DEBUG block in INSTALLED_APPS
  2. Add SilkyMiddleware conditionally in the DEBUG middleware block
 INSTALLED_APPS = (
     # ... other apps ...
     "channels",
-    "silk",
 )
 if DEBUG:
-    INSTALLED_APPS += ("silk", "debug_toolbar", "livereload")
+    INSTALLED_APPS += ("debug_toolbar", "livereload", "silk")

 MIDDLEWARE = [
-    "silk.middleware.SilkyMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     # ... rest of middleware ...
     "blt.middleware.user_visit_tracking.VisitTrackingMiddleware",
-    "silk.middleware.SilkyMiddleware",
 ]

 if DEBUG:
-    MIDDLEWARE += ("livereload.middleware.LiveReloadScript", "debug_toolbar.middleware.DebugToolbarMiddleware")
+    MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE
+    MIDDLEWARE += ["livereload.middleware.LiveReloadScript", "debug_toolbar.middleware.DebugToolbarMiddleware"]
     INTERNAL_IPS = ["127.0.0.1"]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 36a556d and 25907b0.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • blt/settings.py (5 hunks)
  • pyproject.toml (1 hunks)
  • scripts/convert_selected_to_webp.py (1 hunks)
  • website/templates/comments2.html (14 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • website/templates/comments2.html
🔇 Additional comments (7)
scripts/convert_selected_to_webp.py (4)

1-8: LGTM! Clean imports and logging setup.

The logging configuration is appropriate for a conversion script, providing clear output without excessive verbosity.


10-21: LGTM! Pragmatic approach to target file selection.

The combination of explicit high-impact files plus automatic discovery in the features directory aligns well with the PR's optimization strategy.


39-41: LGTM! Clean and straightforward.

The main function effectively coordinates the batch conversion process.


44-45: LGTM! Standard Python entrypoint pattern.

Properly allows the script to be both executable and importable.

blt/settings.py (3)

134-137: LGTM!

The conditional addition of debug middleware and INTERNAL_IPS configuration is appropriate for DEBUG mode.


166-212: LGTM!

The conditional TEMPLATES configuration properly uses APP_DIRS=True for development flexibility and cached template loaders for production performance. Setting APP_DIRS=False when using custom loaders is required by Django.


317-335: LGTM!

Using whitenoise.storage.CompressedManifestStaticFilesStorage enables pre-compressed static files with cache-busting hashes—a recommended optimization for production deployments with WhiteNoise.

@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
DEBUG Creating response model for request model with pk 2666c2fc-20a7-402a-b8a8-61886120c0f9
WARNING Exhausted _process_response attempts; not processing request
ERROR

======================================================================
ERROR: test_recent_searches_in_context (website.tests.test_search.SearchViewTests.test_recent_searches_in_context)
Test that recent_searches is included in context for authenticated users
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 511, in parse
    compile_func = self.tags[command]
                   ~~~~~~~~~^^^^^^^^^
KeyError: 'endif'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/website/tests/test_search.py", line 139, in test_recent_searches_in_context
    response = client.get(reverse("search") + "?query=new&type=all")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1124, in get
    response = super().get(
               ^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 475, in get
    return self.generic(
           ^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 671, in generic
    return self.request(**r)
           ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1087, in request
    self.check_exception(response)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 802, in check_exception
    raise exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/BLT/BLT/website/views/core.py", line 877, in search
    return render(request, template, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/shortcuts.py", line 25, in render
    content = loader.render_to_string(template_name, context, request, using=using)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 61, in render_to_string
    template = get_template(template_name, using=using)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 15, in get_template
    return engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/backends/django.py", line 79, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 177, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 159, in find_template
    template = loader.get_template(name, skip=skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/cached.py", line 57, in get_template
    template = super().get_template(template_name, skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/base.py", line 28, in get_template
    return Template(
           ^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 155, in __init__
    self.nodelist = self.compile_nodelist()
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 197, in compile_nodelist
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 299, in do_extends
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 234, in do_block
    nodelist = parser.parse(("endblock",))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 513, in parse
    self.invalid_block_tag(token, command, parse_until)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 568, in invalid_block_tag
    raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 819: 'endif', expected 'endblock'. Did you forget to register or load this tag?

----------------------------------------------------------------------
Ran 224 tests in 136.671s

FAILED (errors=1)
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

For more information, see the Django testing documentation.

@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
DEBUG Creating response model for request model with pk 67d4d14e-164c-4272-94b7-c2c42097d395
WARNING Exhausted _process_response attempts; not processing request
ERROR

======================================================================
ERROR: test_recent_searches_in_context (website.tests.test_search.SearchViewTests.test_recent_searches_in_context)
Test that recent_searches is included in context for authenticated users
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 511, in parse
    compile_func = self.tags[command]
                   ~~~~~~~~~^^^^^^^^^
KeyError: 'endif'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/website/tests/test_search.py", line 139, in test_recent_searches_in_context
    response = client.get(reverse("search") + "?query=new&type=all")
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1124, in get
    response = super().get(
               ^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 475, in get
    return self.generic(
           ^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 671, in generic
    return self.request(**r)
           ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1087, in request
    self.check_exception(response)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 802, in check_exception
    raise exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/BLT/BLT/website/views/core.py", line 877, in search
    return render(request, template, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/shortcuts.py", line 25, in render
    content = loader.render_to_string(template_name, context, request, using=using)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 61, in render_to_string
    template = get_template(template_name, using=using)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 15, in get_template
    return engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/backends/django.py", line 79, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 177, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 159, in find_template
    template = loader.get_template(name, skip=skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/cached.py", line 57, in get_template
    template = super().get_template(template_name, skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/base.py", line 28, in get_template
    return Template(
           ^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 155, in __init__
    self.nodelist = self.compile_nodelist()
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 197, in compile_nodelist
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 299, in do_extends
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 234, in do_block
    nodelist = parser.parse(("endblock",))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 513, in parse
    self.invalid_block_tag(token, command, parse_until)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 568, in invalid_block_tag
    raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 819: 'endif', expected 'endblock'. Did you forget to register or load this tag?

----------------------------------------------------------------------
Ran 224 tests in 137.505s

FAILED (errors=1)
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

For more information, see the Django testing documentation.

@github-actions github-actions bot removed the changes-requested PR has requested changes from a reviewer label Dec 11, 2025
@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
Adding permission 'Permission object (406)'
Adding permission 'Permission object (407)'
Adding permission 'Permission object (408)'
Running post-migrate handlers for application authtoken
Adding content type 'authtoken | token'
Adding content type 'authtoken | tokenproxy'
Adding permission 'Permission object (409)'
Adding permission 'Permission object (410)'
Adding permission 'Permission object (411)'
Adding permission 'Permission object (412)'
Adding permission 'Permission object (413)'
Adding permission 'Permission object (414)'
Adding permission 'Permission object (415)'
Adding permission 'Permission object (416)'
Running post-migrate handlers for application tz_detect
Running post-migrate handlers for application star_ratings
Adding content type 'star_ratings | rating'
Adding content type 'star_ratings | userrating'
Adding permission 'Permission object (417)'
Adding permission 'Permission object (418)'
Adding permission 'Permission object (419)'
Adding permission 'Permission object (420)'
Adding permission 'Permission object (421)'
Adding permission 'Permission object (422)'
Adding permission 'Permission object (423)'
Adding permission 'Permission object (424)'
Running post-migrate handlers for application captcha
Adding content type 'captcha | captchastore'
Adding permission 'Permission object (425)'
Adding permission 'Permission object (426)'
Adding permission 'Permission object (427)'
Adding permission 'Permission object (428)'
Running post-migrate handlers for application dj_rest_auth
WARNING Slack environment not set. Slack integration disabled.
WARNING Slack environment not set. Slack integration disabled.
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
    super().run_from_argv(argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 63, in handle
    failures = test_runner.run_tests(test_labels)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1098, in run_tests
    self.run_checks(databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1020, in run_checks
    call_command("check", verbosity=self.verbosity, databases=databases)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 194, in call_command
    return command.execute(*args, **defaults)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/check.py", line 81, in handle
    self.check(
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 492, in check
    all_issues = checks.run_checks(
                 ^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/checks/registry.py", line 89, in run_checks
    new_errors = check(app_configs=app_configs, databases=databases)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 243, in check_middleware
    login_required_index = _subclass_index(
                           ^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/contrib/auth/checks.py", line 20, in _subclass_index
    candidate_cls = import_string(path)
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 30, in import_string
    return cached_import(module_path, class_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/utils/module_loading.py", line 15, in cached_import
    module = import_module(module_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/middleware.py", line 11, in <module>
    from silk import models
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/models.py", line 68, in <module>
    class Request(models.Model):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/base.py", line 136, in __new__
    raise RuntimeError(
RuntimeError: Model class silk.models.Request doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

For more information, see the Django testing documentation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
blt/settings.py (1)

129-136: Silky middleware is duplicated in DEBUG and enabled even when silk is not installed

Two related issues here:

  1. silk.middleware.SilkyMiddleware is in the base MIDDLEWARE list (Line 129) and also prepended in the if DEBUG: block (Line 134), so with DEBUG=True the middleware runs twice on every request.
  2. Outside DEBUG, INSTALLED_APPS does not include "silk", but MIDDLEWARE still contains silk.middleware.SilkyMiddleware, which contradicts the django-silk installation guidance that requires adding both 'silk' to INSTALLED_APPS and SilkyMiddleware to MIDDLEWARE together.(silk.readthedocs.io)

This can cause startup or runtime issues and unnecessary overhead in non-debug environments.

A minimal fix is to remove the unconditional middleware entry and keep the debug-only injection:

 MIDDLEWARE = [
     "django.contrib.sessions.middleware.SessionMiddleware",
@@
-    "blt.middleware.user_visit_tracking.VisitTrackingMiddleware",
-    "silk.middleware.SilkyMiddleware",
+    "blt.middleware.user_visit_tracking.VisitTrackingMiddleware",
 ]
 
 if DEBUG:
-    MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE
+    MIDDLEWARE = ["silk.middleware.SilkyMiddleware"] + MIDDLEWARE
     MIDDLEWARE += ["livereload.middleware.LiveReloadScript", "debug_toolbar.middleware.DebugToolbarMiddleware"]
     INTERNAL_IPS = ["127.0.0.1"]

This ensures:

  • In DEBUG: Silk is both installed and its middleware is active (once, at the front of the chain).
  • In non-DEBUG: Neither the Silk app nor its middleware are active.
🧹 Nitpick comments (1)
blt/settings.py (1)

165-211: TEMPLATES now depend on DEBUG before the later Heroku (DYNO) override

TEMPLATES is selected based on DEBUG here, but the "DYNO" in os.environ block later in the file forcibly sets DEBUG = False for Heroku. That means if someone accidentally sets DEBUG=True via environment in Heroku, you’ll still end up with the debug template configuration (APP_DIRS=True, no cached loader) even though DEBUG is later forced to False.

This can leave you in a partially “debug-like” template mode in production.

Consider refactoring so that the final value of DEBUG (including the Heroku override) is computed before TEMPLATES and other DEBUG-dependent settings, e.g.:

  • Move the if "DYNO" in os.environ block above the TEMPLATES definition, or
  • Compute a single IS_DEBUG = (env DEBUG) and "DYNO" not in os.environ flag once, set DEBUG = IS_DEBUG, and base TEMPLATES on that.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 25907b0 and 520fa20.

📒 Files selected for processing (2)
  • blt/settings.py (5 hunks)
  • pyproject.toml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyproject.toml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (2)
blt/settings.py (2)

105-106: Debug-only INSTALLED_APPS additions look good

Conditionally adding debug_toolbar, livereload, and silk when DEBUG is True keeps these tools out of production and matches common Django patterns; no issues here from a settings perspective. Please just ensure these packages are present in your dev dependencies so imports don’t fail at startup.


317-333: Switch to CompressedManifestStaticFilesStorage looks correct

Using whitenoise.storage.CompressedManifestStaticFilesStorage for "staticfiles" in both the Heroku and local STORAGES blocks aligns with current WhiteNoise recommendations for hashed + compressed static assets.(whitenoise.readthedocs.io)

Just confirm that:

  • Your WhiteNoise version supports CompressedManifestStaticFilesStorage at this import path (it has for multiple major versions), and
  • You don’t also define STATICFILES_STORAGE, which would conflict with STORAGES["staticfiles"] in recent Django versions.(learn.microsoft.com)

@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
DEBUG Processing request: POST /forum/add/ from IP: 127.0.0.1
DEBUG Skipping throttling for test mode
DEBUG Skipping throttling for POST /forum/add/ (exempt path or DRF view)
WARNING OpenAI client not available (missing or invalid API key), using default BACON score
ok
test_forum_post_with_repo_link (website.tests.test_core.ForumTests.test_forum_post_with_repo_link) ... INFO ThrottlingMiddleware initialized with limits: {'GET': 100, 'POST': 50, 'OTHER': 30}
DEBUG Using REMOTE_ADDR for IP: 127.0.0.1
DEBUG Processing request: POST /forum/add/ from IP: 127.0.0.1
DEBUG Skipping throttling for test mode
DEBUG Skipping throttling for POST /forum/add/ (exempt path or DRF view)
WARNING OpenAI client not available (missing or invalid API key), using default BACON score
ok
test_status_page_has_required_context (website.tests.test_core.StatusPageTests.test_status_page_has_required_context)
Test that status page provides expected context data ... INFO ThrottlingMiddleware initialized with limits: {'GET': 100, 'POST': 50, 'OTHER': 30}
DEBUG Using REMOTE_ADDR for IP: 127.0.0.1
DEBUG Processing request: GET /status_page/ from IP: 127.0.0.1
DEBUG Skipping throttling for test mode
DEBUG Skipping throttling for GET /status_page/ (exempt path or DRF view)
DEBUG Checking OpenAI API...
DEBUG Checking OpenAI API...
DEBUG Starting new HTTPS connection (1): api.openai.com:443
DEBUG https://api.openai.com:443 "GET /v1/models HTTP/1.1" 401 247
DEBUG Getting memory usage information...
DEBUG Getting memory usage information...
DEBUG Getting database connection count...
DEBUG Getting database connection count...
DEBUG Getting Redis stats...
DEBUG Getting Redis stats...
ok
test_status_page_loads (website.tests.test_core.StatusPageTests.test_status_page_loads)
Test that the status page loads without errors ... INFO ThrottlingMiddleware initialized with limits: {'GET': 100, 'POST': 50, 'OTHER': 30}
DEBUG Using REMOTE_ADDR for IP: 127.0.0.1
DEBUG Processing request: GET /status_page/ from IP: 127.0.0.1
DEBUG Skipping throttling for test mode
DEBUG Skipping throttling for GET /status_page/ (exempt path or DRF view)
ok
test_top_earners_queryset_fallback_to_winnings (website.tests.test_core.TopEarnersTests.test_top_earners_queryset_fallback_to_winnings)
Test that winnings field is used when no GitHub issues with payments exist ... ok
test_top_earners_queryset_from_github_issues (website.tests.test_core.TopEarnersTests.test_top_earners_queryset_from_github_issues)
Test that top earners queryset calculation uses GitHub issue payments when available ... ok
test_top_earners_queryset_mixed_sources (website.tests.test_core.TopEarnersTests.test_top_earners_queryset_mixed_sources)
Test that the calculation works correctly with mixed payment sources ... ok
test_all_endpoints_require_debug_mode (website.tests.test_debug_system_stats.DebugPanelAPITest.test_all_endpoints_require_debug_mode)
Test that all debug endpoints are blocked in production ... ok
test_clear_cache_forbidden_in_production (website.tests.test_debug_system_stats.DebugPanelAPITest.test_clear_cache_forbidden_in_production)
Test that cache clear route is not registered in production ... ok
test_clear_cache_handles_errors (website.tests.test_debug_system_stats.DebugPanelAPITest.test_clear_cache_handles_errors)
Test that cache clear endpoint handles errors gracefully ... ERROR

======================================================================
ERROR: test_clear_cache_handles_errors (website.tests.test_debug_system_stats.DebugPanelAPITest.test_clear_cache_handles_errors)
Test that cache clear endpoint handles errors gracefully
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/utils.py", line 456, in inner
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/mock.py", line 1369, in patched
    return func(*newargs, **newkeywargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/BLT/BLT/website/tests/test_debug_system_stats.py", line 119, in test_clear_cache_handles_errors
    self.reload_urls()
  File "/home/runner/work/BLT/BLT/website/tests/test_debug_system_stats.py", line 25, in reload_urls
    reload(blt.urls)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/importlib/__init__.py", line 169, in reload
    _bootstrap._exec(spec, module)
  File "<frozen importlib._bootstrap>", line 621, in _exec
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/work/BLT/BLT/blt/urls.py", line 1267, in <module>
    path("silk/", include("silk.urls", namespace="silk")),
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/urls/conf.py", line 39, in include
    urlconf_module = import_module(urlconf_module)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/urls.py", line 3, in <module>
    from silk.views.clear_db import ClearDBView
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/views/clear_db.py", line 11, in <module>
    from silk.models import Profile, Request, Response, SQLQuery
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/silk/models.py", line 68, in <module>
    class Request(models.Model):
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/db/models/base.py", line 136, in __new__
    raise RuntimeError(
RuntimeError: Model class silk.models.Request doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.

----------------------------------------------------------------------
Ran 91 tests in 58.872s

FAILED (errors=1)
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

For more information, see the Django testing documentation.

@Nachiket-Roy
Copy link
Contributor Author

@coderabbitai why the tests are failing?

@Nachiket-Roy
Copy link
Contributor Author

@coderabbitai but i already patched it to

if settings.DEBUG and not settings.TESTING:
    urlpatterns += [
        path("silk/", include("silk.urls", namespace="silk")),
    ]

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current state of blt/urls.py to verify the fix
echo "=== Check current silk URL pattern in blt/urls.py ==="
cat blt/urls.py | grep -A 3 -B 1 "silk/.*include"

echo -e "\n=== Get the latest test run for this PR ==="
gh pr checks 5263 --repo OWASP-BLT/BLT --json name,conclusion,completedAt | jq '.[] | select(.name == "Run Tests")'

echo -e "\n=== Get all recent workflow runs for this branch ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=5&branch=optimized-images-clean" --jq '.workflow_runs[] | {id: .id, name: .name, status: .status, conclusion: .conclusion, created_at: .created_at}'

echo -e "\n=== Check the latest commit on the PR branch ==="
gh pr view 5263 --repo OWASP-BLT/BLT --json headRefOid,statusCheckRollup --jq '{commit: .headRefOid, checks: [.statusCheckRollup[] | select(.name == "Run Tests") | {name: .name, conclusion: .conclusion}]}'

Length of output: 1270


🏁 Script executed:

#!/bin/bash
# Get more context around the silk URL inclusion in blt/urls.py
echo "=== Get more context around silk URL in blt/urls.py ==="
cat blt/urls.py | grep -A 5 -B 5 "silk/"

echo -e "\n=== Check line numbers to see the exact location ==="
grep -n "silk/" blt/urls.py

echo -e "\n=== Get the latest CI/CD test run for the branch ==="
RUN_ID=$(gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=10&branch=optimized-images-clean" --jq '.workflow_runs[] | select(.name == "CI/CD Optimized") | .id' | head -1)
echo "Latest CI/CD Run ID: $RUN_ID"

if [ ! -z "$RUN_ID" ]; then
  echo -e "\n=== Get test failure logs from latest run ==="
  gh run view $RUN_ID --repo OWASP-BLT/BLT --log | grep -E "(RuntimeError|FAILED|ERROR|silk)" -A 5 -B 5 | tail -100
fi

Length of output: 470


🏁 Script executed:

#!/bin/bash
# Get the commit history to see when the fix was applied
echo "=== Recent commits on the PR branch ==="
gh api "/repos/OWASP-BLT/BLT/commits?sha=optimized-images-clean&per_page=5" --jq '.[] | {sha: .sha[0:7], message: .commit.message | split("\n")[0], date: .commit.author.date}'

echo -e "\n=== Get all workflow runs after the latest commit ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=20&branch=optimized-images-clean&event=pull_request_target" --jq '.workflow_runs[] | {id: .id, name: .name, status: .status, conclusion: .conclusion, created_at: .created_at, head_sha: .head_sha[0:7]}'

echo -e "\n=== Check for any test runs matching the current commit ==="
gh api "/repos/OWASP-BLT/BLT/commits/dd951df520f4387ad184b3f66d63677a67e9e575/check-runs" --jq '.check_runs[] | select(.name == "Run Tests") | {name: .name, status: .status, conclusion: .conclusion, started_at: .started_at, completed_at: .completed_at, html_url: .html_url}'

Length of output: 3494


🏁 Script executed:

#!/bin/bash
# Get the logs from the latest test run to see what's failing now
echo "=== Get logs from the latest test run (after the fix) ==="
gh run view 20133069282 --repo OWASP-BLT/BLT --log | grep -E "(RuntimeError|FAILED|ERROR|silk)" -A 5 -B 5 | tail -150

echo -e "\n=== Get the test summary to see what failed ==="
gh run view 20133069282 --repo OWASP-BLT/BLT --log | grep -E "(test.*FAILED|Ran [0-9]+ test|exit_code|FAILED.*test)" | tail -30

echo -e "\n=== Check if there are any other errors ==="
gh run view 20133069282 --repo OWASP-BLT/BLT --log | tail -200 | grep -E "(Error|FAILED|Traceback)" -A 10 | head -100

Length of output: 19312


🏁 Script executed:

#!/bin/bash
# Check the search.html template around line 819 for the syntax error
echo "=== Check search.html around line 819 ==="
sed -n '810,830p' website/templates/search.html | nl -v 810

echo -e "\n=== Look for unmatched if/endif/block/endblock tags around that area ==="
grep -n "{% if\|{% endif\|{% block\|{% endblock" website/templates/search.html | tail -30

echo -e "\n=== Count if/endif and block/endblock pairs ==="
echo "Number of {% if %} tags: $(grep -o '{% if' website/templates/search.html | wc -l)"
echo "Number of {% endif %} tags: $(grep -o '{% endif' website/templates/search.html | wc -l)"
echo "Number of {% block %} tags: $(grep -o '{% block' website/templates/search.html | wc -l)"
echo "Number of {% endblock %} tags: $(grep -o '{% endblock' website/templates/search.html | wc -l)"

Length of output: 2266


🏁 Script executed:

#!/bin/bash
# Check the actual structure around line 819 more carefully
echo "=== Lines 805-825 of search.html ==="
sed -n '805,825p' website/templates/search.html | cat -n

echo -e "\n=== Lines 865-875 around endblock ==="
sed -n '865,875p' website/templates/search.html | cat -n

echo -e "\n=== Find where content block starts ==="
grep -n "{% block content" website/templates/search.html

echo -e "\n=== Check the overall structure - all blocks ==="
grep -n "{% block\|{% endblock" website/templates/search.html

Length of output: 1697


🏁 Script executed:

#!/bin/bash
# Let's trace back the if/endif structure to find the mismatch
echo "=== Show all if/endif pairs from line 600 onwards ==="
sed -n '600,820p' website/templates/search.html | grep -n "{% if\|{% endif" 

echo -e "\n=== Let's see the exact context around line 819 with more lines ==="
sed -n '800,825p' website/templates/search.html | cat -n

echo -e "\n=== Check what was changed in search.html in this PR ==="
gh pr view 5263 --repo OWASP-BLT/BLT --json files --jq '.files[] | select(.path == "website/templates/search.html") | {additions: .additions, deletions: .deletions}'

echo -e "\n=== Get the diff for search.html to see what changed ==="
gh pr diff 5263 --repo OWASP-BLT/BLT website/templates/search.html | head -200

Length of output: 4046


🏁 Script executed:

#!/bin/bash
# Get the diff properly
echo "=== Get the diff for search.html ==="
gh pr diff 5263 --repo OWASP-BLT/BLT -- website/templates/search.html | head -300

echo -e "\n=== Continue the diff ==="
gh pr diff 5263 --repo OWASP-BLT/BLT -- website/templates/search.html | tail -300

Length of output: 137


🏁 Script executed:

#!/bin/bash
# Try different approach to get the diff
echo "=== Get changes to search.html using API ==="
gh api "/repos/OWASP-BLT/BLT/pulls/5263/files" --jq '.[] | select(.filename == "website/templates/search.html") | {filename: .filename, patch: .patch}' | jq -r '.patch' | head -200

echo -e "\n=== Continue patch ==="
gh api "/repos/OWASP-BLT/BLT/pulls/5263/files" --jq '.[] | select(.filename == "website/templates/search.html") | {filename: .filename, patch: .patch}' | jq -r '.patch' | tail -200

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Let's manually check the template structure by looking at broader context
echo "=== Lines 1-50 to see the beginning ==="
sed -n '1,50p' website/templates/search.html | grep -n "block\|if"

echo -e "\n=== Lines 400-450 to find the outer if structure ==="
sed -n '400,450p' website/templates/search.html | grep -n "{% if\|{% endif"

echo -e "\n=== Look at what opens before the repos section ==="
sed -n '550,650p' website/templates/search.html | grep -n "{% if\|{% endif"

echo -e "\n=== Check if there's an if before the main search results section ==="
sed -n '100,200p' website/templates/search.html | grep -n "{% if\|{% endif"

echo -e "\n=== Let's look at the actual lines 23-30 where content block starts ==="
sed -n '23,80p' website/templates/search.html

Length of output: 6945


🏁 Script executed:

#!/bin/bash
# Let's trace the block/if structure more carefully
echo "=== Find the main outer structure ==="
grep -n "{% if query %}\|{% endif %}\|{% block content %}\|{% endblock content %}" website/templates/search.html

echo -e "\n=== Let's see the exact line 41 where if query starts ==="
sed -n '38,45p' website/templates/search.html

echo -e "\n=== And let's see what's on line 815-820 ==="
sed -n '815,822p' website/templates/search.html

echo -e "\n=== Let's check if there's nested structure issue - show lines 810-822 with context ==="
sed -n '810,822p' website/templates/search.html

echo -e "\n=== Check what comes after line 819 up to the script tag ==="
sed -n '819,830p' website/templates/search.html

Length of output: 5533


@Nachiket-Roy, you're absolutely correct! I can confirm the silk fix has been applied in blt/urls.py:

if settings.DEBUG and not settings.TESTING:
    urlpatterns += [
        path("silk/", include("silk.urls", namespace="silk")),
    ]

The silk issue is resolved!

However, the tests are still failing, but now for a completely different reason. The latest test run shows a new template syntax error in website/templates/search.html:

django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 819: 
'endif', expected 'endblock'. Did you forget to register or load this tag?

The New Issue

This error is in search.html, which was modified in your PR (109 additions, 69 deletions). The error occurs when the test tries to render the search template.

Looking at the template structure, line 808-809 has a multiline {% if %} tag:

{% if not issues and not domains and not users and not organizations and not projects and not tags and not
    repos %}

This appears to be malformed - the line break in the middle of the {% if ... %} tag is likely causing Django's template parser to fail. Django template tags should be complete on a single line, or properly continued.

How to Fix

Check website/templates/search.html around lines 808-815 and ensure all template tags are properly formatted. The {% if %} condition that spans multiple lines needs to be either:

  1. Put on a single line:
{% if not issues and not domains and not users and not organizations and not projects and not tags and not repos %}
  1. Or use proper Python line continuation (if Django template syntax supports it in your version)

The formatting changes you made to wrap long lines in the templates may have accidentally broken this template tag syntax.

@Nachiket-Roy
Copy link
Contributor Author

@coderabbitai check if removed some code by mistake which should not be a part of this pr?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the actual file content to compare and check what might have been removed
echo "=== Get the base version of search.html from main branch ==="
git fetch origin main:main 2>/dev/null || true
git show main:website/templates/search.html > /tmp/search_main.html 2>/dev/null || echo "Could not fetch main branch version"

echo -e "\n=== Get the current PR version ==="
cat website/templates/search.html > /tmp/search_pr.html

echo -e "\n=== Check line counts ==="
echo "Main branch line count: $(wc -l < /tmp/search_main.html 2>/dev/null || echo 'N/A')"
echo "PR branch line count: $(wc -l < /tmp/search_pr.html)"

echo -e "\n=== Show lines 805-825 from MAIN branch around the problematic area ==="
sed -n '805,825p' /tmp/search_main.html 2>/dev/null | cat -n

echo -e "\n=== Show lines 805-825 from PR branch ==="
sed -n '805,825p' /tmp/search_pr.html | cat -n

echo -e "\n=== Use git diff to show what changed in search.html ==="
git diff origin/main:website/templates/search.html website/templates/search.html | head -400

Length of output: 30153


🏁 Script executed:

#!/bin/bash
# Continue checking the diff
echo "=== Continue git diff for search.html ==="
git diff origin/main:website/templates/search.html website/templates/search.html | tail -400

echo -e "\n=== Check specifically around lines that might have template tag issues ==="
git diff origin/main:website/templates/search.html website/templates/search.html | grep -A 10 -B 10 "{% if not issues"

echo -e "\n=== Check for any removed {% endif %} or {% endblock %} tags ==="
git diff origin/main:website/templates/search.html website/templates/search.html | grep -E "^-.*{% endif|^-.*{% endblock"

Length of output: 28037


🏁 Script executed:

#!/bin/bash
# Check the template tag balance in both versions
echo "=== Count template tags in MAIN branch version ==="
if [ -f /tmp/search_main.html ]; then
    echo "{% if %} tags: $(grep -o '{% if' /tmp/search_main.html | wc -l)"
    echo "{% endif %} tags: $(grep -o '{% endif' /tmp/search_main.html | wc -l)"
    echo "{% block %} tags: $(grep -o '{% block' /tmp/search_main.html | wc -l)"
    echo "{% endblock %} tags: $(grep -o '{% endblock' /tmp/search_main.html | wc -l)"
else
    echo "Main branch file not available"
fi

echo -e "\n=== Count template tags in PR branch version ==="
echo "{% if %} tags: $(grep -o '{% if' /tmp/search_pr.html | wc -l)"
echo "{% endif %} tags: $(grep -o '{% endif' /tmp/search_pr.html | wc -l)"
echo "{% block %} tags: $(grep -o '{% block' /tmp/search_pr.html | wc -l)"
echo "{% endblock %} tags: $(grep -o '{% endblock' /tmp/search_pr.html | wc -l)"

echo -e "\n=== Check for malformed multi-line template tags in PR version ==="
grep -n "{% if.*$" website/templates/search.html | while read line; do
    linenum=$(echo "$line" | cut -d: -f1)
    nextlinenum=$((linenum + 1))
    nextline=$(sed -n "${nextlinenum}p" website/templates/search.html)
    if echo "$nextline" | grep -q "^[^{]*%}"; then
        echo "Potential multi-line tag at line $linenum:"
        sed -n "${linenum},$((linenum+1))p" website/templates/search.html
    fi
done

Length of output: 332


@Nachiket-Roy, good news: No code was accidentally removed! The template tag structure is perfectly balanced:

Template Tag Counts:

  • Main branch: 65 {% if %}, 65 {% endif %}, 6 {% block %}, 6 {% endblock %}
  • PR branch: 65 {% if %}, 65 {% endif %}, 6 {% block %}, 6 {% endblock %}

✅ All tags are balanced, nothing was deleted.

The Real Problem

However, I found what's causing the template syntax error. Your formatting changes broke Django template syntax rules. Throughout search.html, you split template variable expressions across multiple lines like this:

❌ Invalid (what you have now):

<span class="text-[#e74c3c] font-bold bg-red-50 px-3 py-1 rounded-full">{{
organizations|length }}</span>

❌ Another example:

<a href="{{ domain.get_absolute_url }}" class="hover:text-blue-600">{{
domain.name }}</a>

Django does NOT allow breaking {{ ... }} expressions across lines. The opening {{ and closing }} must be on the same line. This invalid syntax is confusing Django's template parser, causing it to report the error on line 819.

The Fix

You need to keep all {{ variable }} expressions on a single line. You can break the HTML across lines, but not the template tags themselves:

✅ Correct:

<span class="text-[#e74c3c] font-bold bg-red-50 px-3 py-1 rounded-full">{{ organizations|length }}</span>

Or if the line is too long, break the HTML tags instead:

<span class="text-[#e74c3c] font-bold bg-red-50 px-3 py-1 rounded-full">
    {{ organizations|length }}
</span>

The diff shows dozens of these broken template variables throughout the file. You'll need to fix all instances where {{ and }} are on different lines.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
website/templates/search.html (2)

483-488: Fix incorrect variable reference in project logo display.

Line 485 references organization.logo.url, but this block iterates over projects (line 480). The logo should reference the current project, not an undefined organization variable. This will cause a runtime error or display no logo.

Apply this diff:

                                {% if project.logo %}
                                    <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
-                                         src="{{ organization.logo.url }}"
-                                         alt="{{ organization.name }} Logo"
+                                         src="{{ project.logo.url }}"
+                                         alt="{{ project.name }} Logo"
                                         height="96"
                                         width="96">

528-535: Fix incorrect variable reference in project organization logo display.

Line 531 references organization.logo.url, but organization is not the loop variable. In this context, it should be project.organization.logo.url to correctly reference the project's associated organization logo.

Apply this diff:

                                {% if project.organization %}
                                    {% if project.organization.logo %}
                                        <img class="h-24 w-24 object-cover rounded-lg border border-indigo-200 mr-6"
-                                             src="{{ organization.logo.url }}"
-                                             alt="{{ organization.name }} Logo"
+                                             src="{{ project.organization.logo.url }}"
+                                             alt="{{ project.organization.name }} Logo"
                                             height="96"
                                             width="96">
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5ea9660 and 0096686.

📒 Files selected for processing (7)
  • website/templates/domain.html (8 hunks)
  • website/templates/home.html (17 hunks)
  • website/templates/includes/_new_activity.html (1 hunks)
  • website/templates/includes/issue_right_info.html (2 hunks)
  • website/templates/issue.html (4 hunks)
  • website/templates/organization/dashboard/organization_team_overview.html (7 hunks)
  • website/templates/search.html (16 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • website/templates/organization/dashboard/organization_team_overview.html
  • website/templates/includes/issue_right_info.html
  • website/templates/issue.html
  • website/templates/home.html
  • website/templates/domain.html
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (1)
website/templates/includes/_new_activity.html (1)

57-57: WebP fallback image correctly updated and verified.

The change properly updates the avatar fallback to use dummy-user.webp, which exists in website/static/images/ and is included in the PR. The Django template syntax is valid and aligns with the WebP migration objective.

@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 511, in parse
    compile_func = self.tags[command]
                   ~~~~~~~~~^^^^^^^^^
KeyError: 'endif'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/website/tests/test_user_profile.py", line 104, in test_update_email_address
    response = self.client.post(reverse("profile_edit"), data, follow=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1163, in post
    response = self._handle_redirects(
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1384, in _handle_redirects
    response = self._follow_redirect(
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1002, in _follow_redirect
    return request_method(
           ^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1124, in get
    response = super().get(
               ^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 475, in get
    return self.generic(
           ^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 671, in generic
    return self.request(**r)
           ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1087, in request
    self.check_exception(response)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 802, in check_exception
    raise exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 90, in rendered_content
    template = self.resolve_template(self.template_name)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 72, in resolve_template
    return select_template(template, using=self.using)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 42, in select_template
    return engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/backends/django.py", line 79, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 177, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 159, in find_template
    template = loader.get_template(name, skip=skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/cached.py", line 57, in get_template
    template = super().get_template(template_name, skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/base.py", line 28, in get_template
    return Template(
           ^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 155, in __init__
    self.nodelist = self.compile_nodelist()
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 197, in compile_nodelist
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 299, in do_extends
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 234, in do_block
    nodelist = parser.parse(("endblock",))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 513, in parse
    self.invalid_block_tag(token, command, parse_until)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 568, in invalid_block_tag
    raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 150: 'endif', expected 'endblock'. Did you forget to register or load this tag?

----------------------------------------------------------------------
Ran 258 tests in 139.121s

FAILED (errors=1)
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

For more information, see the Django testing documentation.

@github-actions github-actions bot added the changes-requested PR has requested changes from a reviewer label Dec 11, 2025
@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 511, in parse
    compile_func = self.tags[command]
                   ~~~~~~~~~^^^^^^^^^
KeyError: 'endif'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/website/tests/test_user_profile.py", line 104, in test_update_email_address
    response = self.client.post(reverse("profile_edit"), data, follow=True)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1163, in post
    response = self._handle_redirects(
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1384, in _handle_redirects
    response = self._follow_redirect(
               ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1002, in _follow_redirect
    return request_method(
           ^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1124, in get
    response = super().get(
               ^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 475, in get
    return self.generic(
           ^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 671, in generic
    return self.request(**r)
           ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 1087, in request
    self.check_exception(response)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/client.py", line 802, in check_exception
    raise exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 90, in rendered_content
    template = self.resolve_template(self.template_name)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/response.py", line 72, in resolve_template
    return select_template(template, using=self.using)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader.py", line 42, in select_template
    return engine.get_template(template_name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/backends/django.py", line 79, in get_template
    return Template(self.engine.get_template(template_name), self)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 177, in get_template
    template, origin = self.find_template(template_name)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/engine.py", line 159, in find_template
    template = loader.get_template(name, skip=skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/cached.py", line 57, in get_template
    template = super().get_template(template_name, skip)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loaders/base.py", line 28, in get_template
    return Template(
           ^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 155, in __init__
    self.nodelist = self.compile_nodelist()
                    ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 197, in compile_nodelist
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 299, in do_extends
    nodelist = parser.parse()
               ^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 519, in parse
    raise self.error(token, e)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 517, in parse
    compiled_result = compile_func(self, token)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/loader_tags.py", line 234, in do_block
    nodelist = parser.parse(("endblock",))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 513, in parse
    self.invalid_block_tag(token, command, parse_until)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/template/base.py", line 568, in invalid_block_tag
    raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 150: 'endif', expected 'endblock'. Did you forget to register or load this tag?

----------------------------------------------------------------------
Ran 258 tests in 144.013s

FAILED (errors=1)
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...

For more information, see the Django testing documentation.

@Nachiket-Roy
Copy link
Contributor Author

@coderabbitai why the tests are failing now?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the current status of test runs for this PR
echo "=== Get latest check runs for PR #5263 ==="
gh pr checks 5263 --repo OWASP-BLT/BLT --json name,conclusion,completedAt | jq '.[] | select(.name == "Run Tests")'

echo -e "\n=== Get the most recent workflow runs for this branch ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=10&branch=optimized-images-clean" --jq '.workflow_runs[] | select(.name == "CI/CD Optimized") | {id: .id, status: .status, conclusion: .conclusion, created_at: .created_at}' | head -3

echo -e "\n=== Get the latest run ID ==="
LATEST_RUN=$(gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=1&branch=optimized-images-clean" --jq '.workflow_runs[] | select(.name == "CI/CD Optimized") | .id' | head -1)
echo "Latest run ID: $LATEST_RUN"

if [ ! -z "$LATEST_RUN" ]; then
  echo -e "\n=== Get the test failure details from latest run ==="
  gh run view $LATEST_RUN --repo OWASP-BLT/BLT --log | grep -E "(FAILED|ERROR|Traceback|TemplateSyntaxError|RuntimeError)" -A 10 -B 5 | tail -200
fi

Length of output: 289


🏁 Script executed:

#!/bin/bash
# Try different approach to get check status
echo "=== Get latest check runs with available fields ==="
gh pr checks 5263 --repo OWASP-BLT/BLT --json name,state,completedAt

echo -e "\n=== Get workflow runs using API ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=5&branch=optimized-images-clean&event=pull_request_target" --jq '.workflow_runs[] | {id: .id, name: .name, status: .status, conclusion: .conclusion, created_at: .created_at}'

echo -e "\n=== Get check runs from commit ==="
gh api "/repos/OWASP-BLT/BLT/commits/$(gh pr view 5263 --repo OWASP-BLT/BLT --json headRefOid --jq -r '.headRefOid')/check-runs" --jq '.check_runs[] | select(.name == "Run Tests") | {name: .name, status: .status, conclusion: .conclusion, completed_at: .completed_at, html_url: .html_url}'

echo -e "\n=== Get the actual test run logs from the most recent one ==="
LATEST_RUN=$(gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=20&branch=optimized-images-clean" --jq '.workflow_runs[0].id')
echo "Latest workflow run ID: $LATEST_RUN"

if [ ! -z "$LATEST_RUN" ] && [ "$LATEST_RUN" != "null" ]; then
  echo -e "\n=== Get error details ==="
  gh run view $LATEST_RUN --repo OWASP-BLT/BLT --log | grep -E "(ERROR|FAILED|Traceback)" -A 15 | tail -150
fi

Length of output: 3168


🏁 Script executed:

#!/bin/bash
# Get the full test logs from the latest run
echo "=== Get logs from run 20135424623 ==="
gh run view 20135424623 --repo OWASP-BLT/BLT --log | tail -300

echo -e "\n=== Search specifically for test failures ==="
gh run view 20135424623 --repo OWASP-BLT/BLT --log | grep -E "(TemplateSyntaxError|Invalid block tag|test.*failed|FAILED)" -B 10 -A 10 | head -150

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Try to get the job details and logs
echo "=== Get jobs for the workflow run ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs/20135424623/jobs" --jq '.jobs[] | {id: .id, name: .name, status: .status, conclusion: .conclusion}'

echo -e "\n=== Find the Run Tests job specifically ==="
TEST_JOB_ID=$(gh api "/repos/OWASP-BLT/BLT/actions/runs/20135424623/jobs" --jq '.jobs[] | select(.name == "Run Tests") | .id')
echo "Test Job ID: $TEST_JOB_ID"

if [ ! -z "$TEST_JOB_ID" ]; then
  echo -e "\n=== Get logs from the Run Tests job ==="
  gh run view 20135424623 --repo OWASP-BLT/BLT --log --job $TEST_JOB_ID | tail -300
fi

echo -e "\n=== Also search for any errors in the logs ==="
gh run view 20135424623 --repo OWASP-BLT/BLT --log | grep -i "error\|fail" | tail -50

Length of output: 227


🏁 Script executed:

#!/bin/bash
# Get all recent workflow runs to find the test run
echo "=== Get all workflow runs for the branch ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=20&branch=optimized-images-clean" --jq '.workflow_runs[] | {id: .id, name: .name, workflow_name: .name, status: .status, conclusion: .conclusion, created_at: .created_at}' | head -20

echo -e "\n=== Look for CI/CD workflow specifically ==="
gh api "/repos/OWASP-BLT/BLT/actions/runs?per_page=20&branch=optimized-images-clean" --jq '.workflow_runs[] | select(.name == "CI/CD" or .name == "CI/CD Optimized" or (.name | contains("CI"))) | {id: .id, name: .name, status: .status, conclusion: .conclusion, created_at: .created_at}'

echo -e "\n=== Get the PR status to find the actual test run ==="
gh pr view 5263 --repo OWASP-BLT/BLT --json statusCheckRollup --jq '.statusCheckRollup[] | select(.name == "Run Tests") | {name: .name, status: .status, conclusion: .conclusion, workflowName: .workflowName}'

Length of output: 3919


🏁 Script executed:

#!/bin/bash
# Get logs from the failed CI/CD run
echo "=== Get logs from CI/CD run 20135369002 ==="
gh run view 20135369002 --repo OWASP-BLT/BLT --log | tail -500

echo -e "\n=== Search for specific errors ==="
gh run view 20135369002 --repo OWASP-BLT/BLT --log | grep -E "(TemplateSyntaxError|RuntimeError|FAILED|ERROR|Traceback)" -A 10 -B 5 | tail -200

Length of output: 73207


🏁 Script executed:

#!/bin/bash
# Find the profile template that's causing the error
echo "=== Find profile-related templates ==="
fd -e html profile website/templates/

echo -e "\n=== Check which files were modified in this PR that contain 'profile' ==="
gh pr view 5263 --repo OWASP-BLT/BLT --json files --jq '.files[] | select(.path | contains("profile")) | .path'

echo -e "\n=== Check the profile template around line 150 ==="
# The error mentions /profile/testuser/ so it's likely users.html or a profile template
cat website/templates/users.html | sed -n '145,155p' | cat -n

echo -e "\n=== Search for templates that might handle /profile/<username> route ==="
rg -l "profile.*template" website/ --type py | head -5

Length of output: 1106


🏁 Script executed:

#!/bin/bash
# Check profile.html around line 150 where the error occurs
echo "=== Check profile.html lines 140-160 ==="
sed -n '140,160p' website/templates/profile.html | cat -n

echo -e "\n=== Search for template tag issues in profile.html ==="
# Look for split template tags like {{ or {% across lines
grep -n "{{$\|{%$" website/templates/profile.html | head -20

echo -e "\n=== Count template tags in profile.html ==="
echo "{% if %} tags: $(grep -o '{% if' website/templates/profile.html | wc -l)"
echo "{% endif %} tags: $(grep -o '{% endif' website/templates/profile.html | wc -l)"
echo "{% block %} tags: $(grep -o '{% block' website/templates/profile.html | wc -l)"
echo "{% endblock %} tags: $(grep -o '{% endblock' website/templates/profile.html | wc -l)"

echo -e "\n=== Check if profile.html was modified in this PR ==="
gh api "/repos/OWASP-BLT/BLT/pulls/5263/files" --jq '.[] | select(.filename == "website/templates/profile.html") | {filename: .filename, additions: .additions, deletions: .deletions, changes: .changes}'

Length of output: 2057


🏁 Script executed:

#!/bin/bash
# Check the lines with split template tags
echo "=== Check line 62 (split template tag) ==="
sed -n '60,65p' website/templates/profile.html | cat -n

echo -e "\n=== Check line 318 (split template tag) ==="
sed -n '316,322p' website/templates/profile.html | cat -n

echo -e "\n=== Check line 334 (split template tag) ==="
sed -n '332,338p' website/templates/profile.html | cat -n

echo -e "\n=== Check line 1329 (split template tag) ==="
sed -n '1327,1332p' website/templates/profile.html | cat -n

echo -e "\n=== Find the unmatched if tag - check around block tags ==="
grep -n "{% block\|{% endblock\|{% if\|{% endif" website/templates/profile.html | sed -n '140,160p'

Length of output: 1989


🏁 Script executed:

#!/bin/bash
# Profile.html has UNMATCHED if/endif tags - let's verify
echo "=== Count: {% if %} vs {% endif %} tags ==="
echo "{% if %} count: $(grep -c '{% if' website/templates/profile.html)"
echo "{% endif %} count: $(grep -c '{% endif' website/templates/profile.html)"

echo -e "\n=== The problem: Split template tags across lines ==="
echo "Line 62-64:"
sed -n '62,64p' website/templates/profile.html

echo -e "\nLine 318-320:"
sed -n '318,320p' website/templates/profile.html

echo -e "\nLine 334-336:"
sed -n '334,336p' website/templates/profile.html

echo -e "\nLine 1329-1330:"
sed -n '1329,1330p' website/templates/profile.html

echo -e "\n=== Check what changes were made to profile.html in this PR ==="
gh pr view 5263 --repo OWASP-BLT/BLT --json files --jq '.files[] | select(.path == "website/templates/profile.html") | {path: .path, additions: .additions, deletions: .deletions}'

Length of output: 1052


@Nachiket-Roy, the tests are still failing due to template syntax errors in profile.html. This is the same type of issue we identified in search.html earlier.

The Problem

Your PR modified profile.html (1,074 additions, 1,028 deletions), and during reformatting, Django template tags were split across multiple lines, which breaks Django's template parser.

Key Issues Found:

  1. Unmatched if/endif tags:

    • 51 {% if %} tags
    • 50 {% endif %} tags
    • One {% endif %} is missing!
  2. Split template tags (Django doesn't allow this):

    Line 318-320: ❌ Invalid

    {{ activity.comments.all|length }} comments{%
    endif %}

    Line 62-64: ❌ Invalid

    class="..."> {%
    trans "Edit Profile" %}</a>

    Line 334-336: ❌ Invalid

    class="...">{%
    trans "View all activity by" %} @{{ user.username }}</a>

    Line 1329-1330: ❌ Invalid

    {%
    endfor %}

The Fix

You need to fix website/templates/profile.html:

  1. Keep all template tags on a single line:

    • {% if ... %}, {% endif %}, {% trans "..." %}, {% endfor %} must not span multiple lines
    • {{ variable }} expressions must not span multiple lines
  2. Add the missing {% endif %} tag to balance the if/endif pairs

  3. If a line is too long, break the HTML attributes instead, not the template tags:

    ✅ Correct:

    <a href="{% url 'profile_edit' %}"
       class="select-none px-3 py-1 rounded-lg bg-red-500 text-white hover:bg-red-600 hover:text-white">
        {% trans "Edit Profile" %}
    </a>

Summary

The same formatting issues that affected search.html have also broken profile.html. Django template tags must remain syntactically complete on single lines. Please review the entire profile.html file and fix all instances where {% ... %} or {{ ... }} are split across lines.

@github-actions github-actions bot removed the changes-requested PR has requested changes from a reviewer label Dec 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
website/templates/profile.html (2)

65-65: Typo: “Follwings” → “Following”

Fix copy for clarity and i18n consistency.

- {{ following|length }} <span ...> {% trans "Follwings" %} </span>
+ {{ following|length }} <span ...> {% trans "Following" %} </span>

546-554: Stray “=” in class output

When pr.is_merged, class contains a literal “=” token. Clean it to avoid invalid class names.

- <span class="... {% if pr.is_merged %} = text-purple-700 {% elif pr.state == 'open' %} text-yellow-700 {% else %} text-red-700 {% endif %} text-lg">
+ <span class="... {% if pr.is_merged %} text-purple-700 {% elif pr.state == 'open' %} text-yellow-700 {% else %} text-red-700 {% endif %} text-lg">
♻️ Duplicate comments (1)
website/templates/comments2.html (1)

135-139: Child comment avatar uses viewer’s avatar, not reply author’s

Use the child comment author’s avatar, with dummy-user.webp fallback, mirroring the top-level block.

- <img class="w-8 h-8 rounded-full object-cover"
-      {% if user.userprofile.user_avatar %} src="{{ MEDIA_URL }}{{ user.userprofile.user_avatar }}" {% else %} src="{% static 'images/dummy-user.webp' %}" {% endif %}
-      alt="{{ child_comment.author }} profile"
-      width="32"
-      height="32">
+ <img class="w-8 h-8 rounded-full object-cover"
+      {% if child_comment.author_fk.user_avatar %}
+          src="{{ MEDIA_URL }}{{ child_comment.author_fk.user_avatar }}"
+      {% else %}
+          src="{% static 'images/dummy-user.webp' %}"
+      {% endif %}
+      alt="{{ child_comment.author }} profile"
+      width="32"
+      height="32">

(Adjust author_fk to actual relation if different.)

🧹 Nitpick comments (1)
website/templates/features.html (1)

32-36: WebP migration looks solid

All featured tiles now reference WebP; alt text and dimensions preserved.

Optional: add for broader fallback

If you want graceful PNG/JPEG fallback for older browsers, wrap each image with a picture element.

<picture>
  <source srcset="{% static 'img/features/pr-review.webp' %}" type="image/webp">
  <img src="{% static 'img/features/pr-review.png' %}" alt="PR_review" width="200" height="100" class="rounded-md mb-3 w-full h-52 object-contain">
</picture>

Also applies to: 54-59, 76-81, 98-103, 120-125, 142-147, 161-166, 180-185, 199-204, 218-223, 237-242, 256-261, 275-280, 294-299, 313-318, 332-337, 351-356, 370-375, 389-394, 408-413, 427-432, 446-451, 465-470, 484-489, 503-508, 522-527, 541-546, 560-565, 579-584, 598-603, 617-622, 636-641, 655-660, 674-679, 693-698, 712-717, 731-736, 750-755, 769-774, 788-793, 807-812, 826-831, 856-861, 878-883, 900-905, 923-928

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 09cd0dd and b9f7d08.

📒 Files selected for processing (9)
  • website/templates/comments2.html (2 hunks)
  • website/templates/domain.html (1 hunks)
  • website/templates/features.html (46 hunks)
  • website/templates/home.html (2 hunks)
  • website/templates/includes/issue_right_info.html (2 hunks)
  • website/templates/issue.html (1 hunks)
  • website/templates/organization/dashboard/organization_team_overview.html (2 hunks)
  • website/templates/profile.html (4 hunks)
  • website/templates/users.html (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • website/templates/issue.html
  • website/templates/includes/issue_right_info.html
  • website/templates/organization/dashboard/organization_team_overview.html
  • website/templates/users.html
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (4)
website/templates/profile.html (1)

289-289: LGTM on WebP fallbacks

onerror fallback to dummy-user.webp is correct and non-breaking. No further action needed.

Also applies to: 437-437

website/templates/domain.html (1)

345-346: LGTM on WebP fallbacks

Good defensive onerror swap to dummy-user.webp for leaderboard avatars.

Also applies to: 352-353

website/templates/comments2.html (1)

54-56: LGTM on default avatar swap

Top-level comment fallback to dummy-user.webp looks good.

website/templates/home.html (1)

437-440: LGTM on WebP swaps

  • Avatar onerror fallback to dummy-user.webp is correct.
  • Google logo switched to WebP; dimensions preserved.

Also applies to: 1198-1203

@github-actions github-actions bot added tests: passed Django tests passed and removed tests: failed Django tests failed labels Dec 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
blt/settings.py (1)

132-135: Dev-only middleware ordering looks good; consider centralizing the "debug tools enabled" flag

Prepending SilkyMiddleware and appending livereload/debug-toolbar only when DEBUG and not TESTING is correct and avoids production/test issues. One robustness tweak to consider:

  • Define a single flag (e.g., DEBUG_TOOLING_ENABLED = DEBUG and not TESTING) after all DEBUG/TESTING logic is finalized (including the DYNO override at line 285), and reuse it for:
    • Adding dev apps to INSTALLED_APPS (line 105)
    • Adding dev middleware (line 132)
    • Configuring debug toolbar panels (line 141)
    • Wiring dev URLs in blt/urls.py (line 1265)

This pattern (DEBUG and not TESTING) appears at least 4 times across the codebase; centralizing keeps all debug/profiling toggles consistent if the way DEBUG/TESTING are set changes later.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between ce34493 and 038eeb7.

📒 Files selected for processing (2)
  • blt/settings.py (5 hunks)
  • website/templates/profile.html (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (6)
website/templates/profile.html (5)

65-65: Typo fix approved.

Changed button label from "Follwings" to "Following" — straightforward correction.


477-477: WebP fallback update consistent.

Updated onerror fallback from dummy-user.png to dummy-user.webp in the top-findings section.


587-587: Template conditional syntax corrected.

Fixed the inline conditional class binding. The structure now correctly applies text color classes based on PR state (pr.is_merged, pr.state == 'open', or default). The conditional logic is sound and properly closed.


782-782: WebP updates in legacy code section.

Updated image fallback references in the commented-out old code block (lines 782, 932). While these lines are commented out and not active, the updates maintain consistency with the WebP migration across the file.

Also applies to: 932-932


329-329: WebP fallback update is correct and deployment-ready.

The onerror fallback has been correctly updated from dummy-user.png to dummy-user.webp. The WebP asset exists at website/static/images/dummy-user.webp and is ready for deployment.

blt/settings.py (1)

316-316: Staticfiles storage switched to CompressedManifestStaticFilesStorage in all environments

Using whitenoise.storage.CompressedManifestStaticFilesStorage for staticfiles in both the Heroku and non-Heroku branches aligns with WhiteNoise best practices. The middleware is correctly positioned right after SecurityMiddleware, which is the recommended placement. This configuration will improve caching and compression behavior for static files across all environments.

Also applies to: 331-331

@Nachiket-Roy Nachiket-Roy marked this pull request as draft December 11, 2025 16:49
@Nachiket-Roy Nachiket-Roy marked this pull request as ready for review December 11, 2025 16:54
@github-actions github-actions bot added the last-active: 0d PR last updated 0 days ago label Dec 12, 2025
@github-actions github-actions bot added has-peer-review PR has received peer review and removed needs-peer-review PR needs peer review labels Dec 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 67 PR changes 67 files has-peer-review PR has received peer review last-active: 0d PR last updated 0 days ago pre-commit: passed Pre-commit checks passed tests: passed Django tests passed

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

3 participants