Skip to content

[bug/qa] Fix lint errors being ignored (continuation of #571)#600

Open
Varadraj75 wants to merge 10 commits intoopenwisp:masterfrom
Varadraj75:fix-lint-errors-485-followup
Open

[bug/qa] Fix lint errors being ignored (continuation of #571)#600
Varadraj75 wants to merge 10 commits intoopenwisp:masterfrom
Varadraj75:fix-lint-errors-485-followup

Conversation

@Varadraj75
Copy link

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #485 .
Continuation of #571

Description of Changes

This PR continues and completes the work started in #571 by @ssmadhavan006, addressing the original goal of issue #485: ensuring QA and lint checks are not ignored and that CI failures are real and actionable.

The branch is based on the commits from #571, with additional fixes applied on top to resolve remaining QA issues and align local behaviour with CI expectations.

Key changes

  • Built on top of the previous contributor’s work instead of re-implementing it
  • Ensured run-qa-checks properly fails on QA, yaml-lint, and ansible-lint errors
  • Fixed missing newline and formatting issues detected by QA
  • Added flake8 exclusions for Jinja templates to avoid false positives
  • Verified QA behaviour locally to match CI (no suppressed errors like || true)

All CI checks are now green, and lint/QA failures are no longer silently ignored.

Screenshot

Screenshot showing local run-qa-checks execution with QA and documentation lint checks
properly enforced and failures correctly reported (RST files reported when applicable).
Screenshot 2026-02-01 at 3 23 07 AM

Copilot AI review requested due to automatic review settings February 1, 2026 10:02
@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

Walkthrough

This PR addresses ignored lint errors by implementing a comprehensive QA check system. Key changes include: creating a run-qa-checks script with yamllint and ansible-lint checks, removing lint configurations from molecule files and consolidating them in the script, enhancing .yamllint.yml with additional validation rules, updating .github/workflows/ci.yml to properly execute QA checks with step conditions ensuring dependency satisfaction and preventing build blockage, standardizing YAML quoting and formatting across configuration files, modernizing Ansible module references to community collections, and expanding FreeRADIUS task handling with distribution-specific configurations for multiple Ubuntu/Debian versions. Documentation is updated with QA check instructions.

Sequence Diagram(s)

sequenceDiagram
    participant CI as GitHub Actions
    participant Deps as Install Dependencies
    participant QA as QA Checks<br/>(yamllint, ansible-lint)
    participant Tests as Run Tests
    participant Results as Build Results

    CI->>Deps: Start with id: deps
    activate Deps
    Deps-->>CI: Success
    deactivate Deps
    
    CI->>QA: if: success() && !cancelled()
    activate QA
    QA->>QA: yamllint
    QA->>QA: ansible-lint
    alt QA Passes
        QA-->>CI: Success (non-blocking)
    else QA Fails
        QA-->>CI: Failure (continues)
    end
    deactivate QA
    
    CI->>Tests: Proceed to tests<br/>PY_COLORS quoted
    activate Tests
    Tests-->>CI: Results
    deactivate Tests
    
    CI->>Results: Report outcome
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: fixing lint errors that were being ignored, building upon previous work (#571) to address issue #485.
Description check ✅ Passed The description includes reference to issue #485 and #571, clear explanation of changes, includes screenshot evidence, but test cases and documentation updates are not marked as complete.
Linked Issues check ✅ Passed The PR addresses all core requirements from #485: centralized QA checks in run-qa-checks script, removed lint error suppression, fixed linting issues across multiple files, and ensured CI reflects real actionable failures.
Out of Scope Changes check ✅ Passed All changes are in-scope: YAML formatting standardization, mode value conversions to strings, repository configuration improvements, and lint check implementation align with issue #485 objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR continues and completes the work started in #571 to ensure QA and lint checks are not ignored and CI failures are real and actionable. It addresses issue #485 by removing suppressed QA checks and fixing all resulting lint errors.

Changes:

  • Removed all QA check skips from the run-qa-checks script, enabling full enforcement of code quality standards
  • Fixed YAML formatting issues across all Ansible files including line endings (CRLF to LF), trailing spaces, document start markers, and octal mode value quoting
  • Added comprehensive flake8 and yamllint configuration files with appropriate exclusions for templates and virtual environments
  • Updated CI workflow to properly fail on QA errors by removing continue-on-error flag and adding conditional test execution
  • Moved block-level when and tags clauses before the block keyword for improved readability and consistency
  • Updated one instance of django_manage to use FQCN community.general.django_manage in handlers

Reviewed changes

Copilot reviewed 27 out of 33 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
run-qa-checks Removed all QA skip flags to enforce lint checks
pyproject.toml Added flake8 configuration section
.flake8 Added comprehensive flake8 exclusion configuration
.yamllint.yml Expanded yamllint rules and ignore patterns
.ansible-lint Fixed YAML formatting
.github/workflows/ci.yml Removed continue-on-error and added conditional execution
.github/dependabot.yml Fixed YAML document start marker
.github/FUNDING.yml Fixed YAML document start marker
tasks/*.yml Fixed YAML formatting, removed blank lines, quoted mode values
handlers/main.yml Updated django_manage to use FQCN, fixed YAML formatting
molecule/*/molecule.yml Removed inline lint blocks, fixed YAML formatting
molecule/resources/*.yml Fixed YAML formatting
defaults/main.yml Fixed indentation and changed null to empty values
meta/main.yml Fixed YAML document start marker
vars/debian-13.yml Removed trailing spaces, fixed document start marker
templates/openwisp2/settings.py Line number updates only (template excluded from linting)
docs/developer/installation.rst Added QA checks documentation section
README.md Line number updates only

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

run-qa-checks Outdated
--skip-black \
--skip-checkmigrations
openwisp-qa-check

Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

There is a trailing whitespace on line 5. This should be removed to maintain consistency with the QA standards being enforced by this PR.

Suggested change

Copilot uses AI. Check for mistakes.
pyproject.toml Outdated
Comment on lines 10 to 11
]

No newline at end of file
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

There is a trailing whitespace on line 11. This should be removed to maintain consistency with the QA standards being enforced by this PR.

Suggested change
]
]

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +219
django_manage:
app_path: "{{ openwisp2_path }}"
command: "check --deploy --tag geocoding"
virtualenv: "{{ virtualenv_path }}"
- name: Migrate
notify: Reload application
become: true
become_user: "{{ www_user }}"
django_manage:
app_path: "{{ openwisp2_path }}"
command: migrate
virtualenv: "{{ virtualenv_path }}"
tags: [django_settings]
- name: Set permissions to sqlite db
when: openwisp2_database.engine == "django.db.backends.sqlite3"
file:
path: "{{ openwisp2_database.name }}"
state: file
group: "{{ www_group }}"
mode: 0770
- name: Set permissions to "{{ openwisp2_path }}/static"
file:
path: "{{ openwisp2_path }}/static"
state: directory
group: "{{ www_group }}"
owner: "{{ www_user }}"
# sets executable only to directories
mode: u=rwX,g=rX,o=rX
recurse: true
tags:
- molecule-idempotence-notest
- openwisp2_theme
- name: Check if ow2_static exists
stat:
path: "{{ role_path }}/files/ow2_static"
register: ow2_static_dir
tags:
- openwisp2_theme
- name: Copy static files
become: true
copy:
src: ow2_static/
dest: "{{ openwisp2_path }}/static_custom"
owner: "{{ www_user }}"
group: "{{ www_group }}"
mode: 0640
when: ow2_static_dir.stat.exists
tags:
- openwisp2_theme
- name: Collectstatic
notify: Reload application
become: true
become_user: "{{ www_user }}"
django_manage:
app_path: "{{ openwisp2_path }}"
command: "collectstatic --noinput"
virtualenv: "{{ virtualenv_path }}"
register: collectstatic_output
changed_when: '"\n0 static files" not in collectstatic_output.out'
tags:
- molecule-idempotence-notest
- openwisp2_theme
# needed to run compilemessages
# some .mo files are created in virtualenv_path
- name: Set permissions to "{{ virtualenv_path }}"
when: openwisp2_internationalization
file:
path: "{{ virtualenv_path }}"
state: directory
group: "{{ www_group }}"
owner: "{{ www_user }}"
# sets executable only to directories
mode: u=rwX,g=rX,o=rX
recurse: true
tags:
- molecule-idempotence-notest
- name: Compilemessages
notify: Reload application
become: true
become_user: "{{ www_user }}"
when: openwisp2_internationalization
django_manage:
app_path: "{{ openwisp2_path }}"
command: "compilemessages"
virtualenv: "{{ virtualenv_path }}"
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The django_manage module usage in handlers/main.yml has been updated to use the FQCN (community.general.django_manage), but the usage in tasks/django.yml still uses the short form. For consistency and to avoid potential deprecation warnings in future Ansible versions, all django_manage module usages should use the fully qualified collection name.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +132
openwisp2_celery_autoscale:
openwisp2_celery_prefetch_multiplier:
openwisp2_celery_optimization: default
openwisp2_celery_network: true
openwisp2_celery_network_concurrency: 1
openwisp2_celery_network_autoscale:
openwisp2_celery_network_prefetch_multiplier: 10
openwisp2_celery_network_optimization: fair
openwisp2_celery_firmware_upgrader: true
openwisp2_celery_firmware_upgrader_concurrency: 1
openwisp2_celery_firmware_upgrader_autoscale:
openwisp2_celery_firmware_upgrader_prefetch_multiplier: 1
openwisp2_celery_firmware_upgrader_optimization: fair
openwisp2_celery_monitoring: true
openwisp2_celery_monitoring_concurrency: 2
openwisp2_celery_monitoring_autoscale:
openwisp2_celery_monitoring_prefetch_multiplier: 10
openwisp2_celery_monitoring_optimization: fair
openwisp2_celery_task_routes_defaults: true
openwisp2_celery_broker_url: redis://{{ openwisp2_redis_host }}:{{ openwisp2_redis_port }}/3
openwisp2_celery_task_acks_late: true
openwisp2_celery_broker_max_tries: 10
openwisp2_celery_pool:
openwisp2_celery_monitoring_pool:
openwisp2_celery_network_pool:
openwisp2_celery_firmware_upgrader_pool:
openwisp2_django_celery_logging: false
openwisp2_postfix_install: true
postfix_smtpd_relay_restrictions_override: "permit_sasl_authenticated, permit_mynetworks, check_relay_domains, reject_unauth_destination, reject"
openwisp2_uwsgi_gid:
openwisp2_uwsgi_processes: 1
openwisp2_uwsgi_threads: 1
openwisp2_uwsgi_listen: 100
openwisp2_uwsgi_socket: "{{ openwisp2_path }}/uwsgi.sock"
openwisp2_uwsgi_extra_conf: |
single-interpreter=True
log-4xx=True
log-5xx=True
disable-logging=True
auto-procname=True
openwisp2_daphne_install: true
openwisp2_daphne_socket: "unix://{{ openwisp2_path }}/daphne0.sock"
openwisp2_daphne_processes: 1
openwisp2_daphne_websocket_timeout: 3600 # in seconds
# nginx configuration
openwisp2_http_allowed_ip: false
openwisp2_admin_allowed_network:
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

Variables with empty values (lines 86, 87, 91, 96, 101, 108-111, 115, 132) may cause issues. In YAML, a key with no value after the colon is interpreted as null/None, which is semantically the same as explicitly setting 'null'. However, for clarity and consistency with the rest of the codebase, these should explicitly use 'null' or omit the keys entirely if they are meant to be optional. The yamllint configuration forbids empty values in block mappings, though these are at the root level. Consider being explicit about null values for clarity.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +225
mode: 0770
- name: Create "{{ openwisp2_path }}/openwisp2"
file:
path: "{{ openwisp2_path }}/openwisp2"
state: directory
group: "{{ www_group }}"
mode: 0770
- name: Create "{{ openwisp2_path }}/log"
file:
path: "{{ openwisp2_path }}/log"
state: directory
group: "{{ www_group }}"
mode: 0770
recurse: true
tags:
- molecule-idempotence-notest
- name: Create "{{ openwisp2_path }}/static_custom"
file:
path: "{{ openwisp2_path }}/static_custom"
state: directory
group: "{{ www_group }}"
mode: 0770
tags:
- openwisp2_theme
- name: Template manage.py
notify: Reload application
template:
src: manage.py
dest: "{{ openwisp2_path }}/manage.py"
group: "{{ www_group }}"
mode: 0754
- name: Template celery.py
notify: Reload application
template:
src: openwisp2/celery.py
dest: "{{ openwisp2_path }}/openwisp2/celery.py"
group: "{{ www_group }}"
mode: 0640
tags: [django_init]
- name: Template version.py
notify: Reload application
template:
src: openwisp2/version.py
dest: "{{ openwisp2_path }}/openwisp2/version.py"
group: "{{ www_group }}"
mode: 0640
tags: [django_init]
- name: Template __init__.py
notify: Reload application
template:
src: openwisp2/__init__.py
dest: "{{ openwisp2_path }}/openwisp2/__init__.py"
group: "{{ www_group }}"
mode: 0640
tags: [django_init]
- name: Template urls.py
notify: Reload application
template:
src: openwisp2/urls.py
dest: "{{ openwisp2_path }}/openwisp2/urls.py"
group: "{{ www_group }}"
mode: 0640
- name: Template wsgi.py
notify: Reload application
template:
src: openwisp2/wsgi.py
dest: "{{ openwisp2_path }}/openwisp2/wsgi.py"
group: "{{ www_group }}"
mode: 0640
- name: Template asgi.py
notify: Reload application
template:
src: openwisp2/asgi.py
dest: "{{ openwisp2_path }}/openwisp2/asgi.py"
group: "{{ www_group }}"
mode: 0640
# set openwisp2_secret_key if not defined explicitly
- import_tasks: django_secret_key.yml
when: openwisp2_secret_key is not defined
tags: [django_settings]
- name: Template settings.py
notify: Reload application
template:
src: openwisp2/settings.py
dest: "{{ openwisp2_path }}/openwisp2/settings.py"
group: "{{ www_group }}"
mode: 0640
tags: [django_settings]
- name: Template routing.py
notify: Reload application
template:
src: openwisp2/routing.py
dest: "{{ openwisp2_path }}/openwisp2/routing.py"
group: "{{ www_group }}"
mode: 0640
- name: Start redis for migration
when: openwisp2_redis_install
service:
name: redis
state: started
- name: Run geocoding check
become: true
become_user: "{{ www_user }}"
when: openwisp2_geocoding_check
django_manage:
app_path: "{{ openwisp2_path }}"
command: "check --deploy --tag geocoding"
virtualenv: "{{ virtualenv_path }}"
- name: Migrate
notify: Reload application
become: true
become_user: "{{ www_user }}"
django_manage:
app_path: "{{ openwisp2_path }}"
command: migrate
virtualenv: "{{ virtualenv_path }}"
tags: [django_settings]
- name: Set permissions to sqlite db
when: openwisp2_database.engine == "django.db.backends.sqlite3"
file:
path: "{{ openwisp2_database.name }}"
state: file
group: "{{ www_group }}"
mode: 0770
- name: Set permissions to "{{ openwisp2_path }}/static"
file:
path: "{{ openwisp2_path }}/static"
state: directory
group: "{{ www_group }}"
owner: "{{ www_user }}"
# sets executable only to directories
mode: u=rwX,g=rX,o=rX
recurse: true
tags:
- molecule-idempotence-notest
- openwisp2_theme
- name: Check if ow2_static exists
stat:
path: "{{ role_path }}/files/ow2_static"
register: ow2_static_dir
tags:
- openwisp2_theme
- name: Copy static files
become: true
copy:
src: ow2_static/
dest: "{{ openwisp2_path }}/static_custom"
owner: "{{ www_user }}"
group: "{{ www_group }}"
mode: 0640
when: ow2_static_dir.stat.exists
tags:
- openwisp2_theme
- name: Collectstatic
notify: Reload application
become: true
become_user: "{{ www_user }}"
django_manage:
app_path: "{{ openwisp2_path }}"
command: "collectstatic --noinput"
virtualenv: "{{ virtualenv_path }}"
register: collectstatic_output
changed_when: '"\n0 static files" not in collectstatic_output.out'
tags:
- molecule-idempotence-notest
- openwisp2_theme
# needed to run compilemessages
# some .mo files are created in virtualenv_path
- name: Set permissions to "{{ virtualenv_path }}"
when: openwisp2_internationalization
file:
path: "{{ virtualenv_path }}"
state: directory
group: "{{ www_group }}"
owner: "{{ www_user }}"
# sets executable only to directories
mode: u=rwX,g=rX,o=rX
recurse: true
tags:
- molecule-idempotence-notest
- name: Compilemessages
notify: Reload application
become: true
become_user: "{{ www_user }}"
when: openwisp2_internationalization
django_manage:
app_path: "{{ openwisp2_path }}"
command: "compilemessages"
virtualenv: "{{ virtualenv_path }}"
- name: Create load_initial_data.py script
template:
src: load_initial_data.py
dest: "{{ openwisp2_path }}/load_initial_data.py"
mode: 0754
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The yamllint configuration at line 24-25 sets forbid-implicit-octal: true and forbid-explicit-octal: true, which would forbid octal mode values like mode: 0770 used throughout this file. However, these mode values remain unquoted. Either the yamllint configuration should be adjusted to allow octal values with proper quoting (by setting forbid-explicit-octal to false), or all mode values in this file should be quoted as strings (e.g., "0770"). Based on the changes in other task files where mode values are quoted, it appears the intention is to quote all mode values, but this file was missed.

Copilot uses AI. Check for mistakes.
pyproject.toml Outdated
Comment on lines 2 to 11
extend_exclude = ["**/*.py"]

[tool.flake8]
exclude = [
".venv",
"venv",
"__pycache__",
".git",
]

No newline at end of file
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

There are two flake8 configuration files: pyproject.toml with a [tool.flake8] section and .flake8. Flake8 does not natively support pyproject.toml configuration (it only reads from .flake8, setup.cfg, or tox.ini). The [tool.flake8] section in pyproject.toml will be ignored by flake8 unless a plugin like flake8-pyproject is installed. Consider removing the duplicate configuration from pyproject.toml or documenting the requirement for the flake8-pyproject plugin in requirements-test.txt.

Suggested change
extend_exclude = ["**/*.py"]
[tool.flake8]
exclude = [
".venv",
"venv",
"__pycache__",
".git",
]
extend_exclude = ["**/*.py"]

Copilot uses AI. Check for mistakes.

TEST_RUNNER = "openwisp_utils.metric_collection.tests.runner.MockRequestPostRunner"
SECURE_REFERRER_POLICY = "strict-origin"
import os
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

Syntax Error (in Python 3).

Copilot uses AI. Check for mistakes.
Copy link

@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: 11

🤖 Fix all issues with AI agents
In @.github/dependabot.yml:
- Line 13: The configuration's interval value doesn't match its inline comment:
update the interval setting named "interval" so it matches the intended cadence
in the comment (change "monthly" to "weekly") or adjust the comment to reflect
the actual value; specifically edit the "interval" entry in the dependabot
configuration so the value and the comment are consistent.
- Line 7: The inline comment is inconsistent with the dependabot setting: the
key interval: "monthly" does not match the comment "Check for updates weekly";
update the comment to reflect the actual configuration (change the comment to
"Check for updates monthly") or, if weekly updates are intended, change the
interval value to "weekly" so the comment and the interval: "monthly" setting
for dependabot are consistent; edit the line containing interval: "monthly" and
its adjacent comment accordingly.

In @.github/workflows/ci.yml:
- Around line 38-41: The step name uses an undefined matrix variable
"matrix.python-version" which should be corrected to match the defined matrix
value; update the step name "Set up Python ${{ matrix.python-version }}" to
reference the actual matrix key (e.g., "Set up Python ${{ matrix.distro }}" or
simply "Set up Python") or alternatively add a "python-version" entry to the
matrix so the interpolation matches; locate the workflow step with the name
string and make the name and matrix keys consistent.

In `@handlers/main.yml`:
- Around line 48-55: Add a machine-readable collection requirement so the play
using community.general.django_manage always has the collection installed:
update the role metadata by adding a galaxy_info.collections entry that declares
community.general with version ">=3.6.0" (and also add ansible.posix) under the
role's meta configuration so the dependency is enforced for handlers that call
the django_manage module; ensure the collections list includes the names and
version constraint exactly as specified.

In `@README.md`:
- Around line 3-5: Replace the insecure HTTP Galaxy badge image URL with HTTPS:
find the markdown line starting with the "[![Galaxy]" badge (the image URL
currently
"http://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square")
and update it to use "https://", ensuring the badge image is loaded securely
like the other badges.

In `@run-qa-checks`:
- Line 5: Remove the trailing whitespace at the end of the file "run-qa-checks"
(the extra space at EOF) so the file ends cleanly with no spaces or blank
characters after the last line; simply delete the trailing space and save the
file.

In `@tasks/cron.yml`:
- Around line 5-7: Update the typo in the cron job name: change the value of the
cron.name string "Update toplogies" to "Update topologies" so the cron entry is
correctly spelled; edit the cron mapping where the name key is defined (cron ->
name) to use the corrected spelling.

In `@tasks/freeradius.yml`:
- Around line 52-61: The task "Add freeradius repository into Debian bookworm
sources list" uses an unnecessary sudo inside the shell command while already
declaring become: true; remove the "sudo" and the backslash continuation so the
command simply echoes to /etc/apt/sources.list.d/networkradius.list under
privilege escalation. Ensure the shell entry matches the bullseye task style (no
sudo) and keep become: true so the file is created with elevated privileges.
- Around line 74-83: The task "Add Network RADIUS PGP public key" currently uses
ignore_errors: true while registering result and using until: result is success,
which masks failures; remove ignore_errors: true so the playbook will fail if
the get_url task (register: result, until: result is success, retries/delay)
cannot download the key, or alternatively keep ignore_errors but add an explicit
verification step after the task (e.g., an assert or stat checking the presence
of /etc/apt/trusted.gpg.d/packages.networkradius.com.asc and failing the
playbook if not found) before any apt update/install tasks that depend on the
signed-by key.
- Around line 195-201: Replace the non-idiomatic include_tasks path
"tasks/freeradius_eap.yml" with the filename only "freeradius_eap.yml" in the
task that loops over freeradius_eap_orgs (the task with include_tasks, loop: "{{
freeradius_eap_orgs }}", loop_control.loop_var: org and tags: [freeradius_eap])
so the role includes the task file from the role’s tasks/ directory in the
standard Ansible way; keep the loop, loop_control and when condition unchanged.

In `@tasks/nginx.yml`:
- Around line 26-36: The task "Create SSL cert if not exists yet" embeds
creates={{ openwisp2_ssl_cert }} inside the openssl command string, so Ansible
won't treat it as the command module's creates argument; remove the trailing
"creates=..." from the command value and add a separate top-level creates: "{{
openwisp2_ssl_cert }}" key under the same task (keeping the command string
intact and the notify: Restart nginx), so the command module will skip execution
when the certificate file exists.
🧹 Nitpick comments (1)
tasks/django.yml (1)

8-8: Consider quoting octal mode values for consistency.

Ansible recommends quoting file mode values as strings (e.g., mode: "0770") to avoid potential YAML parser ambiguities. This pattern appears throughout the file on lines 8, 15, 22, 32, 42, 50, 59, 68, 77, 85, 93, 106, 115, 148, 157, 177, and 225.

Note: The tasks/django_secret_key.yml file in this PR already uses quoted modes (mode: "0754"), so aligning this file would improve consistency.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9ee54c and 404106d.

📒 Files selected for processing (33)
  • .ansible-lint
  • .flake8
  • .github/FUNDING.yml
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .yamllint.yml
  • README.md
  • defaults/main.yml
  • docs/developer/installation.rst
  • handlers/main.yml
  • meta/main.yml
  • molecule/default/molecule.yml
  • molecule/local/molecule.yml
  • molecule/resources/converge.yml
  • molecule/resources/requirements.yml
  • molecule/resources/verify.yml
  • pyproject.toml
  • run-qa-checks
  • tasks/apt.yml
  • tasks/complete.yml
  • tasks/cron.yml
  • tasks/django.yml
  • tasks/django_secret_key.yml
  • tasks/freeradius.yml
  • tasks/freeradius_eap.yml
  • tasks/main.yml
  • tasks/nginx.yml
  • tasks/pip.yml
  • tasks/ssh.yml
  • tasks/supervisor.yml
  • tasks/system.yml
  • templates/openwisp2/settings.py
  • vars/debian-13.yml
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/ci.yml

[error] 38-38: property "python-version" is not defined in object type {distro: string}

(expression)

🪛 Checkov (3.2.334)
molecule/resources/verify.yml

[medium] 50-55: Ensure that certificate validation isn't disabled with uri

(CKV_ANSIBLE_1)

🪛 Ruff (0.14.14)
templates/openwisp2/settings.py

[warning] 20-20: Expected an expression

(invalid-syntax)


[warning] 20-20: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 20-20: Expected ,, found name

(invalid-syntax)


[warning] 20-20: Expected an expression

(invalid-syntax)


[warning] 21-21: Expected ,, found string

(invalid-syntax)


[warning] 22-22: Expected an expression

(invalid-syntax)


[warning] 22-22: Expected an expression

(invalid-syntax)


[warning] 50-50: Expected an expression

(invalid-syntax)


[warning] 50-50: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 50-50: Expected ,, found name

(invalid-syntax)


[warning] 50-50: Expected an expression

(invalid-syntax)


[warning] 51-51: Expected ,, found string

(invalid-syntax)


[warning] 52-52: Expected an expression

(invalid-syntax)


[warning] 52-52: Expected an expression

(invalid-syntax)


[warning] 53-53: Expected ,, found {

(invalid-syntax)


[warning] 53-53: Expected an expression

(invalid-syntax)


[warning] 53-53: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 53-53: Expected ,, found name

(invalid-syntax)


[warning] 53-53: Expected an expression

(invalid-syntax)


[warning] 54-54: Expected ,, found string

(invalid-syntax)


[warning] 58-58: Expected an expression

(invalid-syntax)


[warning] 58-58: Expected an expression

(invalid-syntax)


[warning] 59-59: Expected ,, found string

(invalid-syntax)


[warning] 61-61: Expected an expression

(invalid-syntax)


[warning] 61-61: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 61-61: Expected ,, found name

(invalid-syntax)


[warning] 61-61: Expected an expression

(invalid-syntax)


[warning] 62-62: Expected ,, found string

(invalid-syntax)


[warning] 63-63: Expected an expression

(invalid-syntax)


[warning] 63-63: Expected an expression

(invalid-syntax)


[warning] 64-64: Expected ,, found {

(invalid-syntax)


[warning] 64-64: Expected an expression

(invalid-syntax)


[warning] 64-64: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 64-64: Expected ,, found name

(invalid-syntax)


[warning] 64-64: Expected an expression

(invalid-syntax)


[warning] 65-65: Expected ,, found string

(invalid-syntax)


[warning] 66-66: Expected an expression

(invalid-syntax)


[warning] 66-66: Expected an expression

(invalid-syntax)


[warning] 67-67: Expected ,, found string

(invalid-syntax)


[warning] 68-68: Expected an expression

(invalid-syntax)


[warning] 68-68: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 68-68: Expected ,, found name

(invalid-syntax)


[warning] 68-68: Expected an expression

(invalid-syntax)


[warning] 69-69: Expected ,, found string

(invalid-syntax)


[warning] 72-72: Expected an expression

(invalid-syntax)


[warning] 72-72: Expected an expression

(invalid-syntax)


[warning] 75-75: Expected ,, found string

(invalid-syntax)


[warning] 76-76: Expected an expression

(invalid-syntax)


[warning] 76-76: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 76-76: Expected ,, found name

(invalid-syntax)


[warning] 76-76: Expected an expression

(invalid-syntax)


[warning] 77-77: Expected ,, found string

(invalid-syntax)


[warning] 78-78: Expected an expression

(invalid-syntax)


[warning] 78-78: Expected an expression

(invalid-syntax)


[warning] 79-79: Expected ,, found string

(invalid-syntax)


[warning] 91-91: Expected an expression

(invalid-syntax)


[warning] 91-91: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 91-91: Expected ,, found name

(invalid-syntax)


[warning] 91-91: Expected an expression

(invalid-syntax)


[warning] 92-92: Expected ,, found string

(invalid-syntax)


[warning] 93-93: Expected an expression

(invalid-syntax)


[warning] 93-93: Expected an expression

(invalid-syntax)


[warning] 94-94: Expected ,, found string

(invalid-syntax)


[warning] 98-98: Expected an expression

(invalid-syntax)


[warning] 98-98: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 98-98: Expected ,, found name

(invalid-syntax)


[warning] 98-98: Expected an expression

(invalid-syntax)


[warning] 99-99: Expected ,, found string

(invalid-syntax)


[warning] 100-100: Expected an expression

(invalid-syntax)


[warning] 100-100: Expected an expression

(invalid-syntax)


[warning] 101-101: Expected ,, found {

(invalid-syntax)


[warning] 101-101: Expected an expression

(invalid-syntax)


[warning] 101-101: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 101-101: Expected ,, found name

(invalid-syntax)


[warning] 101-101: Expected an expression

(invalid-syntax)


[warning] 102-102: Expected ,, found string

(invalid-syntax)


[warning] 103-103: Expected an expression

(invalid-syntax)


[warning] 103-103: Expected an expression

(invalid-syntax)


[warning] 104-104: Expected ,, found {

(invalid-syntax)


[warning] 104-104: Expected an expression

(invalid-syntax)


[warning] 104-104: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 104-104: Expected ,, found name

(invalid-syntax)


[warning] 104-104: Expected an expression

(invalid-syntax)


[warning] 105-105: Expected ,, found string

(invalid-syntax)


[warning] 106-106: Expected an expression

(invalid-syntax)


[warning] 106-106: Expected an expression

(invalid-syntax)


[warning] 114-114: Expected an expression

(invalid-syntax)


[warning] 114-114: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 114-114: Expected ,, found name

(invalid-syntax)


[warning] 114-114: Expected an expression

(invalid-syntax)


[warning] 116-116: Expected an expression

(invalid-syntax)


[warning] 116-116: Expected an expression

(invalid-syntax)


[warning] 118-118: Expected an expression

(invalid-syntax)


[warning] 118-118: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 118-118: Expected ,, found name

(invalid-syntax)


[warning] 118-118: Expected an expression

(invalid-syntax)


[warning] 120-120: Expected an expression

(invalid-syntax)


[warning] 120-120: Expected an expression

(invalid-syntax)


[warning] 138-138: Expected an expression

(invalid-syntax)


[warning] 138-138: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 138-138: Expected ,, found name

(invalid-syntax)


[warning] 138-138: Expected an expression

(invalid-syntax)


[warning] 139-139: Expected ,, found string

(invalid-syntax)


[warning] 140-140: Expected an expression

(invalid-syntax)


[warning] 140-140: Expected an expression

(invalid-syntax)


[warning] 141-141: Expected ,, found string

(invalid-syntax)


[warning] 144-144: Expected an expression

(invalid-syntax)


[warning] 144-144: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 144-144: Expected ,, found name

(invalid-syntax)


[warning] 144-144: Expected an expression

(invalid-syntax)


[warning] 145-145: Expected ,, found string

(invalid-syntax)


[warning] 146-146: Expected an expression

(invalid-syntax)


[warning] 146-146: Expected an expression

(invalid-syntax)


[warning] 147-147: Expected ,, found string

(invalid-syntax)


[warning] 150-150: Expected an expression

(invalid-syntax)


[warning] 150-150: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 150-150: Expected ,, found name

(invalid-syntax)


[warning] 150-150: Expected an expression

(invalid-syntax)


[warning] 151-151: Expected ,, found string

(invalid-syntax)


[warning] 152-152: Expected an expression

(invalid-syntax)


[warning] 152-152: Expected an expression

(invalid-syntax)


[warning] 153-153: Expected ,, found string

(invalid-syntax)


[warning] 160-160: Expected an expression

(invalid-syntax)


[warning] 160-160: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 160-160: Expected ,, found name

(invalid-syntax)


[warning] 160-160: Expected an expression

(invalid-syntax)


[warning] 170-170: Expected an expression

(invalid-syntax)


[warning] 170-170: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 170-170: Expected ,, found name

(invalid-syntax)


[warning] 170-170: Expected an expression

(invalid-syntax)


[warning] 172-172: Expected an expression

(invalid-syntax)


[warning] 172-172: Expected an expression

(invalid-syntax)


[warning] 173-173: Expected an expression

(invalid-syntax)


[warning] 173-173: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 173-173: Expected ,, found name

(invalid-syntax)


[warning] 173-173: Expected an expression

(invalid-syntax)


[warning] 175-175: Expected an expression

(invalid-syntax)


[warning] 175-175: Expected an expression

(invalid-syntax)


[warning] 184-184: Expected an expression

(invalid-syntax)


[warning] 184-184: Expected an expression

(invalid-syntax)


[warning] 188-188: Expected an expression

(invalid-syntax)


[warning] 188-188: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 188-188: Expected ,, found name

(invalid-syntax)


[warning] 188-188: Expected an expression

(invalid-syntax)


[warning] 190-190: Expected an expression

(invalid-syntax)


[warning] 190-190: Expected an expression

(invalid-syntax)


[warning] 191-191: Expected an expression

(invalid-syntax)


[warning] 191-191: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 191-191: Expected ,, found name

(invalid-syntax)


[warning] 191-191: Expected an expression

(invalid-syntax)


[warning] 193-193: Expected an expression

(invalid-syntax)


[warning] 193-193: Expected an expression

(invalid-syntax)


[warning] 250-250: Expected an expression

(invalid-syntax)


[warning] 250-250: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 250-250: Expected ,, found name

(invalid-syntax)


[warning] 250-250: Expected an expression

(invalid-syntax)


[warning] 251-251: Expected ,, found string

(invalid-syntax)


[warning] 251-251: Expected ,, found :

(invalid-syntax)


[warning] 255-255: Expected an expression

(invalid-syntax)


[warning] 255-255: Expected an expression

(invalid-syntax)


[warning] 256-256: Expected ,, found {

(invalid-syntax)


[warning] 256-256: Expected an expression

(invalid-syntax)


[warning] 256-256: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 256-256: Expected ,, found name

(invalid-syntax)


[warning] 256-256: Expected an expression

(invalid-syntax)


[warning] 257-257: Expected ,, found string

(invalid-syntax)


[warning] 257-257: Expected ,, found :

(invalid-syntax)


[warning] 262-262: Expected an expression

(invalid-syntax)


[warning] 262-262: Expected an expression

(invalid-syntax)


[warning] 263-263: Expected ,, found {

(invalid-syntax)


[warning] 263-263: Expected an expression

(invalid-syntax)


[warning] 263-263: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 263-263: Expected ,, found name

(invalid-syntax)


[warning] 263-263: Expected an expression

(invalid-syntax)


[warning] 264-264: Expected ,, found string

(invalid-syntax)


[warning] 264-264: Expected ,, found :

(invalid-syntax)


[warning] 268-268: Expected an expression

(invalid-syntax)


[warning] 268-268: Expected an expression

(invalid-syntax)


[warning] 269-269: Expected ,, found {

(invalid-syntax)


[warning] 269-269: Expected an expression

(invalid-syntax)


[warning] 269-269: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 269-269: Expected ,, found name

(invalid-syntax)


[warning] 269-269: Expected an expression

(invalid-syntax)


[warning] 270-270: Expected ,, found string

(invalid-syntax)


[warning] 270-270: Expected ,, found :

(invalid-syntax)


[warning] 276-276: Expected ,, found :

(invalid-syntax)


[warning] 284-284: Expected ,, found :

(invalid-syntax)


[warning] 290-290: Expected ,, found :

(invalid-syntax)


[warning] 296-296: Expected an expression

(invalid-syntax)


[warning] 296-296: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 296-296: Expected ,, found name

(invalid-syntax)


[warning] 296-296: Expected an expression

(invalid-syntax)


[warning] 297-297: Expected ,, found string

(invalid-syntax)


[warning] 297-297: Expected ,, found :

(invalid-syntax)


[warning] 303-303: Expected an expression

(invalid-syntax)


[warning] 303-303: Expected an expression

(invalid-syntax)


[warning] 304-304: Expected ,, found {

(invalid-syntax)


[warning] 304-304: Expected an expression

(invalid-syntax)


[warning] 304-304: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 304-304: Expected ,, found name

(invalid-syntax)


[warning] 304-304: Expected an expression

(invalid-syntax)


[warning] 305-305: Expected ,, found string

(invalid-syntax)


[warning] 305-305: Expected ,, found :

(invalid-syntax)


[warning] 310-310: Expected an expression

(invalid-syntax)


[warning] 310-310: Expected an expression

(invalid-syntax)


[warning] 311-311: Expected ,, found {

(invalid-syntax)


[warning] 311-311: Expected an expression

(invalid-syntax)


[warning] 311-311: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 311-311: Expected ,, found name

(invalid-syntax)


[warning] 311-311: Expected an expression

(invalid-syntax)


[warning] 312-312: Expected ,, found string

(invalid-syntax)


[warning] 312-312: Expected ,, found :

(invalid-syntax)


[warning] 317-317: Expected an expression

(invalid-syntax)


[warning] 317-317: Expected an expression

(invalid-syntax)


[warning] 318-318: Expected ,, found {

(invalid-syntax)


[warning] 318-318: Expected an expression

(invalid-syntax)


[warning] 318-318: Expected an expression

(invalid-syntax)


[warning] 319-319: Expected ,, found {

(invalid-syntax)


[warning] 319-319: Expected an expression

(invalid-syntax)


[warning] 319-319: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 319-319: Expected ,, found name

(invalid-syntax)


[warning] 319-319: Expected an expression

(invalid-syntax)


[warning] 320-320: Expected ,, found string

(invalid-syntax)


[warning] 320-320: Expected ,, found :

(invalid-syntax)


[warning] 324-324: Expected an expression

(invalid-syntax)


[warning] 324-324: Expected an expression

(invalid-syntax)


[warning] 327-327: Expected an expression

(invalid-syntax)


[warning] 327-327: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 327-327: Expected ,, found name

(invalid-syntax)


[warning] 327-327: Expected an expression

(invalid-syntax)


[warning] 329-329: Expected an expression

(invalid-syntax)


[warning] 329-329: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 329-329: Expected ,, found name

(invalid-syntax)


[warning] 329-329: Expected an expression

(invalid-syntax)


[warning] 331-331: Expected ,, found string

(invalid-syntax)


[warning] 331-331: Expected ,, found :

(invalid-syntax)


[warning] 332-332: Expected an expression

(invalid-syntax)


[warning] 332-332: Expected an expression

(invalid-syntax)


[warning] 333-333: Expected ,, found {

(invalid-syntax)


[warning] 333-333: Expected an expression

(invalid-syntax)


[warning] 333-333: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 333-333: Expected ,, found name

(invalid-syntax)


[warning] 333-333: Expected an expression

(invalid-syntax)


[warning] 335-335: Expected ,, found string

(invalid-syntax)


[warning] 335-335: Expected ,, found :

(invalid-syntax)


[warning] 336-336: Expected ,, found :

(invalid-syntax)


[warning] 339-339: Expected an expression

(invalid-syntax)


[warning] 339-339: Expected an expression

(invalid-syntax)


[warning] 340-340: Expected ,, found {

(invalid-syntax)


[warning] 340-340: Expected an expression

(invalid-syntax)


[warning] 340-340: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 340-340: Expected ,, found name

(invalid-syntax)


[warning] 340-340: Expected an expression

(invalid-syntax)


[warning] 342-342: Expected ,, found string

(invalid-syntax)


[warning] 342-342: Expected ,, found :

(invalid-syntax)


[warning] 343-343: Expected ,, found :

(invalid-syntax)


[warning] 346-346: Expected an expression

(invalid-syntax)


[warning] 346-346: Expected an expression

(invalid-syntax)


[warning] 349-349: Expected an expression

(invalid-syntax)


[warning] 349-349: Expected an expression

(invalid-syntax)


[warning] 378-378: Expected an expression

(invalid-syntax)


[warning] 378-378: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 378-378: Expected ,, found name

(invalid-syntax)


[warning] 378-378: Expected an expression

(invalid-syntax)


[warning] 379-379: Expected :, found string

(invalid-syntax)


[warning] 379-379: Expected ,, found :

(invalid-syntax)


[warning] 379-379: Expected :, found ,

(invalid-syntax)


[warning] 380-380: Expected an expression

(invalid-syntax)


[warning] 380-380: Expected an expression

(invalid-syntax)


[warning] 381-381: Expected :, found {

(invalid-syntax)


[warning] 381-381: Expected an expression

(invalid-syntax)


[warning] 381-381: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 381-381: Expected ,, found name

(invalid-syntax)


[warning] 381-381: Expected an expression

(invalid-syntax)


[warning] 382-382: Expected ,, found string

(invalid-syntax)


[warning] 383-383: Expected an expression

(invalid-syntax)


[warning] 383-383: Expected an expression

(invalid-syntax)


[warning] 384-384: Expected :, found {

(invalid-syntax)


[warning] 384-384: Expected an expression

(invalid-syntax)


[warning] 384-384: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 384-384: Expected ,, found name

(invalid-syntax)


[warning] 384-384: Expected an expression

(invalid-syntax)


[warning] 385-385: Expected ,, found string

(invalid-syntax)


[warning] 386-386: Expected an expression

(invalid-syntax)


[warning] 386-386: Expected an expression

(invalid-syntax)


[warning] 387-387: Expected :, found {

(invalid-syntax)


[warning] 387-387: Expected an expression

(invalid-syntax)


[warning] 387-387: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 387-387: Expected ,, found name

(invalid-syntax)


[warning] 387-387: Expected an expression

(invalid-syntax)


[warning] 388-388: Expected ,, found string

(invalid-syntax)


[warning] 389-389: Expected an expression

(invalid-syntax)


[warning] 389-389: Expected an expression

(invalid-syntax)


[warning] 390-390: Expected :, found {

(invalid-syntax)


[warning] 390-390: Expected an expression

(invalid-syntax)


[warning] 390-390: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 390-390: Expected ,, found name

(invalid-syntax)


[warning] 390-390: Expected an expression

(invalid-syntax)


[warning] 391-391: Expected ,, found string

(invalid-syntax)


[warning] 392-392: Expected an expression

(invalid-syntax)


[warning] 392-392: Expected an expression

(invalid-syntax)


[warning] 393-393: Expected :, found }

(invalid-syntax)


[warning] 396-396: Expected an expression

(invalid-syntax)


[warning] 396-396: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 396-396: Expected ,, found name

(invalid-syntax)


[warning] 396-396: Expected an expression

(invalid-syntax)


[warning] 398-398: Expected an expression

(invalid-syntax)


[warning] 398-398: Expected an expression

(invalid-syntax)


[warning] 418-418: Expected an expression

(invalid-syntax)


[warning] 418-418: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 418-418: Expected ,, found name

(invalid-syntax)


[warning] 418-418: Expected an expression

(invalid-syntax)


[warning] 420-420: Expected an expression

(invalid-syntax)


[warning] 420-420: Expected an expression

(invalid-syntax)


[warning] 432-432: Expected an expression

(invalid-syntax)


[warning] 432-432: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 432-432: Expected ,, found name

(invalid-syntax)


[warning] 432-432: Expected an expression

(invalid-syntax)


[warning] 434-434: Expected an expression

(invalid-syntax)


[warning] 434-434: Expected an expression

(invalid-syntax)


[warning] 440-440: Expected an expression

(invalid-syntax)


[warning] 440-440: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 440-440: Expected ,, found name

(invalid-syntax)


[warning] 440-440: Expected an expression

(invalid-syntax)


[warning] 442-442: Expected an expression

(invalid-syntax)


[warning] 442-442: Expected an identifier, but found a keyword else that cannot be used here

(invalid-syntax)


[warning] 442-442: Expected an expression

(invalid-syntax)


[warning] 444-444: Expected an expression

(invalid-syntax)


[warning] 444-444: Expected an expression

(invalid-syntax)


[warning] 495-495: Expected an expression

(invalid-syntax)


[warning] 495-495: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 495-495: Expected ,, found name

(invalid-syntax)


[warning] 495-495: Expected an expression

(invalid-syntax)


[warning] 496-496: Expected :, found string

(invalid-syntax)


[warning] 496-496: Expected ,, found :

(invalid-syntax)


[warning] 500-500: Expected :, found ,

(invalid-syntax)


[warning] 501-501: Expected an expression

(invalid-syntax)


[warning] 501-501: Expected an expression

(invalid-syntax)


[warning] 502-502: Expected :, found }

(invalid-syntax)


[warning] 509-509: Expected an expression

(invalid-syntax)


[warning] 509-509: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 509-509: Expected ,, found name

(invalid-syntax)


[warning] 509-509: Expected an expression

(invalid-syntax)


[warning] 510-510: Expected ,, found string

(invalid-syntax)


[warning] 511-511: Expected an expression

(invalid-syntax)


[warning] 511-511: Expected an expression

(invalid-syntax)


[warning] 534-534: Expected an expression

(invalid-syntax)


[warning] 534-534: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 534-534: Expected ,, found name

(invalid-syntax)


[warning] 534-534: Expected an expression

(invalid-syntax)


[warning] 536-536: Expected an expression

(invalid-syntax)


[warning] 536-536: Expected an expression

(invalid-syntax)


[warning] 538-538: Expected an expression

(invalid-syntax)


[warning] 538-538: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 538-538: Expected ,, found name

(invalid-syntax)


[warning] 538-538: Expected an expression

(invalid-syntax)


[warning] 550-550: Expected an expression

(invalid-syntax)


[warning] 550-550: Expected an expression

(invalid-syntax)


[warning] 552-552: Expected an expression

(invalid-syntax)


[warning] 552-552: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 552-552: Expected ,, found name

(invalid-syntax)


[warning] 552-552: Expected an expression

(invalid-syntax)


[warning] 553-553: Invalid assignment target

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 553-553: Expected ,, found name

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an identifier, but found a keyword else that cannot be used here

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 554-554: Expected an expression

(invalid-syntax)


[warning] 554-554: Expected an expression

(invalid-syntax)


[warning] 556-556: Expected an expression

(invalid-syntax)


[warning] 556-556: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 556-556: Expected ,, found name

(invalid-syntax)


[warning] 556-556: Expected an expression

(invalid-syntax)


[warning] 558-558: Expected an expression

(invalid-syntax)


[warning] 558-558: Expected an expression

(invalid-syntax)


[warning] 560-560: Expected an expression

(invalid-syntax)


[warning] 560-560: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 560-560: Expected ,, found name

(invalid-syntax)


[warning] 560-560: Expected an expression

(invalid-syntax)


[warning] 568-568: Expected an expression

(invalid-syntax)


[warning] 568-568: Expected an expression

(invalid-syntax)

⏰ 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: CodeQL analysis (python)
  • GitHub Check: Agent
🔇 Additional comments (30)
.flake8 (1)

1-11: LGTM — exclude list looks reasonable.

pyproject.toml (1)

4-10: Keep Flake8 excludes consistent across configs.

.flake8 excludes templates/**, but [tool.flake8] does not (Line 5–10). If flake8 is run via flake8-pyproject or another tool that reads pyproject.toml, Jinja templates may still be linted, reintroducing the false positives this PR aims to avoid. Please align the lists or remove the duplicate config.

🔧 Suggested alignment (option A: mirror .flake8)
 [tool.flake8]
 exclude = [
   ".venv",
+  ".venv/*",
+  "**/.venv/*",
   "venv",
+  "venv/*",
+  "**/venv/*",
   "__pycache__",
   ".git",
+  "templates/**",
 ]
tasks/apt.yml (1)

1-169: LGTM — formatting-only change set.

tasks/main.yml (1)

1-43: LGTM — no semantic changes detected.

tasks/ssh.yml (1)

1-32: LGTM — formatting-only update.

tasks/supervisor.yml (1)

1-72: LGTM — quoting modes is fine; no behavior change.

meta/main.yml (1)

1-37: LGTM — formatting-only change set.

.github/FUNDING.yml (1)

1-16: LGTM!

Adding the YAML document start marker (---) is a good practice for YAML files and aligns with the PR's goal of fixing formatting issues detected by QA checks.

tasks/django.yml (1)

1-237: LGTM on overall structure.

The Django task file is well-organized with clear task names, appropriate use of handlers, tags, and conditionals. The logic flow for Django setup, migrations, and static file handling is correct.

run-qa-checks (1)

1-4: LGTM on the script logic.

The simplified script correctly:

  • Uses set -e to exit immediately on any error
  • Runs the full QA check suite without skip flags
  • Avoids error suppression constructs like || true

This aligns well with the PR objective to ensure lint/QA failures are no longer silently ignored.

molecule/local/molecule.yml (1)

1-56: LGTM!

The removal of the lint block from the Molecule configuration aligns with the PR objective to centralize lint checks in the run-qa-checks script. This ensures lint errors are properly enforced through the dedicated QA script rather than being potentially bypassed in the Molecule configuration.

The platform definitions, provisioner settings, and verifier configuration are well-structured.

tasks/django_secret_key.yml (1)

1-27: LGTM!

The file properly:

  • Uses quoted mode values ("0754", "0600") which is the recommended practice for Ansible
  • Implements idempotent secret key generation with the creates argument
  • Sets appropriately restrictive permissions (0600) on the secret key file
  • Uses changed_when: false for the read-only command
docs/developer/installation.rst (1)

103-121: LGTM! Clear and helpful documentation addition.

The new "How to Run QA checks" section provides valuable guidance for contributors. The instructions align with the simplified run-qa-checks script.

One minor consideration: ansible-lint --fix only auto-fixes ansible-lint issues. If developers encounter yamllint or flake8 errors, they may need different remediation steps. You might optionally expand the troubleshooting guidance to mention this.

.ansible-lint (1)

1-9: Formatting-only change looks good.
No behavioral impact detected.

vars/debian-13.yml (1)

1-7: Formatting-only change looks good.

tasks/complete.yml (1)

1-14: Formatting-only change looks good.

molecule/resources/requirements.yml (1)

1-8: Formatting-only change looks good.

tasks/system.yml (1)

1-14: Formatting-only change looks good.

molecule/resources/converge.yml (1)

1-42: Formatting-only change looks good.

.yamllint.yml (1)

1-36: LGTM: ruleset expansion looks consistent.
The added rules and ignore patterns are clear and cohesive with the goal of stricter YAML hygiene.

molecule/default/molecule.yml (1)

1-36: LGTM: Molecule config simplification aligns with centralized QA.
Moving lint out and keeping platforms top-level is clean and consistent with a single QA entrypoint.

templates/openwisp2/settings.py (1)

1-23: Linter exclusion for this file is already properly configured. The .flake8 configuration explicitly excludes templates/** (line 11), which covers this Jinja-templated settings module and prevents linting errors.

molecule/resources/verify.yml (1)

50-54: This is test code that uses self-signed certificates intentionally generated during the test setup. In a Molecule CI environment with self-signed certificates, validate_certs: false is necessary and standard practice—making it configurable would break the tests unnecessarily.

Likely an incorrect or invalid review comment.

tasks/freeradius_eap.yml (1)

1-77: LGTM!

The task file is well-structured with consistent use of:

  • Quoted mode values ("0644")
  • Proper handler notifications for freeradius restarts
  • Appropriate tags on all tasks
  • Block-level notify for the DH certificate generation group
.github/workflows/ci.yml (1)

52-67: Improved CI flow aligns with PR objectives.

Good changes:

  • QA checks no longer have continue-on-error, ensuring failures are properly reported
  • Tests step now has explicit conditional !cancelled() && steps.deps.conclusion == 'success' which ensures tests only run when dependencies are successfully installed

This correctly addresses the issue of QA/lint errors being silently ignored.

defaults/main.yml (2)

30-38: LGTM - Nested dictionary structure.

The conversion to nested dictionaries is cleaner and allows natural dot notation access (e.g., openwisp2_database.engine, openwisp2_database.options.timeout). This is consistent with Ansible best practices for structured data.


1-236: Well-structured defaults file.

The restructured YAML is consistent throughout:

  • Database configurations use proper nested mappings
  • Null values for optional settings are correctly represented
  • All sections follow the same structural pattern
tasks/pip.yml (2)

117-129: Consider adding a condition for the copy task as well.

The copy task at line 113-116 will always execute, but the pip install task at line 119-129 only runs when CORS is enabled. This leaves an orphaned requirements file when CORS is disabled.

However, looking more closely, line 117 already has the when condition on the copy task, so this is correctly implemented.


1-233: LGTM - Well-structured pip installation tasks.

The tasks are properly implemented with:

  • Consistent retry/delay patterns for network resilience
  • Appropriate conditional execution based on feature flags
  • Correct use of nested variable access (e.g., openwisp2_database.engine)
  • Proper idempotence tags where needed
tasks/freeradius.yml (1)

89-108: LGTM - FreeRADIUS package installation.

The package installation tasks are well-structured with:

  • Conditional installation based on SQL dialect
  • Proper handler notifications for freeradius restarts
  • Consistent state: latest usage

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

- package-ecosystem: "pip" # Check for Python package updates
directory: "/" # The root directory where the Ansible role is located
schedule:
interval: "monthly" # Check for updates weekly
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Comment is inconsistent with configuration.

The comment says "Check for updates weekly" but the interval is set to "monthly". Consider updating the comment to match the actual configuration.

📝 Suggested fix
     schedule:
-      interval: "monthly" # Check for updates weekly
+      interval: "monthly" # Check for updates monthly
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interval: "monthly" # Check for updates weekly
interval: "monthly" # Check for updates monthly
🤖 Prompt for AI Agents
In @.github/dependabot.yml at line 7, The inline comment is inconsistent with
the dependabot setting: the key interval: "monthly" does not match the comment
"Check for updates weekly"; update the comment to reflect the actual
configuration (change the comment to "Check for updates monthly") or, if weekly
updates are intended, change the interval value to "weekly" so the comment and
the interval: "monthly" setting for dependabot are consistent; edit the line
containing interval: "monthly" and its adjacent comment accordingly.

- package-ecosystem: "github-actions" # Check for GitHub Actions updates
directory: "/" # The root directory where the Ansible role is located
schedule:
interval: "monthly" # Check for updates weekly
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same inconsistency here.

The comment says "weekly" but the interval is "monthly".

📝 Suggested fix
     schedule:
-      interval: "monthly" # Check for updates weekly
+      interval: "monthly" # Check for updates monthly
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interval: "monthly" # Check for updates weekly
schedule:
interval: "monthly" # Check for updates monthly
🤖 Prompt for AI Agents
In @.github/dependabot.yml at line 13, The configuration's interval value
doesn't match its inline comment: update the interval setting named "interval"
so it matches the intended cadence in the comment (change "monthly" to "weekly")
or adjust the comment to reflect the actual value; specifically edit the
"interval" entry in the dependabot configuration so the value and the comment
are consistent.

Comment on lines +38 to +41
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: "3.12"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Undefined matrix variable in step name.

The step name references matrix.python-version but the matrix only defines distro. This won't break the workflow but will display incorrectly in the GitHub Actions UI.

Proposed fix
-      - name: Set up Python ${{ matrix.python-version }}
+      - name: Set up Python 3.12
         uses: actions/setup-python@v6
         with:
           python-version: "3.12"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"
🧰 Tools
🪛 actionlint (1.7.10)

[error] 38-38: property "python-version" is not defined in object type {distro: string}

(expression)

🤖 Prompt for AI Agents
In @.github/workflows/ci.yml around lines 38 - 41, The step name uses an
undefined matrix variable "matrix.python-version" which should be corrected to
match the defined matrix value; update the step name "Set up Python ${{
matrix.python-version }}" to reference the actual matrix key (e.g., "Set up
Python ${{ matrix.distro }}" or simply "Set up Python") or alternatively add a
"python-version" entry to the matrix so the interpolation matches; locate the
workflow step with the name string and make the name and matrix keys consistent.

Comment on lines +48 to +55
- name: Migrate timeseries database
become: true
become_user: "{{ www_user }}"
community.general.django_manage:
app_path: "{{ openwisp2_path }}"
command: migrate_timeseries
virtualenv: "{{ virtualenv_path }}"
when: openwisp2_monitoring
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all Ansible requirements files
fd -e yml -e yaml | grep -E '(requirements|collections)' | head -20

Repository: openwisp/ansible-openwisp2

Length of output: 105


🏁 Script executed:

#!/bin/bash
# Search for community.general in the entire repository
rg 'community\.general' -A 1 -B 1

Repository: openwisp/ansible-openwisp2

Length of output: 791


🏁 Script executed:

#!/bin/bash
# Check content of molecule resources requirements file
cat molecule/resources/requirements.yml

Repository: openwisp/ansible-openwisp2

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Look for requirements files at root and common locations
fd -e yml -e yaml -path '*/requirements*' --max-results 10

Repository: openwisp/ansible-openwisp2

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Check if there's a requirements.yml at the root
if [ -f requirements.yml ]; then
  cat requirements.yml
else
  echo "No requirements.yml at root"
fi

Repository: openwisp/ansible-openwisp2

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Search for any collections configuration
rg 'collections:' -B 2 -A 5 --type yaml

Repository: openwisp/ansible-openwisp2

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check if there's an ansible.cfg or galaxy.yml
ls -la | grep -E '(ansible\.cfg|galaxy\.yml|collections)'

Repository: openwisp/ansible-openwisp2

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Check root directory for any requirements or config files
find . -maxdepth 1 -type f \( -name '*.yml' -o -name '*.yaml' -o -name 'requirements*' -o -name 'ansible.cfg' \)

Repository: openwisp/ansible-openwisp2

Length of output: 109


🏁 Script executed:

#!/bin/bash
# Check for any file with 'requirements' or 'collections' in its name
find . -type f \( -name '*requirements*' -o -name '*collections*' \) | grep -v '.git' | head -20

Repository: openwisp/ansible-openwisp2

Length of output: 335


🏁 Script executed:

#!/bin/bash
# Check the requirements-test.txt to see if it has Ansible collections info
cat requirements-test.txt

Repository: openwisp/ansible-openwisp2

Length of output: 183


🏁 Script executed:

#!/bin/bash
# Check if there's a galaxy.yml or any collections declaration
find . -name 'galaxy.yml' -o -name '*collections*' -type f | head -10

Repository: openwisp/ansible-openwisp2

Length of output: 52


🏁 Script executed:

#!/bin/bash
# Let's look at the complete structure of molecule directory
ls -la molecule/

Repository: openwisp/ansible-openwisp2

Length of output: 351


🏁 Script executed:

#!/bin/bash
# Check if molecule/resources/requirements.yml should include collections
head -30 molecule/resources/requirements.yml

Repository: openwisp/ansible-openwisp2

Length of output: 284


🏁 Script executed:

#!/bin/bash
# Check the README to understand how this repository is meant to be used
if [ -f README.md ]; then
  head -100 README.md
else
  echo "No README.md found"
fi

Repository: openwisp/ansible-openwisp2

Length of output: 995


🏁 Script executed:

#!/bin/bash
# Check the user documentation that mentioned community.general
cat docs/user/quickstart.rst | head -50

Repository: openwisp/ansible-openwisp2

Length of output: 1624


🏁 Script executed:

#!/bin/bash
# Continue reading the quickstart to find the collections section
cat docs/user/quickstart.rst | grep -A 20 "community.general"

Repository: openwisp/ansible-openwisp2

Length of output: 1387


🏁 Script executed:

#!/bin/bash
# Check if there's a main.yml playbook or similar that might have collection requirements
find . -name 'main.yml' -o -name 'site.yml' -o -name 'playbook.yml' | head -10

Repository: openwisp/ansible-openwisp2

Length of output: 158


🏁 Script executed:

#!/bin/bash
# Check if there's a collections requirement in meta/main.yml or similar
find . -path '*/meta/*' -name '*.yml' -o -path '*/meta/*' -name '*.yaml'

Repository: openwisp/ansible-openwisp2

Length of output: 85


🏁 Script executed:

#!/bin/bash
# Check meta/main.yml for collections declaration
cat meta/main.yml

Repository: openwisp/ansible-openwisp2

Length of output: 1133


Add community.general to the role's collection requirements.

The community.general collection is used by the django_manage module but is not declared in a machine-readable requirements format within the role. While the user documentation mentions manual installation via ansible-galaxy collection install "community.general:>=3.6.0", users might miss this step. Add a collections declaration to meta/main.yml under galaxy_info to ensure the requirement is enforced:

galaxy_info:
  collections:
    - name: community.general
      version: ">=3.6.0"
    - name: ansible.posix

This prevents runtime failures when the handler is executed.

🤖 Prompt for AI Agents
In `@handlers/main.yml` around lines 48 - 55, Add a machine-readable collection
requirement so the play using community.general.django_manage always has the
collection installed: update the role metadata by adding a
galaxy_info.collections entry that declares community.general with version
">=3.6.0" (and also add ansible.posix) under the role's meta configuration so
the dependency is enforced for handlers that call the django_manage module;
ensure the collections list includes the names and version constraint exactly as
specified.

Comment on lines +3 to +5
[![Ansible-OpenWISP2 CI Build](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml/badge.svg)](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml)
[![Galaxy](http://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square)](https://galaxy.ansible.com/ui/standalone/roles/openwisp/openwisp2/)
[![Chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/openwisp/general)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use HTTPS for the Galaxy badge image URL.
The http badge can trigger mixed‑content warnings and allows MITM on the image. Switch to https for consistency with the other badges.

✅ Suggested fix
-[![Galaxy](http://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square)](https://galaxy.ansible.com/ui/standalone/roles/openwisp/openwisp2/)
+[![Galaxy](https://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square)](https://galaxy.ansible.com/ui/standalone/roles/openwisp/openwisp2/)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[![Ansible-OpenWISP2 CI Build](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml/badge.svg)](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml)
[![Galaxy](http://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square)](https://galaxy.ansible.com/ui/standalone/roles/openwisp/openwisp2/)
[![Chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/openwisp/general)
[![Ansible-OpenWISP2 CI Build](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml/badge.svg)](https://github.com/openwisp/ansible-openwisp2/actions/workflows/ci.yml)
[![Galaxy](https://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square)](https://galaxy.ansible.com/ui/standalone/roles/openwisp/openwisp2/)
[![Chat](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/openwisp/general)
🤖 Prompt for AI Agents
In `@README.md` around lines 3 - 5, Replace the insecure HTTP Galaxy badge image
URL with HTTPS: find the markdown line starting with the "[![Galaxy]" badge (the
image URL currently
"http://img.shields.io/badge/galaxy-openwisp.openwisp2-blue.svg?style=flat-square")
and update it to use "https://", ensuring the badge image is loaded securely
like the other badges.

Comment on lines +5 to +7
cron:
name: "Update toplogies"
day: "{{ openwisp2_topology_update_frequency.day }}"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in cron name to avoid confusing crontab entries.
“toplogies” should be “topologies”.

📝 Proposed fix
-    name: "Update toplogies"
+    name: "Update topologies"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cron:
name: "Update toplogies"
day: "{{ openwisp2_topology_update_frequency.day }}"
cron:
name: "Update topologies"
day: "{{ openwisp2_topology_update_frequency.day }}"
🤖 Prompt for AI Agents
In `@tasks/cron.yml` around lines 5 - 7, Update the typo in the cron job name:
change the value of the cron.name string "Update toplogies" to "Update
topologies" so the cron entry is correctly spelled; edit the cron mapping where
the name key is defined (cron -> name) to use the corrected spelling.

Comment on lines 195 to 201
- name: Copy configuration for WPA Enterprise TTLS
include_tasks: tasks/freeradius_eap.yml
loop: "{{ freeradius_eap_orgs }}"
loop_control:
loop_var: org
when: freeradius_eap_orgs | length > 0
tags: [freeradius_eap]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the freeradius_eap.yml file
echo "=== Searching for freeradius_eap.yml ==="
fd -t f "freeradius_eap.yml" 

# Check the directory structure around tasks/freeradius.yml
echo -e "\n=== Directory structure ==="
fd -t f "freeradius" --type f | head -20

# Check all include_tasks usages in YAML files
echo -e "\n=== include_tasks patterns in codebase ==="
rg "include_tasks:" --type yaml -B1 -A1 | head -50

Repository: openwisp/ansible-openwisp2

Length of output: 448


🏁 Script executed:

#!/bin/bash
# Check the full context of the include_tasks and any variables
echo "=== Full context of freeradius.yml around line 195-201 ==="
sed -n '190,210p' tasks/freeradius.yml

# Look for all include_tasks in the entire codebase (not just in tasks/)
echo -e "\n=== All include_tasks usages in YAML files ==="
rg "include_tasks:" --type yaml -B1 -A2

# Check if there's a role structure
echo -e "\n=== Check for role structure ==="
ls -la | head -20
cat meta/main.yml 2>/dev/null || echo "No meta/main.yml found"

Repository: openwisp/ansible-openwisp2

Length of output: 3026


🏁 Script executed:

#!/bin/bash
# Look for test files or CI/CD that might validate this
echo "=== Looking for tests or CI validation ==="
find . -name "*.yml" -o -name "*.yaml" | grep -E "(test|spec|molecule)" | head -20

# Check if there are any error logs or issues mentioning this path
echo -e "\n=== Check CHANGES.md for any recent fixes ==="
grep -i "include_tasks\|freeradius_eap" CHANGES.md

# Look for any documentation about path resolution
echo -e "\n=== Check if role is being tested somewhere ==="
find . -type f -name "*.yml" | xargs grep -l "openwisp2" | head -10

Repository: openwisp/ansible-openwisp2

Length of output: 728


🏁 Script executed:

#!/bin/bash
# Check the molecule converge file
echo "=== Molecule converge.yml ==="
cat molecule/resources/converge.yml

# Check if freeradius_eap_orgs is set in tests
echo -e "\n=== Check for freeradius_eap_orgs in test files ==="
rg "freeradius_eap_orgs" --type yaml

Repository: openwisp/ansible-openwisp2

Length of output: 1400


🏁 Script executed:

#!/bin/bash
# Check if there's a CI workflow that runs these tests
echo "=== Check CI workflow ==="
cat .github/workflows/ci.yml | head -100

# Look for any recent commits or issues about this path
echo -e "\n=== Git log for freeradius.yml ==="
git log --oneline tasks/freeradius.yml | head -10

# Check if the path works by looking at the actual file
echo -e "\n=== Verify include_tasks behavior in Ansible docs ==="
echo "According to Ansible documentation, include_tasks paths in roles are relative to the role's tasks/ directory"

# Let's check if there are any reports or issues about this
echo -e "\n=== Search for any error handling or workarounds ==="
rg "tasks/freeradius_eap" --type yaml -B2 -A2

Repository: openwisp/ansible-openwisp2

Length of output: 2862


🌐 Web query:

Ansible include_tasks path resolution relative to role tasks directory

💡 Result:

include_tasks resolves its file as a local path on the control node using Ansible’s task search path (“search magic”).

What “relative” means inside a role

  • If you are in a role task file (for example roles/myrole/tasks/main.yml) and you do:

    - ansible.builtin.include_tasks: other.yml

    Ansible will find roles/myrole/tasks/other.yml (role context has highest precedence). (docs.ansible.com)

  • If you include from a task file in a subdirectory (for example roles/myrole/tasks/setup/main.yml) and do:

    - ansible.builtin.include_tasks: common.yml

    it will first try relative to the including task file’s directory (so roles/myrole/tasks/setup/common.yml), then fall back through the stacked contexts. (docs.ansible.com)

Parent-role / caller-role behavior (often the surprise)

Ansible only treats another role as a “parent role” in the search stack if the current role was entered via include_role, import_role, or role dependencies—not merely because the role was listed under roles: in the play. This is a common reason a “callback.yml in the parent role” isn’t found. (docs.ansible.com)

If you want it unambiguous

Use an explicit path anchored to the current role:

- ansible.builtin.include_tasks: "{{ role_path }}/tasks/other.yml"

(role_path points at the role root, so you add /tasks/... yourself.)

Debugging

Check ansible_search_path (and/or run with -vvvvv) to see exactly where Ansible searched. (docs.ansible.com)

Citations:


Simplify the include_tasks path to idiomatic form.

When including tasks from the same role directory, use just the filename:

include_tasks: freeradius_eap.yml

In Ansible roles, include_tasks paths within the tasks/ directory should reference only the filename, not the directory prefix. Using tasks/freeradius_eap.yml is non-idiomatic and relies on fallback search path resolution rather than explicit pathing.

🤖 Prompt for AI Agents
In `@tasks/freeradius.yml` around lines 195 - 201, Replace the non-idiomatic
include_tasks path "tasks/freeradius_eap.yml" with the filename only
"freeradius_eap.yml" in the task that loops over freeradius_eap_orgs (the task
with include_tasks, loop: "{{ freeradius_eap_orgs }}", loop_control.loop_var:
org and tags: [freeradius_eap]) so the role includes the task file from the
role’s tasks/ directory in the standard Ansible way; keep the loop, loop_control
and when condition unchanged.

Comment on lines +26 to +36
- name: Create SSL cert if not exists yet
command: >
openssl req -new -nodes -x509 \
-subj "/C={{ openwisp2_ssl_country }}/ST={{ openwisp2_ssl_state }} \
/L={{ openwisp2_ssl_locality }}/O={{ openwisp2_ssl_organization }} \
/CN={{ openwisp2_ssl_common_name }}" \
-days 3650 \
-keyout {{ openwisp2_ssl_key }} \
-out {{ openwisp2_ssl_cert }} \
-extensions v3_ca creates={{ openwisp2_ssl_cert }}
notify: Restart nginx
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "nginx.yml" -type f | head -20

Repository: openwisp/ansible-openwisp2

Length of output: 87


🏁 Script executed:

git ls-files | grep -i nginx

Repository: openwisp/ansible-openwisp2

Length of output: 215


🏁 Script executed:

sed -n '26,36p' tasks/nginx.yml

Repository: openwisp/ansible-openwisp2

Length of output: 543


🏁 Script executed:

head -40 tasks/nginx.yml | tail -20

Repository: openwisp/ansible-openwisp2

Length of output: 748


🌐 Web query:

Ansible command module creates parameter syntax idempotency

💡 Result:

The ansible.builtin.command module is not inherently idempotent (it will run every time). To make it effectively idempotent, use creates and/or removes guards. Ansible checks these paths on the remote host before deciding whether to run the command. [1][2]

creates (skip if file already exists)

  • Meaning: Run the command only if the file/glob does not exist.
  • Order: creates is checked before removes. [1]
- name: Initialize app (only once)
  ansible.builtin.command:
    cmd: /usr/local/bin/init_app --db
    creates: /var/lib/myapp/.initialized

removes (run only if file exists)

  • Meaning: Run the command only if the file/glob does exist. [1]
- name: Cleanup legacy file (only if present)
  ansible.builtin.command:
    cmd: /usr/local/bin/cleanup_legacy
    removes: /opt/legacy/.installed

“Inline” syntax (legacy style) also works

Docs note you can place these “after the command” in free-form style: [1][2]

- ansible.builtin.command: /usr/local/bin/init_app --db creates=/var/lib/myapp/.initialized

Check mode behavior

In --check mode, command can only predict changed vs ok when creates or removes is provided; otherwise the task is skipped in check mode. [1]

Tip: Prefer a purpose-built idempotent module (e.g., file, package, user, etc.) when one exists; use command with creates/removes mainly as a last resort. [1]

Sources: Ansible command module docs. [1][2]


🏁 Script executed:

python3 << 'EOF'
# Let's understand how Ansible command module parses the command string with creates
# The 'command' module in Ansible has these key parameters:
# - cmd/command: the command to execute
# - creates: if this file exists, the command is skipped
# - removes: if this file doesn't exist, the command is skipped
# - chdir, env, stdin, etc.

# The issue is that when you write:
# command: >
#   openssl req ... -extensions v3_ca creates={{ openwisp2_ssl_cert }}
#
# Ansible treats everything after "command:" as the actual shell command string
# So "creates=..." becomes part of the openssl command itself, not an Ansible module argument

# According to Ansible docs, creates should be at the same indentation level as 'command:'
print("Issue Verification:")
print("==================")
print("✓ The 'creates' parameter MUST be a module argument (at same level as 'command:')")
print("✓ When appended to the command string, it's passed literally to openssl")
print("✓ This breaks idempotency because Ansible won't check file existence")
print("\nCorrect syntax should be:")
print("""
- name: Create SSL cert if not exists yet
  command: >
    openssl req -new -nodes -x509 \\
    -subj "..." \\
    -days 3650 \\
    -keyout {{ openwisp2_ssl_key }} \\
    -out {{ openwisp2_ssl_cert }} \\
    -extensions v3_ca
  args:
    creates: "{{ openwisp2_ssl_cert }}"
  notify: Restart nginx
""")
EOF

Repository: openwisp/ansible-openwisp2

Length of output: 654


Fix creates usage to make SSL cert generation idempotent.
The creates={{ openwisp2_ssl_cert }} is incorrectly embedded in the openssl command string instead of being a separate module argument. Move it to module args so Ansible will skip the task if the certificate file already exists.

✅ Proposed fix
 - name: Create SSL cert if not exists yet
   command: >
     openssl req -new -nodes -x509 \
     -subj "/C={{ openwisp2_ssl_country }}/ST={{ openwisp2_ssl_state }} \
            /L={{ openwisp2_ssl_locality }}/O={{ openwisp2_ssl_organization }} \
            /CN={{ openwisp2_ssl_common_name }}" \
     -days 3650 \
     -keyout {{ openwisp2_ssl_key }} \
     -out {{ openwisp2_ssl_cert }} \
-    -extensions v3_ca creates={{ openwisp2_ssl_cert }}
+    -extensions v3_ca
+  args:
+    creates: "{{ openwisp2_ssl_cert }}"
   notify: Restart nginx
🤖 Prompt for AI Agents
In `@tasks/nginx.yml` around lines 26 - 36, The task "Create SSL cert if not
exists yet" embeds creates={{ openwisp2_ssl_cert }} inside the openssl command
string, so Ansible won't treat it as the command module's creates argument;
remove the trailing "creates=..." from the command value and add a separate
top-level creates: "{{ openwisp2_ssl_cert }}" key under the same task (keeping
the command string intact and the notify: Restart nginx), so the command module
will skip execution when the certificate file exists.

@Varadraj75 Varadraj75 force-pushed the fix-lint-errors-485-followup branch from 59688e7 to 2a24864 Compare February 2, 2026 04:45
Copy link

@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 (3)
tasks/django.yml (3)

169-178: ⚠️ Potential issue | 🟠 Major

Avoid masking copy errors with failed_when: false.

This suppresses real failures (permissions, disk full, etc.). Since you already stat the source, it’s safer to conditionally skip instead of ignoring errors.

✅ Suggested fix
 - name: Copy static files
   become: true
   copy:
     src: ow2_static/
     dest: "{{ openwisp2_path }}/static_custom"
     owner: "{{ www_user }}"
     group: "{{ www_group }}"
     mode: "0640"
-  failed_when: false
+  when: ow2_static_dir.stat.exists
   tags:
     - openwisp2_theme

122-138: ⚠️ Potential issue | 🟠 Major

Add community.general collection dependency to role metadata.

The role uses community.general.django_manage in multiple tasks but does not declare the collection dependency. Add the collection to the dependencies section in meta/main.yml or create a requirements.yml file at the role root level to ensure the collection is installed before the role runs.

Affected tasks: "Run geocoding check" (line ~122), "Migrate" (line ~142), "Collectstatic" (line ~215), "Compilemessages" (line ~230).


181-191: ⚠️ Potential issue | 🟡 Minor

Use stdout instead of out for the field name.

The community.general.django_manage module returns standard Ansible common return values like stdout, stderr, and stdout_lines. The current code references collectstatic_output.out, which does not exist; this breaks idempotence reporting. Change to collectstatic_output.stdout.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59688e7 and 2a24864.

📒 Files selected for processing (30)
  • .ansible-lint
  • .flake8
  • .github/FUNDING.yml
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .yamllint.yml
  • defaults/main.yml
  • docs/developer/installation.rst
  • handlers/main.yml
  • molecule/default/molecule.yml
  • molecule/local/molecule.yml
  • molecule/resources/converge.yml
  • molecule/resources/requirements.yml
  • molecule/resources/verify.yml
  • pyproject.toml
  • run-qa-checks
  • tasks/apt.yml
  • tasks/complete.yml
  • tasks/cron.yml
  • tasks/django.yml
  • tasks/django_secret_key.yml
  • tasks/freeradius.yml
  • tasks/freeradius_eap.yml
  • tasks/main.yml
  • tasks/nginx.yml
  • tasks/pip.yml
  • tasks/ssh.yml
  • tasks/supervisor.yml
  • tasks/system.yml
  • vars/debian-13.yml
💤 Files with no reviewable changes (9)
  • molecule/default/molecule.yml
  • molecule/local/molecule.yml
  • tasks/system.yml
  • tasks/pip.yml
  • tasks/ssh.yml
  • tasks/main.yml
  • molecule/resources/requirements.yml
  • tasks/cron.yml
  • tasks/complete.yml
✅ Files skipped from review due to trivial changes (3)
  • docs/developer/installation.rst
  • .ansible-lint
  • vars/debian-13.yml
🚧 Files skipped from review as they are similar to previous changes (10)
  • tasks/apt.yml
  • .github/FUNDING.yml
  • .yamllint.yml
  • tasks/supervisor.yml
  • .flake8
  • tasks/nginx.yml
  • run-qa-checks
  • molecule/resources/verify.yml
  • molecule/resources/converge.yml
  • handlers/main.yml
⏰ 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). (4)
  • GitHub Check: Build debian13
  • GitHub Check: Build debian12
  • GitHub Check: Build ubuntu2404
  • GitHub Check: Build ubuntu2204
🔇 Additional comments (16)
.github/workflows/ci.yml (4)

5-5: LGTM!

The inline yamllint disable comment is the correct approach to suppress the false positive on the on: key being flagged as a truthy value.


43-44: LGTM!

Adding the id: deps enables proper step referencing for the conditional check in the Tests step.


56-57: LGTM!

The conditional !cancelled() && steps.deps.conclusion == 'success' ensures Tests run only when dependencies installed successfully while still allowing QA checks to fail independently. This aligns with the PR objective of making CI failures real and actionable.


64-65: LGTM!

Quoting the environment variable values ("1") is good YAML hygiene—it ensures the values are treated as strings rather than potentially being interpreted as integers.

tasks/django_secret_key.yml (2)

2-6: Consistent quoted mode on copy task looks good.


21-24: Quoted mode on secret key file is correct.

pyproject.toml (1)

4-10: Flake8 exclude block is clear and standard.

tasks/freeradius_eap.yml (2)

11-13: Notify/tag placement on DH generation block is fine.


25-68: Quoted mode values are consistent across templates.

tasks/django.yml (1)

2-115: Mode string normalization is consistent.

Also applies to: 220-225

tasks/freeradius.yml (2)

2-40: Jammy gating and repo setup block look good.


110-189: Mode string quoting is consistent across FreeRADIUS templates.

defaults/main.yml (4)

130-163: Structured nginx CSP/gzip config is clean and readable.


188-205: freeradius_db_map restructure looks consistent.


223-226: CORS config mapped cleanly under openwisp2_django_cors.


30-112: No downstream compatibility issues detected.

All templates and tasks correctly reference the restructured variables using the new nested format. Verified:

  • openwisp2_database.engine, .name accessed correctly in tasks/django.yml, tasks/pip.yml, tasks/apt.yml
  • openwisp2_topology_update_frequency.day, .hour, .minute accessed correctly in tasks/cron.yml
  • openwisp2_timeseries_database.backend, .user, .password, .name, .host, .port accessed correctly in templates/openwisp2/settings.py
  • Celery pool/autoscale variables referenced as flat scalars in all supervisor templates

No legacy access patterns found. Migration is complete and consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@Varadraj75 Varadraj75 force-pushed the fix-lint-errors-485-followup branch from 740a4dd to 282f29c Compare February 2, 2026 07:43
Copy link

@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: 6

🤖 Fix all issues with AI agents
In @.yamllint.yml:
- Line 1: The file .yamllint.yml currently uses CRLF line endings causing
yamllint to fail with "wrong new line character: expected \n"; convert/normalize
the file to LF line endings (e.g., open and re-save with Unix (LF) line endings
or run a tool like dos2unix .yamllint.yml or use git's core.autocrlf=false and
re-checkout) so the linter can read it correctly; ensure the commit only changes
line endings and verify by running yamllint or git diff to confirm the line
endings are now LF.

In `@defaults/main.yml`:
- Line 115: The YAML contains empty key assignments (e.g., openwisp2_uwsgi_gid)
that trigger yamllint; locate the empty keys such as openwisp2_uwsgi_gid (and
the other empty entry around the same block) and either remove those key lines
entirely if unused, or give them an explicit null value (~) or a valid value
instead of leaving them blank so the file is valid YAML and passes CI.
- Line 1: The file in the diff has Windows CRLF line endings; convert it to Unix
LF line endings (e.g., run a line-ending conversion tool or strip carriage
returns) and commit the change so the file uses \n only; also ensure repository
settings prevent reintroducing CRLF (set core.autocrlf appropriately or add a
.gitattributes rule) so the YAML defaults file remains LF.
- Around line 86-111: Several Celery-related variables (e.g.,
openwisp2_celery_autoscale, openwisp2_celery_prefetch_multiplier,
openwisp2_celery_autoscale, openwisp2_celery_network_autoscale,
openwisp2_celery_pool, openwisp2_celery_monitoring_pool,
openwisp2_celery_network_pool, openwisp2_celery_firmware_upgrader_autoscale,
openwisp2_celery_firmware_upgrader_pool) are defined with empty values which
violates the yamllint empty-values rule; update each empty entry to an explicit
value (choose either null or an empty string "" or a sensible default) so the
YAML linter accepts the file (e.g., set those variables to null where
semantically appropriate, or "" if an empty string is preferred) and keep the
non-empty entries like openwisp2_celery_broker_url unchanged.

In `@molecule/resources/verify.yml`:
- Line 44: Fix the typo in the Ansible task name string "Chcke if redis is
running" by updating it to "Check if redis is running" so play output is clear;
locate the task name in verify.yml (the task whose name equals "Chcke if redis
is running") and replace the misspelled word.

In `@tasks/freeradius.yml`:
- Around line 241-242: Remove the unresolved git merge marker `=======` and the
duplicated YAML document separator `---`, decide which version to keep (either
the first block before the marker or the second block that begins with `---`),
delete the other duplicate block so the file contains a single valid YAML
document, and ensure the resulting content parses as YAML (no leftover conflict
markers).
🧹 Nitpick comments (2)
tasks/pip.yml (1)

17-112: Consider the trade-off of state: latest.

Multiple tasks use state: latest for OpenWISP components. This ensures users get the latest versions but may cause non-reproducible deployments. This appears intentional for an Ansible role meant to deploy the latest OpenWISP stack, but consider documenting this behavior for users who need reproducible builds.

tasks/freeradius.yml (1)

67-75: Consider breaking long lines to satisfy yamllint line-length warnings.

Lines 69, 79, 90, and 319 exceed the 162-character limit. While these are warnings (not errors), you can improve readability by breaking the echo commands:

♻️ Example fix for line 69 (Ubuntu noble)
 - name: Add freeradius repository into Ubuntu noble sources list
   shell: |
-    echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/packages.networkradius.com.asc] http://packages.networkradius.com/freeradius-3.2/ubuntu/noble noble main" | \
+    echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/packages.networkradius.com.asc] \
+    http://packages.networkradius.com/freeradius-3.2/ubuntu/noble noble main" | \
     tee /etc/apt/sources.list.d/networkradius.list > /dev/null

Also applies to: 77-86, 88-97, 317-326

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 740a4dd and fe75d66.

📒 Files selected for processing (31)
  • .ansible-lint
  • .github/FUNDING.yml
  • .github/dependabot.yml
  • .github/workflows/ci.yml
  • .yamllint.yml
  • README.md
  • defaults/main.yml
  • docs/developer/installation.rst
  • handlers/main.yml
  • meta/main.yml
  • molecule/default/molecule.yml
  • molecule/local/molecule.yml
  • molecule/resources/converge.yml
  • molecule/resources/requirements.yml
  • molecule/resources/verify.yml
  • run-qa-checks
  • tasks/apt.yml
  • tasks/complete.yml
  • tasks/cron.yml
  • tasks/django.yml
  • tasks/django_secret_key.yml
  • tasks/freeradius.yml
  • tasks/freeradius_eap.yml
  • tasks/main.yml
  • tasks/nginx.yml
  • tasks/pip.yml
  • tasks/ssh.yml
  • tasks/supervisor.yml
  • tasks/system.yml
  • templates/openwisp2/settings.py
  • vars/debian-13.yml
✅ Files skipped from review due to trivial changes (5)
  • README.md
  • tasks/cron.yml
  • meta/main.yml
  • tasks/django.yml
  • tasks/main.yml
🚧 Files skipped from review as they are similar to previous changes (10)
  • tasks/system.yml
  • tasks/complete.yml
  • docs/developer/installation.rst
  • molecule/resources/requirements.yml
  • molecule/default/molecule.yml
  • vars/debian-13.yml
  • .github/FUNDING.yml
  • tasks/ssh.yml
  • .ansible-lint
  • run-qa-checks
🧰 Additional context used
🪛 actionlint (1.7.10)
.github/workflows/ci.yml

[error] 38-38: property "python-version" is not defined in object type {distro: string}

(expression)

🪛 Checkov (3.2.334)
molecule/resources/verify.yml

[medium] 50-55: Ensure that certificate validation isn't disabled with uri

(CKV_ANSIBLE_1)

🪛 GitHub Actions: Ansible-OpenWISP2 CI Build
.yamllint.yml

[error] 1-1: Yamllint: wrong new line character: expected newline (line 1).

🪛 GitHub Check: Build debian12
defaults/main.yml

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping


[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

tasks/freeradius.yml

[warning] 90-90:
90:163 [line-length] line too long (174 > 162 characters)


[warning] 79-79:
79:163 [line-length] line too long (172 > 162 characters)


[warning] 69-69:
69:163 [line-length] line too long (168 > 162 characters)


[warning] 319-319:
319:163 [line-length] line too long (172 > 162 characters)

.yamllint.yml

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build debian13
defaults/main.yml

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping


[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

tasks/freeradius.yml

[warning] 90-90:
90:163 [line-length] line too long (174 > 162 characters)


[warning] 79-79:
79:163 [line-length] line too long (172 > 162 characters)


[warning] 69-69:
69:163 [line-length] line too long (168 > 162 characters)


[warning] 319-319:
319:163 [line-length] line too long (172 > 162 characters)

.yamllint.yml

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2204
defaults/main.yml

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping


[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

tasks/freeradius.yml

[warning] 90-90:
90:163 [line-length] line too long (174 > 162 characters)


[warning] 79-79:
79:163 [line-length] line too long (172 > 162 characters)


[warning] 69-69:
69:163 [line-length] line too long (168 > 162 characters)


[warning] 319-319:
319:163 [line-length] line too long (172 > 162 characters)

.yamllint.yml

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2404
defaults/main.yml

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping


[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

tasks/freeradius.yml

[warning] 90-90:
90:163 [line-length] line too long (174 > 162 characters)


[warning] 79-79:
79:163 [line-length] line too long (172 > 162 characters)


[warning] 69-69:
69:163 [line-length] line too long (168 > 162 characters)


[warning] 319-319:
319:163 [line-length] line too long (172 > 162 characters)

.yamllint.yml

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 Ruff (0.14.14)
templates/openwisp2/settings.py

[warning] 20-20: Expected an expression

(invalid-syntax)


[warning] 20-20: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 20-20: Expected ,, found name

(invalid-syntax)


[warning] 20-20: Expected an expression

(invalid-syntax)


[warning] 21-21: Expected ,, found string

(invalid-syntax)


[warning] 22-22: Expected an expression

(invalid-syntax)


[warning] 22-22: Expected an expression

(invalid-syntax)


[warning] 50-50: Expected an expression

(invalid-syntax)


[warning] 50-50: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 50-50: Expected ,, found name

(invalid-syntax)


[warning] 50-50: Expected an expression

(invalid-syntax)


[warning] 51-51: Expected ,, found string

(invalid-syntax)


[warning] 52-52: Expected an expression

(invalid-syntax)


[warning] 52-52: Expected an expression

(invalid-syntax)


[warning] 53-53: Expected ,, found {

(invalid-syntax)


[warning] 53-53: Expected an expression

(invalid-syntax)


[warning] 53-53: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 53-53: Expected ,, found name

(invalid-syntax)


[warning] 53-53: Expected an expression

(invalid-syntax)


[warning] 54-54: Expected ,, found string

(invalid-syntax)


[warning] 58-58: Expected an expression

(invalid-syntax)


[warning] 58-58: Expected an expression

(invalid-syntax)


[warning] 59-59: Expected ,, found string

(invalid-syntax)


[warning] 61-61: Expected an expression

(invalid-syntax)


[warning] 61-61: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 61-61: Expected ,, found name

(invalid-syntax)


[warning] 61-61: Expected an expression

(invalid-syntax)


[warning] 62-62: Expected ,, found string

(invalid-syntax)


[warning] 63-63: Expected an expression

(invalid-syntax)


[warning] 63-63: Expected an expression

(invalid-syntax)


[warning] 64-64: Expected ,, found {

(invalid-syntax)


[warning] 64-64: Expected an expression

(invalid-syntax)


[warning] 64-64: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 64-64: Expected ,, found name

(invalid-syntax)


[warning] 64-64: Expected an expression

(invalid-syntax)


[warning] 65-65: Expected ,, found string

(invalid-syntax)


[warning] 66-66: Expected an expression

(invalid-syntax)


[warning] 66-66: Expected an expression

(invalid-syntax)


[warning] 67-67: Expected ,, found string

(invalid-syntax)


[warning] 68-68: Expected an expression

(invalid-syntax)


[warning] 68-68: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 68-68: Expected ,, found name

(invalid-syntax)


[warning] 68-68: Expected an expression

(invalid-syntax)


[warning] 69-69: Expected ,, found string

(invalid-syntax)


[warning] 72-72: Expected an expression

(invalid-syntax)


[warning] 72-72: Expected an expression

(invalid-syntax)


[warning] 75-75: Expected ,, found string

(invalid-syntax)


[warning] 76-76: Expected an expression

(invalid-syntax)


[warning] 76-76: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 76-76: Expected ,, found name

(invalid-syntax)


[warning] 76-76: Expected an expression

(invalid-syntax)


[warning] 77-77: Expected ,, found string

(invalid-syntax)


[warning] 78-78: Expected an expression

(invalid-syntax)


[warning] 78-78: Expected an expression

(invalid-syntax)


[warning] 79-79: Expected ,, found string

(invalid-syntax)


[warning] 91-91: Expected an expression

(invalid-syntax)


[warning] 91-91: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 91-91: Expected ,, found name

(invalid-syntax)


[warning] 91-91: Expected an expression

(invalid-syntax)


[warning] 92-92: Expected ,, found string

(invalid-syntax)


[warning] 93-93: Expected an expression

(invalid-syntax)


[warning] 93-93: Expected an expression

(invalid-syntax)


[warning] 94-94: Expected ,, found string

(invalid-syntax)


[warning] 98-98: Expected an expression

(invalid-syntax)


[warning] 98-98: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 98-98: Expected ,, found name

(invalid-syntax)


[warning] 98-98: Expected an expression

(invalid-syntax)


[warning] 99-99: Expected ,, found string

(invalid-syntax)


[warning] 100-100: Expected an expression

(invalid-syntax)


[warning] 100-100: Expected an expression

(invalid-syntax)


[warning] 101-101: Expected ,, found {

(invalid-syntax)


[warning] 101-101: Expected an expression

(invalid-syntax)


[warning] 101-101: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 101-101: Expected ,, found name

(invalid-syntax)


[warning] 101-101: Expected an expression

(invalid-syntax)


[warning] 102-102: Expected ,, found string

(invalid-syntax)


[warning] 103-103: Expected an expression

(invalid-syntax)


[warning] 103-103: Expected an expression

(invalid-syntax)


[warning] 104-104: Expected ,, found {

(invalid-syntax)


[warning] 104-104: Expected an expression

(invalid-syntax)


[warning] 104-104: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 104-104: Expected ,, found name

(invalid-syntax)


[warning] 104-104: Expected an expression

(invalid-syntax)


[warning] 105-105: Expected ,, found string

(invalid-syntax)


[warning] 106-106: Expected an expression

(invalid-syntax)


[warning] 106-106: Expected an expression

(invalid-syntax)


[warning] 114-114: Expected an expression

(invalid-syntax)


[warning] 114-114: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 114-114: Expected ,, found name

(invalid-syntax)


[warning] 114-114: Expected an expression

(invalid-syntax)


[warning] 116-116: Expected an expression

(invalid-syntax)


[warning] 116-116: Expected an expression

(invalid-syntax)


[warning] 118-118: Expected an expression

(invalid-syntax)


[warning] 118-118: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 118-118: Expected ,, found name

(invalid-syntax)


[warning] 118-118: Expected an expression

(invalid-syntax)


[warning] 120-120: Expected an expression

(invalid-syntax)


[warning] 120-120: Expected an expression

(invalid-syntax)


[warning] 138-138: Expected an expression

(invalid-syntax)


[warning] 138-138: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 138-138: Expected ,, found name

(invalid-syntax)


[warning] 138-138: Expected an expression

(invalid-syntax)


[warning] 139-139: Expected ,, found string

(invalid-syntax)


[warning] 140-140: Expected an expression

(invalid-syntax)


[warning] 140-140: Expected an expression

(invalid-syntax)


[warning] 141-141: Expected ,, found string

(invalid-syntax)


[warning] 144-144: Expected an expression

(invalid-syntax)


[warning] 144-144: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 144-144: Expected ,, found name

(invalid-syntax)


[warning] 144-144: Expected an expression

(invalid-syntax)


[warning] 145-145: Expected ,, found string

(invalid-syntax)


[warning] 146-146: Expected an expression

(invalid-syntax)


[warning] 146-146: Expected an expression

(invalid-syntax)


[warning] 147-147: Expected ,, found string

(invalid-syntax)


[warning] 150-150: Expected an expression

(invalid-syntax)


[warning] 150-150: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 150-150: Expected ,, found name

(invalid-syntax)


[warning] 150-150: Expected an expression

(invalid-syntax)


[warning] 151-151: Expected ,, found string

(invalid-syntax)


[warning] 152-152: Expected an expression

(invalid-syntax)


[warning] 152-152: Expected an expression

(invalid-syntax)


[warning] 153-153: Expected ,, found string

(invalid-syntax)


[warning] 160-160: Expected an expression

(invalid-syntax)


[warning] 160-160: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 160-160: Expected ,, found name

(invalid-syntax)


[warning] 160-160: Expected an expression

(invalid-syntax)


[warning] 170-170: Expected an expression

(invalid-syntax)


[warning] 170-170: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 170-170: Expected ,, found name

(invalid-syntax)


[warning] 170-170: Expected an expression

(invalid-syntax)


[warning] 172-172: Expected an expression

(invalid-syntax)


[warning] 172-172: Expected an expression

(invalid-syntax)


[warning] 173-173: Expected an expression

(invalid-syntax)


[warning] 173-173: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 173-173: Expected ,, found name

(invalid-syntax)


[warning] 173-173: Expected an expression

(invalid-syntax)


[warning] 175-175: Expected an expression

(invalid-syntax)


[warning] 175-175: Expected an expression

(invalid-syntax)


[warning] 184-184: Expected an expression

(invalid-syntax)


[warning] 184-184: Expected an expression

(invalid-syntax)


[warning] 188-188: Expected an expression

(invalid-syntax)


[warning] 188-188: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 188-188: Expected ,, found name

(invalid-syntax)


[warning] 188-188: Expected an expression

(invalid-syntax)


[warning] 190-190: Expected an expression

(invalid-syntax)


[warning] 190-190: Expected an expression

(invalid-syntax)


[warning] 191-191: Expected an expression

(invalid-syntax)


[warning] 191-191: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 191-191: Expected ,, found name

(invalid-syntax)


[warning] 191-191: Expected an expression

(invalid-syntax)


[warning] 193-193: Expected an expression

(invalid-syntax)


[warning] 193-193: Expected an expression

(invalid-syntax)


[warning] 250-250: Expected an expression

(invalid-syntax)


[warning] 250-250: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 250-250: Expected ,, found name

(invalid-syntax)


[warning] 250-250: Expected an expression

(invalid-syntax)


[warning] 251-251: Expected ,, found string

(invalid-syntax)


[warning] 251-251: Expected ,, found :

(invalid-syntax)


[warning] 255-255: Expected an expression

(invalid-syntax)


[warning] 255-255: Expected an expression

(invalid-syntax)


[warning] 256-256: Expected ,, found {

(invalid-syntax)


[warning] 256-256: Expected an expression

(invalid-syntax)


[warning] 256-256: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 256-256: Expected ,, found name

(invalid-syntax)


[warning] 256-256: Expected an expression

(invalid-syntax)


[warning] 257-257: Expected ,, found string

(invalid-syntax)


[warning] 257-257: Expected ,, found :

(invalid-syntax)


[warning] 262-262: Expected an expression

(invalid-syntax)


[warning] 262-262: Expected an expression

(invalid-syntax)


[warning] 263-263: Expected ,, found {

(invalid-syntax)


[warning] 263-263: Expected an expression

(invalid-syntax)


[warning] 263-263: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 263-263: Expected ,, found name

(invalid-syntax)


[warning] 263-263: Expected an expression

(invalid-syntax)


[warning] 264-264: Expected ,, found string

(invalid-syntax)


[warning] 264-264: Expected ,, found :

(invalid-syntax)


[warning] 268-268: Expected an expression

(invalid-syntax)


[warning] 268-268: Expected an expression

(invalid-syntax)


[warning] 269-269: Expected ,, found {

(invalid-syntax)


[warning] 269-269: Expected an expression

(invalid-syntax)


[warning] 269-269: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 269-269: Expected ,, found name

(invalid-syntax)


[warning] 269-269: Expected an expression

(invalid-syntax)


[warning] 270-270: Expected ,, found string

(invalid-syntax)


[warning] 270-270: Expected ,, found :

(invalid-syntax)


[warning] 276-276: Expected ,, found :

(invalid-syntax)


[warning] 284-284: Expected ,, found :

(invalid-syntax)


[warning] 290-290: Expected ,, found :

(invalid-syntax)


[warning] 296-296: Expected an expression

(invalid-syntax)


[warning] 296-296: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 296-296: Expected ,, found name

(invalid-syntax)


[warning] 296-296: Expected an expression

(invalid-syntax)


[warning] 297-297: Expected ,, found string

(invalid-syntax)


[warning] 297-297: Expected ,, found :

(invalid-syntax)


[warning] 303-303: Expected an expression

(invalid-syntax)


[warning] 303-303: Expected an expression

(invalid-syntax)


[warning] 304-304: Expected ,, found {

(invalid-syntax)


[warning] 304-304: Expected an expression

(invalid-syntax)


[warning] 304-304: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 304-304: Expected ,, found name

(invalid-syntax)


[warning] 304-304: Expected an expression

(invalid-syntax)


[warning] 305-305: Expected ,, found string

(invalid-syntax)


[warning] 305-305: Expected ,, found :

(invalid-syntax)


[warning] 310-310: Expected an expression

(invalid-syntax)


[warning] 310-310: Expected an expression

(invalid-syntax)


[warning] 311-311: Expected ,, found {

(invalid-syntax)


[warning] 311-311: Expected an expression

(invalid-syntax)


[warning] 311-311: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 311-311: Expected ,, found name

(invalid-syntax)


[warning] 311-311: Expected an expression

(invalid-syntax)


[warning] 312-312: Expected ,, found string

(invalid-syntax)


[warning] 312-312: Expected ,, found :

(invalid-syntax)


[warning] 317-317: Expected an expression

(invalid-syntax)


[warning] 317-317: Expected an expression

(invalid-syntax)


[warning] 318-318: Expected ,, found {

(invalid-syntax)


[warning] 318-318: Expected an expression

(invalid-syntax)


[warning] 318-318: Expected an expression

(invalid-syntax)


[warning] 319-319: Expected ,, found {

(invalid-syntax)


[warning] 319-319: Expected an expression

(invalid-syntax)


[warning] 319-319: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 319-319: Expected ,, found name

(invalid-syntax)


[warning] 319-319: Expected an expression

(invalid-syntax)


[warning] 320-320: Expected ,, found string

(invalid-syntax)


[warning] 320-320: Expected ,, found :

(invalid-syntax)


[warning] 324-324: Expected an expression

(invalid-syntax)


[warning] 324-324: Expected an expression

(invalid-syntax)


[warning] 327-327: Expected an expression

(invalid-syntax)


[warning] 327-327: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 327-327: Expected ,, found name

(invalid-syntax)


[warning] 327-327: Expected an expression

(invalid-syntax)


[warning] 329-329: Expected an expression

(invalid-syntax)


[warning] 329-329: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 329-329: Expected ,, found name

(invalid-syntax)


[warning] 329-329: Expected an expression

(invalid-syntax)


[warning] 331-331: Expected ,, found string

(invalid-syntax)


[warning] 331-331: Expected ,, found :

(invalid-syntax)


[warning] 332-332: Expected an expression

(invalid-syntax)


[warning] 332-332: Expected an expression

(invalid-syntax)


[warning] 333-333: Expected ,, found {

(invalid-syntax)


[warning] 333-333: Expected an expression

(invalid-syntax)


[warning] 333-333: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 333-333: Expected ,, found name

(invalid-syntax)


[warning] 333-333: Expected an expression

(invalid-syntax)


[warning] 335-335: Expected ,, found string

(invalid-syntax)


[warning] 335-335: Expected ,, found :

(invalid-syntax)


[warning] 336-336: Expected ,, found :

(invalid-syntax)


[warning] 339-339: Expected an expression

(invalid-syntax)


[warning] 339-339: Expected an expression

(invalid-syntax)


[warning] 340-340: Expected ,, found {

(invalid-syntax)


[warning] 340-340: Expected an expression

(invalid-syntax)


[warning] 340-340: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 340-340: Expected ,, found name

(invalid-syntax)


[warning] 340-340: Expected an expression

(invalid-syntax)


[warning] 342-342: Expected ,, found string

(invalid-syntax)


[warning] 342-342: Expected ,, found :

(invalid-syntax)


[warning] 343-343: Expected ,, found :

(invalid-syntax)


[warning] 346-346: Expected an expression

(invalid-syntax)


[warning] 346-346: Expected an expression

(invalid-syntax)


[warning] 349-349: Expected an expression

(invalid-syntax)


[warning] 349-349: Expected an expression

(invalid-syntax)


[warning] 378-378: Expected an expression

(invalid-syntax)


[warning] 378-378: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 378-378: Expected ,, found name

(invalid-syntax)


[warning] 378-378: Expected an expression

(invalid-syntax)


[warning] 379-379: Expected :, found string

(invalid-syntax)


[warning] 379-379: Expected ,, found :

(invalid-syntax)


[warning] 379-379: Expected :, found ,

(invalid-syntax)


[warning] 380-380: Expected an expression

(invalid-syntax)


[warning] 380-380: Expected an expression

(invalid-syntax)


[warning] 381-381: Expected :, found {

(invalid-syntax)


[warning] 381-381: Expected an expression

(invalid-syntax)


[warning] 381-381: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 381-381: Expected ,, found name

(invalid-syntax)


[warning] 381-381: Expected an expression

(invalid-syntax)


[warning] 382-382: Expected ,, found string

(invalid-syntax)


[warning] 383-383: Expected an expression

(invalid-syntax)


[warning] 383-383: Expected an expression

(invalid-syntax)


[warning] 384-384: Expected :, found {

(invalid-syntax)


[warning] 384-384: Expected an expression

(invalid-syntax)


[warning] 384-384: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 384-384: Expected ,, found name

(invalid-syntax)


[warning] 384-384: Expected an expression

(invalid-syntax)


[warning] 385-385: Expected ,, found string

(invalid-syntax)


[warning] 386-386: Expected an expression

(invalid-syntax)


[warning] 386-386: Expected an expression

(invalid-syntax)


[warning] 387-387: Expected :, found {

(invalid-syntax)


[warning] 387-387: Expected an expression

(invalid-syntax)


[warning] 387-387: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 387-387: Expected ,, found name

(invalid-syntax)


[warning] 387-387: Expected an expression

(invalid-syntax)


[warning] 388-388: Expected ,, found string

(invalid-syntax)


[warning] 389-389: Expected an expression

(invalid-syntax)


[warning] 389-389: Expected an expression

(invalid-syntax)


[warning] 390-390: Expected :, found {

(invalid-syntax)


[warning] 390-390: Expected an expression

(invalid-syntax)


[warning] 390-390: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 390-390: Expected ,, found name

(invalid-syntax)


[warning] 390-390: Expected an expression

(invalid-syntax)


[warning] 391-391: Expected ,, found string

(invalid-syntax)


[warning] 392-392: Expected an expression

(invalid-syntax)


[warning] 392-392: Expected an expression

(invalid-syntax)


[warning] 393-393: Expected :, found }

(invalid-syntax)


[warning] 396-396: Expected an expression

(invalid-syntax)


[warning] 396-396: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 396-396: Expected ,, found name

(invalid-syntax)


[warning] 396-396: Expected an expression

(invalid-syntax)


[warning] 398-398: Expected an expression

(invalid-syntax)


[warning] 398-398: Expected an expression

(invalid-syntax)


[warning] 418-418: Expected an expression

(invalid-syntax)


[warning] 418-418: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 418-418: Expected ,, found name

(invalid-syntax)


[warning] 418-418: Expected an expression

(invalid-syntax)


[warning] 420-420: Expected an expression

(invalid-syntax)


[warning] 420-420: Expected an expression

(invalid-syntax)


[warning] 432-432: Expected an expression

(invalid-syntax)


[warning] 432-432: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 432-432: Expected ,, found name

(invalid-syntax)


[warning] 432-432: Expected an expression

(invalid-syntax)


[warning] 434-434: Expected an expression

(invalid-syntax)


[warning] 434-434: Expected an expression

(invalid-syntax)


[warning] 440-440: Expected an expression

(invalid-syntax)


[warning] 440-440: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 440-440: Expected ,, found name

(invalid-syntax)


[warning] 440-440: Expected an expression

(invalid-syntax)


[warning] 442-442: Expected an expression

(invalid-syntax)


[warning] 442-442: Expected an identifier, but found a keyword else that cannot be used here

(invalid-syntax)


[warning] 442-442: Expected an expression

(invalid-syntax)


[warning] 444-444: Expected an expression

(invalid-syntax)


[warning] 444-444: Expected an expression

(invalid-syntax)


[warning] 495-495: Expected an expression

(invalid-syntax)


[warning] 495-495: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 495-495: Expected ,, found name

(invalid-syntax)


[warning] 495-495: Expected an expression

(invalid-syntax)


[warning] 496-496: Expected :, found string

(invalid-syntax)


[warning] 496-496: Expected ,, found :

(invalid-syntax)


[warning] 500-500: Expected :, found ,

(invalid-syntax)


[warning] 501-501: Expected an expression

(invalid-syntax)


[warning] 501-501: Expected an expression

(invalid-syntax)


[warning] 502-502: Expected :, found }

(invalid-syntax)


[warning] 509-509: Expected an expression

(invalid-syntax)


[warning] 509-509: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 509-509: Expected ,, found name

(invalid-syntax)


[warning] 509-509: Expected an expression

(invalid-syntax)


[warning] 510-510: Expected ,, found string

(invalid-syntax)


[warning] 511-511: Expected an expression

(invalid-syntax)


[warning] 511-511: Expected an expression

(invalid-syntax)


[warning] 534-534: Expected an expression

(invalid-syntax)


[warning] 534-534: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 534-534: Expected ,, found name

(invalid-syntax)


[warning] 534-534: Expected an expression

(invalid-syntax)


[warning] 536-536: Expected an expression

(invalid-syntax)


[warning] 536-536: Expected an expression

(invalid-syntax)


[warning] 538-538: Expected an expression

(invalid-syntax)


[warning] 538-538: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 538-538: Expected ,, found name

(invalid-syntax)


[warning] 538-538: Expected an expression

(invalid-syntax)


[warning] 550-550: Expected an expression

(invalid-syntax)


[warning] 550-550: Expected an expression

(invalid-syntax)


[warning] 552-552: Expected an expression

(invalid-syntax)


[warning] 552-552: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 552-552: Expected ,, found name

(invalid-syntax)


[warning] 552-552: Expected an expression

(invalid-syntax)


[warning] 553-553: Invalid assignment target

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 553-553: Expected ,, found name

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an identifier, but found a keyword else that cannot be used here

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Simple statements must be separated by newlines or semicolons

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 553-553: Expected an expression

(invalid-syntax)


[warning] 554-554: Expected an expression

(invalid-syntax)


[warning] 554-554: Expected an expression

(invalid-syntax)


[warning] 556-556: Expected an expression

(invalid-syntax)


[warning] 556-556: Expected an identifier, but found a keyword for that cannot be used here

(invalid-syntax)


[warning] 556-556: Expected ,, found name

(invalid-syntax)


[warning] 556-556: Expected an expression

(invalid-syntax)


[warning] 558-558: Expected an expression

(invalid-syntax)


[warning] 558-558: Expected an expression

(invalid-syntax)


[warning] 560-560: Expected an expression

(invalid-syntax)


[warning] 560-560: Expected an identifier, but found a keyword if that cannot be used here

(invalid-syntax)


[warning] 560-560: Expected ,, found name

(invalid-syntax)


[warning] 560-560: Expected an expression

(invalid-syntax)


[warning] 568-568: Expected an expression

(invalid-syntax)


[warning] 568-568: Expected an expression

(invalid-syntax)

🔇 Additional comments (19)
.github/dependabot.yml (1)

1-2: YAML document start looks good.

No issues with the document header addition.

molecule/resources/verify.yml (2)

1-2: YAML header addition looks good.

No concerns here.


50-53: This is Molecule test code using self-signed certificates—validate_certs: false is appropriate here.

The verify.yml file is part of Molecule, which is a testing framework for Ansible roles. This file runs in a Docker test environment where OpenWISP is configured with self-signed certificates (openwisp2_ssl_cert and openwisp2_ssl_key in defaults). The validate_certs: false setting is necessary and correct for verifying the application is running in this test context. Since this is test-only code and not production code, the security concern does not apply.

Likely an incorrect or invalid review comment.

molecule/resources/converge.yml (1)

1-35: Formatting-only changes look good.

No functional concerns found.

tasks/supervisor.yml (1)

1-72: Looks good.

No concerns with these updates.

templates/openwisp2/settings.py (1)

1-571: Template formatting looks consistent.

No functional issues noted in this update.

tasks/django_secret_key.yml (1)

1-27: Looks good.

No concerns with these changes.

molecule/local/molecule.yml (1)

1-56: LGTM!

The removal of the inline lint: block from the Molecule configuration aligns with the PR objective to centralize lint and QA checks into the run-qa-checks script. The remaining configuration is well-structured with consistent platform definitions.

tasks/apt.yml (2)

1-12: LGTM!

The molecule-idempotence-notest tag on the APT cache update task is appropriate since package cache updates are inherently non-idempotent operations. This prevents false idempotency test failures.


53-61: Redis IPv4 binding configuration looks correct.

The escaped regexp pattern and quoted line value maintain proper functionality while improving YAML consistency.

tasks/freeradius_eap.yml (2)

11-24: Good use of block-level notification.

The block structure with notify: Restart freeradius at the block level ensures a single restart notification for the entire certificate generation sequence. The stat check before running make destroycerts dh all provides proper idempotency.


25-77: LGTM!

Individual tasks for EAP configuration are well-structured with:

  • Consistent use of FQCNs (ansible.builtin.file, ansible.builtin.stat)
  • Proper string-quoted file modes ("0644")
  • Appropriate restart notifications on each configuration change
tasks/pip.yml (2)

1-16: LGTM!

The pip/setuptools/wheel update task with retry logic is appropriate for ensuring a stable Python environment before installing dependencies.


199-219: Good handling of django-celery-email migration.

The task sequence properly removes the unmaintained django-celery-email package before installing the replacement, preventing potential conflicts.

.github/workflows/ci.yml (2)

43-48: Good addition of step ID for dependency tracking.

Adding id: deps enables proper conditional execution of downstream steps.


52-67: QA checks now properly fail the build.

The removal of continue-on-error (implied by absence) ensures QA failures are actionable, directly addressing the PR objective. The conditional if: ${{ !cancelled() && steps.deps.conclusion == 'success' }} on the Tests step ensures tests still run even if QA fails, while properly skipping if dependencies failed.

tasks/nginx.yml (2)

8-24: LGTM!

Directory creation tasks with consistent string-quoted mode values ("0775", "0770") follow Ansible best practices for YAML typing.


88-94: LGTM!

The escaped regexp pattern "#(\\s+)server_tokens off" is correctly formatted for YAML double-quoted strings where backslashes need escaping.

tasks/freeradius.yml (1)

99-108: Verify ignore_errors: true removal after resolving merge conflict.

The PGP key download task still has ignore_errors: true (lines 104 and 344). A previous review flagged this as problematic since it silently continues if all retries fail, causing downstream apt operations to fail with a misleading error.

After resolving the merge conflict, please verify this was addressed. If not, either remove ignore_errors: true or add an explicit assertion to verify the key file exists before proceeding.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

line-length:
max: 162
level: warning
---
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix CRLF line endings to unblock yamllint.

CI is failing with “wrong new line character: expected \n”. Please normalize this file to LF endings (e.g., re-save with LF or run a tool like dos2unix .yamllint.yml) so the lint step passes.

🧰 Tools
🪛 GitHub Actions: Ansible-OpenWISP2 CI Build

[error] 1-1: Yamllint: wrong new line character: expected newline (line 1).

🪛 GitHub Check: Build debian12

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build debian13

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2204

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2404

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🤖 Prompt for AI Agents
In @.yamllint.yml at line 1, The file .yamllint.yml currently uses CRLF line
endings causing yamllint to fail with "wrong new line character: expected \n";
convert/normalize the file to LF line endings (e.g., open and re-save with Unix
(LF) line endings or run a tool like dos2unix .yamllint.yml or use git's
core.autocrlf=false and re-checkout) so the linter can read it correctly; ensure
the commit only changes line endings and verify by running yamllint or git diff
to confirm the line endings are now LF.

openwisp2_usage_metric_collection_periodic_tasks: true
# point {{ inventory_name }} to localhost in /etc/hosts
openwisp2_inventory_hostname_localhost: true
---
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Wrong newline character detected.

The static analysis reports wrong new line character: expected \n. This file likely has CRLF (Windows) line endings instead of LF (Unix) line endings.

Convert the file to Unix line endings:

# Using dos2unix or sed
dos2unix defaults/main.yml
# or
sed -i 's/\r$//' defaults/main.yml
🧰 Tools
🪛 GitHub Check: Build debian12

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build debian13

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2204

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🪛 GitHub Check: Build ubuntu2404

[failure] 1-1:
1:4 [new-lines] wrong new line character: expected \n

🤖 Prompt for AI Agents
In `@defaults/main.yml` at line 1, The file in the diff has Windows CRLF line
endings; convert it to Unix LF line endings (e.g., run a line-ending conversion
tool or strip carriage returns) and commit the change so the file uses \n only;
also ensure repository settings prevent reintroducing CRLF (set core.autocrlf
appropriately or add a .gitattributes rule) so the YAML defaults file remains
LF.

Comment on lines +86 to +111
openwisp2_celery_autoscale:
openwisp2_celery_prefetch_multiplier:
openwisp2_celery_optimization: default
openwisp2_celery_network: true
openwisp2_celery_network_concurrency: 1
openwisp2_celery_network_autoscale:
openwisp2_celery_network_prefetch_multiplier: 10
openwisp2_celery_network_optimization: fair
openwisp2_celery_firmware_upgrader: true
openwisp2_celery_firmware_upgrader_concurrency: 1
openwisp2_celery_firmware_upgrader_autoscale:
openwisp2_celery_firmware_upgrader_prefetch_multiplier: 1
openwisp2_celery_firmware_upgrader_optimization: fair
openwisp2_celery_monitoring: true
openwisp2_celery_monitoring_concurrency: 2
openwisp2_celery_monitoring_autoscale:
openwisp2_celery_monitoring_prefetch_multiplier: 10
openwisp2_celery_monitoring_optimization: fair
openwisp2_celery_task_routes_defaults: true
openwisp2_celery_broker_url: redis://{{ openwisp2_redis_host }}:{{ openwisp2_redis_port }}/3
openwisp2_celery_task_acks_late: true
openwisp2_celery_broker_max_tries: 10
openwisp2_celery_pool:
openwisp2_celery_monitoring_pool:
openwisp2_celery_network_pool:
openwisp2_celery_firmware_upgrader_pool:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CI is failing: empty values in block mapping violate yamllint rules.

The static analysis shows the CI is failing with [empty-values] errors on multiple lines. These variables are declared with empty values (which evaluate to null), but the .yamllint.yml configuration now enforces the empty-values rule.

Either explicitly set these to null or use an empty string/appropriate default:

🔧 Proposed fix to satisfy yamllint empty-values rule
 openwisp2_celery_concurrency: "{{ openwisp2_eventlet_concurrency | default(1) }}"
-openwisp2_celery_autoscale:
-openwisp2_celery_prefetch_multiplier:
+openwisp2_celery_autoscale: null
+openwisp2_celery_prefetch_multiplier: null
 openwisp2_celery_optimization: default
 openwisp2_celery_network: true
 openwisp2_celery_network_concurrency: 1
-openwisp2_celery_network_autoscale:
+openwisp2_celery_network_autoscale: null
 openwisp2_celery_network_prefetch_multiplier: 10
 openwisp2_celery_network_optimization: fair
 openwisp2_celery_firmware_upgrader: true
 openwisp2_celery_firmware_upgrader_concurrency: 1
-openwisp2_celery_firmware_upgrader_autoscale:
+openwisp2_celery_firmware_upgrader_autoscale: null
 openwisp2_celery_firmware_upgrader_prefetch_multiplier: 1
 openwisp2_celery_firmware_upgrader_optimization: fair
 openwisp2_celery_monitoring: true
 openwisp2_celery_monitoring_concurrency: 2
-openwisp2_celery_monitoring_autoscale:
+openwisp2_celery_monitoring_autoscale: null
 openwisp2_celery_monitoring_prefetch_multiplier: 10
 openwisp2_celery_monitoring_optimization: fair
 openwisp2_celery_task_routes_defaults: true
 openwisp2_celery_broker_url: redis://{{ openwisp2_redis_host }}:{{ openwisp2_redis_port }}/3
 openwisp2_celery_task_acks_late: true
 openwisp2_celery_broker_max_tries: 10
-openwisp2_celery_pool:
-openwisp2_celery_monitoring_pool:
-openwisp2_celery_network_pool:
-openwisp2_celery_firmware_upgrader_pool:
+openwisp2_celery_pool: null
+openwisp2_celery_monitoring_pool: null
+openwisp2_celery_network_pool: null
+openwisp2_celery_firmware_upgrader_pool: null
🧰 Tools
🪛 GitHub Check: Build debian12

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping

🪛 GitHub Check: Build debian13

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping

🪛 GitHub Check: Build ubuntu2204

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping

🪛 GitHub Check: Build ubuntu2404

[failure] 110-110:
110:31 [empty-values] empty value in block mapping


[failure] 109-109:
109:34 [empty-values] empty value in block mapping


[failure] 108-108:
108:23 [empty-values] empty value in block mapping


[failure] 101-101:
101:39 [empty-values] empty value in block mapping


[failure] 96-96:
96:46 [empty-values] empty value in block mapping


[failure] 91-91:
91:36 [empty-values] empty value in block mapping


[failure] 87-87:
87:38 [empty-values] empty value in block mapping


[failure] 86-86:
86:28 [empty-values] empty value in block mapping

🤖 Prompt for AI Agents
In `@defaults/main.yml` around lines 86 - 111, Several Celery-related variables
(e.g., openwisp2_celery_autoscale, openwisp2_celery_prefetch_multiplier,
openwisp2_celery_autoscale, openwisp2_celery_network_autoscale,
openwisp2_celery_pool, openwisp2_celery_monitoring_pool,
openwisp2_celery_network_pool, openwisp2_celery_firmware_upgrader_autoscale,
openwisp2_celery_firmware_upgrader_pool) are defined with empty values which
violates the yamllint empty-values rule; update each empty entry to an explicit
value (choose either null or an empty string "" or a sensible default) so the
YAML linter accepts the file (e.g., set those variables to null where
semantically appropriate, or "" if an empty string is preferred) and keep the
non-empty entries like openwisp2_celery_broker_url unchanged.

openwisp2_django_celery_logging: false
openwisp2_postfix_install: true
postfix_smtpd_relay_restrictions_override: "permit_sasl_authenticated, permit_mynetworks, check_relay_domains, reject_unauth_destination, reject"
openwisp2_uwsgi_gid:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Additional empty values causing CI failures.

Lines 115 and 132 also have empty values that trigger the same yamllint violation.

🔧 Proposed fix
-openwisp2_uwsgi_gid:
+openwisp2_uwsgi_gid: null
-openwisp2_admin_allowed_network:
+openwisp2_admin_allowed_network: null

Also applies to: 132-132

🤖 Prompt for AI Agents
In `@defaults/main.yml` at line 115, The YAML contains empty key assignments
(e.g., openwisp2_uwsgi_gid) that trigger yamllint; locate the empty keys such as
openwisp2_uwsgi_gid (and the other empty entry around the same block) and either
remove those key lines entirely if unused, or give them an explicit null value
(~) or a valid value instead of leaving them blank so the file is valid YAML and
passes CI.

command: systemctl status redis-server
changed_when: false
- name: Chcke if redis is running
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the task name typo.

“Chcke” should be “Check” for clarity in play output.

📝 Suggested fix
-    - name: Chcke if redis is running
+    - name: Check if redis is running
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Chcke if redis is running
- name: Check if redis is running
🤖 Prompt for AI Agents
In `@molecule/resources/verify.yml` at line 44, Fix the typo in the Ansible task
name string "Chcke if redis is running" by updating it to "Check if redis is
running" so play output is clear; locate the task name in verify.yml (the task
whose name equals "Chcke if redis is running") and replace the misspelled word.

Comment on lines +241 to +242
=======
---
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for merge conflict markers in the file
echo "=== Checking for merge conflict markers ==="
grep -n "^=======$\|^<<<<<<\|^>>>>>>" tasks/freeradius.yml

echo -e "\n=== Line count ==="
wc -l tasks/freeradius.yml

echo -e "\n=== Content around line 241 ==="
sed -n '238,245p' tasks/freeradius.yml

echo -e "\n=== First 5 lines of file ==="
head -5 tasks/freeradius.yml

echo -e "\n=== Lines 240-242 (context around conflict marker) ==="
sed -n '240,242p' tasks/freeradius.yml

Repository: openwisp/ansible-openwisp2

Length of output: 594


Critical: Unresolved Git merge conflict marker at line 241.

The file contains an unresolved merge conflict marker (======= at line 241). Ansible will fail to parse this file due to the invalid YAML syntax. The file content is duplicated (lines 240-241 show the end of the first version and the merge marker, followed by a new YAML document starting at line 242).

Resolve the merge conflict by keeping only one version of the file content and removing the merge conflict marker. Typically, this means either keeping lines 1-240 or lines 242-481, then deleting the duplicate half.

🤖 Prompt for AI Agents
In `@tasks/freeradius.yml` around lines 241 - 242, Remove the unresolved git merge
marker `=======` and the duplicated YAML document separator `---`, decide which
version to keep (either the first block before the marker or the second block
that begins with `---`), delete the other duplicate block so the file contains a
single valid YAML document, and ensure the resulting content parses as YAML (no
leftover conflict markers).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug/qa] Lint errors are ignored

3 participants