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
34 changes: 34 additions & 0 deletions docs/configuration/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ The plugin must have a configuration, located at the top directory; it can be ei
- a YAML file named `.qgis-plugin-ci`
- an INI file named `setup.cfg` with a `[qgis-plugin-ci]` section
- a TOML file (= your actual `pyproject.toml` file) with a `[tool.qgis-plugin-ci]` section.
- a section `[tool:qgis-plugin-ci]` in the plugin's metadata.txt, but `plugin_path` at least has to defined in one of the above configuration files

In the configuration, you should at least provide the following configuration:

Expand Down Expand Up @@ -65,3 +66,36 @@ plugin_path = "qgis_plugin_ci_testing"
github_organization_slug = "opengisch"
project_slug = "qgis-plugin-ci"
```

### Combining minimal configuration with `metadata.txt`

When the main configuration file only sets `plugin_path` (and optionally `changelog_path`), qgis-plugin-ci will look for a `[tool:qgis-plugin-ci]` section in `{plugin_path}/metadata.txt` and merge any options found there. Values in the main config file always take precedence.

This allows reducing the main config file to a single line:

```yaml
# .qgis-plugin-ci
plugin_path: qtribu
```

With the remaining options in `qtribu/metadata.txt`:

```ini
[general]
name=QTribu
[...]

[tool:qgis-plugin-ci]
create_date = 2021-03-02
github_organization_slug=geotribu
project_slug=qtribu
repository_url_raw = https://raw.githubusercontent.com/geotribu/qtribu/
repository_plugin_id = 2733
timezone=Europe/Paris
```

If `metadata.txt` is missing at the expected path, or if the `[tool:qgis-plugin-ci]` section is missing, a warning is emitted and qgis-plugin-ci continues with the minimal configuration.

:::{note}
The `[tool:qgis-plugin-ci]` section is already used by qgis-plugin-ci at packaging time to write runtime values (`commitNumber`, `commitSha1`, `dateTime`). Static configuration options and runtime-written keys can coexist in the same section.
:::
4 changes: 4 additions & 0 deletions docs/partials/_extra_plugin_metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ commitSha1=
dateTime=
```

This same section can also hold **static configuration options**
(e.g. `github_organization_slug`, `timezone`) when the main config file contains
only `plugin_path`. See [Minimal configuration with `metadata.txt`](../configuration/options.md#combining-minimal-configuration-with-metadata-txt).

:::
45 changes: 45 additions & 0 deletions qgispluginci/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,18 @@ def make_from(
)

def load_config(path_to_config_file: Path, file_name: str) -> dict[str, Any]:
"""Load configuration from a file.

Args:
path_to_config_file (Path): path to the configuration file.
file_name (str): file name.

Raises:
configuration_not_found: if any proper configuration is found in the file.

Returns:
dict[str, Any]: configuration as dict
"""
if file_name == "setup.cfg":
config = configparser.ConfigParser()
config.read(path_to_config_file)
Expand Down Expand Up @@ -197,6 +209,39 @@ def explore_config() -> dict[str, Any]:
config_dict = load_config(path_to_config_file, file_name)
else:
config_dict = explore_config()

# load config from metadata.txt
_extra_config_keys = {
k for k in config_dict if k not in ("plugin_path", "changelog_path")
}
if config_dict.get("plugin_path") and not _extra_config_keys:
_metadata_txt = Path(config_dict["plugin_path"]) / "metadata.txt"
if not _metadata_txt.is_file():
logger.warning(
f"Config only defines 'plugin_path' but {_metadata_txt.resolve()} "
"does not exist. Add a '[tool:qgis-plugin-ci]' section in "
"metadata.txt to configure remaining options."
)
else:
_meta_cfg = configparser.ConfigParser()
_meta_cfg.read(_metadata_txt)
if not _meta_cfg.has_section("tool:qgis-plugin-ci"):
logger.warning(
f"No [tool:qgis-plugin-ci] section found in "
f"{_metadata_txt.resolve()}. "
"Add it to avoid keeping a separate config file."
)
else:
_meta_dict = dict(_meta_cfg.items("tool:qgis-plugin-ci"))
config_dict = {
**_meta_dict,
**config_dict,
}
logger.info(
f"Loaded config from {_metadata_txt.resolve()} "
f"[tool:qgis-plugin-ci]: {_meta_dict}"
)

return cls(config_dict)

def __init__(self, definition: dict[str, Any]):
Expand Down
105 changes: 105 additions & 0 deletions test/test_parameters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#! /usr/bin/env python

# standard
import os
import tempfile
import unittest
from datetime import datetime
from pathlib import Path
Expand All @@ -11,6 +13,24 @@


class TestParameters(unittest.TestCase):
# -- helpers ----------------------------------------------------------

@staticmethod
def _minimal_metadata(plugin_dir: Path) -> None:
"""Write a valid metadata.txt with all mandatory fields."""
(plugin_dir / "metadata.txt").write_text(
data="[general]\n"
"name=Quicui\n"
"about=Lorem ipsum GIS ergo sum\n"
"description=Test plugin\n"
"qgisMinimumVersion=3.44.10\n"
"author=Former Esri repentant\n"
"homepage=https://opengisch.github.io/qgis-plugin-ci/\n"
"tracker=https://github.com/opengisch/qgis-plugin-ci/issues\n"
"repository=https://github.com/opengisch/qgis-plugin-ci\n",
encoding="UTF-8",
)

def test_changelog_parameters(self):
"""Test parameters for changelog command."""
# For the changelog command, the configuration file is optional.
Expand Down Expand Up @@ -46,3 +66,88 @@ def test_plugin_repo_url_from_config(self):
self.assertEqual(
"https://opengisch.github.io/qgis-plugin-ci/", parameters.plugin_repo_url
)

def test_metadata_txt_ci_section_merged_when_plugin_path_only(self):
"""[tool:qgis-plugin-ci] from metadata.txt is merged when config only defines plugin_path."""
original_dir = Path.cwd()
with tempfile.TemporaryDirectory() as tmp_dir:
try:
os.chdir(tmp_dir)
plugin_dir = Path(tmp_dir) / "my_plugin"
plugin_dir.mkdir()
self._minimal_metadata(plugin_dir)
# Append [tool:qgis-plugin-ci] section
with (plugin_dir / "metadata.txt").open("a") as fh:
fh.write(
"\n[tool:qgis-plugin-ci]\n"
"github_organization_slug=my_org\n"
"project_slug=my_project\n"
"timezone=Europe/Paris\n"
)
config_file = Path(tmp_dir) / ".qgis-plugin-ci"
config_file.write_text("plugin_path: my_plugin\n")

parameters = Parameters.make_from(path_to_config_file=config_file)

self.assertEqual("my_org", parameters.github_organization_slug)
self.assertEqual("my_project", parameters.project_slug)
self.assertEqual("Europe/Paris", parameters.timezone)
finally:
os.chdir(original_dir)

def test_missing_ci_section_in_metadata_txt_logs_warning(self):
"""A warning is logged when metadata.txt exists but has no [tool:qgis-plugin-ci] section."""
original_dir = Path.cwd()
with tempfile.TemporaryDirectory() as tmp_dir:
try:
os.chdir(tmp_dir)
plugin_dir = Path(tmp_dir) / "my_plugin"
plugin_dir.mkdir()
self._minimal_metadata(plugin_dir)
config_file = Path(tmp_dir) / ".qgis-plugin-ci"
config_file.write_text("plugin_path: my_plugin\n")

with self.assertLogs("qgispluginci.parameters", level="WARNING") as cm:
Parameters.make_from(path_to_config_file=config_file)

self.assertTrue(
any("tool:qgis-plugin-ci" in msg for msg in cm.output),
"Expected a warning mentioning [tool:qgis-plugin-ci]",
)
finally:
os.chdir(original_dir)

def test_missing_metadata_txt_logs_warning(self):
"""A warning is logged when metadata.txt itself is absent."""
original_dir = Path.cwd()
with tempfile.TemporaryDirectory() as tmp_dir:
try:
os.chdir(tmp_dir)
config_file = Path(tmp_dir) / ".qgis-plugin-ci"
config_file.write_text("plugin_path: ghost_plugin\n")

with self.assertLogs("qgispluginci.parameters", level="WARNING") as cm:
with self.assertRaises(FileNotFoundError):
Parameters.make_from(path_to_config_file=config_file)

self.assertTrue(
any(
"ghost_plugin" in msg and "metadata.txt" in msg
for msg in cm.output
),
"Expected a warning mentioning the missing metadata.txt path",
)
finally:
os.chdir(original_dir)

def test_metadata_txt_not_checked_when_config_has_extra_keys(self):
"""metadata.txt is not consulted when the config file has more than plugin_path."""
# Fixture has github_organization_slug in addition to plugin_path → new block must NOT trigger.
# If it were triggered and merged a metadata.txt section with conflicting values,
# the assertion below would still pass (config file takes priority), but any warning
# log would betray the wrong code path.
with self.assertNoLogs("qgispluginci.parameters", level="WARNING"):
parameters = Parameters.make_from(
path_to_config_file=Path("test/fixtures/.qgis-plugin-ci")
)
self.assertEqual("opengisch", parameters.github_organization_slug)
Loading