Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/developer/other-utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ Storage Utilities
``openwisp_utils.storage.CompressStaticFilesStorage``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A static storage backend for compression inheriting from
`django-compress-staticfiles's
<https://pypi.org/project/django-compress-staticfiles/>`_
``CompressStaticFilesStorage`` class.
A static storage backend for minification and compression inheriting from
`django-minify-compress-staticfiles's
<https://github.com/openwisp/django-minify-compress-staticfiles>`_
``MinicompressStorage`` class.

Adds support for excluding file types using
:ref:`OPENWISP_STATICFILES_VERSIONED_EXCLUDE
Expand Down
12 changes: 11 additions & 1 deletion openwisp_utils/releaser/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,17 @@ def run_git_cliff(version=None):
file=sys.stderr,
)
sys.exit(1)

# Pull latest tags before calculating changelog
try:
subprocess.run(
["git", "pull", "--tags"],
capture_output=True,
text=True,
check=True,
)
except subprocess.CalledProcessError as e:
print(f"Warning: Failed to pull tags: {e.stderr}", file=sys.stderr)
# Run git-cliff to calculate changelog
try:
cmd = ["git", "cliff", "--unreleased", "--config", config_path]
if version:
Expand Down
12 changes: 10 additions & 2 deletions openwisp_utils/releaser/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import subprocess
from datetime import datetime
from pathlib import Path
Expand Down Expand Up @@ -70,7 +71,6 @@ def mock_all(mocker):
stdout="main", check_returncode=True
),
}

default_mock = MagicMock(stdout="", stderr="", check_returncode=True)

def subprocess_side_effect(command, *args, **kwargs):
Expand All @@ -92,6 +92,15 @@ def subprocess_side_effect(command, *args, **kwargs):
"openwisp_utils.releaser.version.questionary.select", new=mock_q_select
)

# Mock OPENAI_CHATGPT_TOKEN to None to skip AI summary by default
original_environ_get = os.environ.get

def mock_environ_get(key, default=None):
if key == "OPENAI_CHATGPT_TOKEN":
return None
return original_environ_get(key, default)

mocker.patch("os.environ.get", side_effect=mock_environ_get)
mocks = {
"subprocess": mocker.patch(
"openwisp_utils.releaser.release.subprocess.run",
Expand Down Expand Up @@ -149,5 +158,4 @@ def subprocess_side_effect(command, *args, **kwargs):
}
mocks["check_prerequisites"].return_value = (mock_config, mock_gh_instance)
mocks["load_config"].return_value = mock_config

return mocks
60 changes: 59 additions & 1 deletion openwisp_utils/releaser/tests/test_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,13 @@ def test_run_git_cliff_no_config(mock_find_config):


@patch("openwisp_utils.releaser.changelog.find_cliff_config", return_value="path")
@patch("subprocess.run", side_effect=FileNotFoundError)
@patch("subprocess.run")
def test_run_git_cliff_file_not_found(mock_run, mock_find_config):
# First call (git pull --tags) succeeds, second call (git cliff) raises FileNotFoundError
mock_run.side_effect = [
subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
FileNotFoundError,
]
with pytest.raises(SystemExit):
run_git_cliff()

Expand Down Expand Up @@ -322,6 +327,59 @@ def test_run_git_cliff_with_version_tag(mock_subprocess_run, mock_find_config):
assert "--tag" in executed_cmd and "1.0.0" in executed_cmd


@patch(
"openwisp_utils.releaser.changelog.find_cliff_config",
return_value="path/to/cliff.toml",
)
@patch("openwisp_utils.releaser.changelog.subprocess.run")
def test_run_git_cliff_calls_git_pull_tags(mock_subprocess_run, mock_find_config):
"""Tests that `git pull --tags` is executed before running git-cliff."""
# Mock the subprocess.run to return different results for different calls
mock_subprocess_run.return_value = subprocess.CompletedProcess(
args=[], returncode=0, stdout="Changelog content", stderr=""
)
run_git_cliff()
# Verify that subprocess.run was called twice: once for git pull --tags, once for git-cliff
assert mock_subprocess_run.call_count == 2
# Check the first call is for git pull --tags
first_call_args = mock_subprocess_run.call_args_list[0][0][0]
assert first_call_args == ["git", "pull", "--tags"]
# Check the second call is for git cliff
second_call_args = mock_subprocess_run.call_args_list[1][0][0]
assert (
"git" in second_call_args
and "cliff" in second_call_args
and "--unreleased" in second_call_args
)


@patch(
"openwisp_utils.releaser.changelog.find_cliff_config",
return_value="path/to/cliff.toml",
)
@patch("openwisp_utils.releaser.changelog.subprocess.run")
@patch("builtins.print")
def test_run_git_cliff_git_pull_tags_fails(
mock_print, mock_subprocess_run, mock_find_config
):
"""Tests that `git pull --tags` failure is handled gracefully and git-cliff still runs."""
# First call (git pull --tags) fails, second call (git cliff) succeeds
mock_subprocess_run.side_effect = [
subprocess.CalledProcessError(1, "git pull --tags", stderr="Network error"),
subprocess.CompletedProcess(
args=[], returncode=0, stdout="Changelog content", stderr=""
),
]
run_git_cliff()
# Verify that subprocess.run was called twice despite the first failure
assert mock_subprocess_run.call_count == 2
# Verify warning message was printed
mock_print.assert_called_once()
call_args = mock_print.call_args[0][0]
assert "Warning: Failed to pull tags:" in call_args
assert "Network error" in call_args


def test_process_changelog_dependencies_as_last_section():
"""Tests where 'Dependencies' is the last section."""
changelog_text = (
Expand Down
19 changes: 11 additions & 8 deletions openwisp_utils/storage.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import fnmatch

from compress_staticfiles.storage import (
CompressStaticFilesStorage as BaseCompressStaticFilesStorage,
)
from django.conf import settings
from django_minify_compress_staticfiles.storage import (
MinicompressStorage as BaseMinicompressStorage,
)


class FileHashedNameMixin:
default_excluded_patterns = ["leaflet/*/*.png"]
excluded_patterns = default_excluded_patterns + getattr(
settings, "OPENWISP_STATICFILES_VERSIONED_EXCLUDE", []
)

@property
def excluded_patterns(self):
return self.default_excluded_patterns + getattr(
settings, "OPENWISP_STATICFILES_VERSIONED_EXCLUDE", []
)

def hashed_name(self, name, content=None, filename=None):
if not any(
Expand All @@ -22,8 +25,8 @@ def hashed_name(self, name, content=None, filename=None):

class CompressStaticFilesStorage(
FileHashedNameMixin,
BaseCompressStaticFilesStorage,
BaseMinicompressStorage,
):
"""Like CompressStaticFilesStorage, but allows excluding some files."""
"""Like MinicompressStorage, but allows excluding some files."""

pass
7 changes: 7 additions & 0 deletions openwisp_utils/tests/selenium.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ def _setup_and_call(self, result, debug=False):
stream=None, descriptions=None, verbosity=0
)
super()._setup_and_call(result, debug)
# IMPORTANT: a skip is not a success; propagate it as a skip and stop.
if getattr(result, "skipped", None):
for _, reason in result.skipped:
original_result.addSkip(self, reason)
if hasattr(original_result, "events") and hasattr(result, "events"):
original_result.events = result.events
return
if result.wasSuccessful():
if attempt == 0:
original_result.addSuccess(self)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
zip_safe=False,
install_requires=[
"django-model-utils>=4.5,<5.1",
"django-compress-staticfiles~=1.0.1b",
"django-minify-compress-staticfiles~=1.1.0",
"django-admin-autocomplete-filter~=0.7.1",
"swapper~=1.4.0",
# allow wider range here to avoid interfering with other modules
Expand Down
Loading