Commit Graph

119 Commits

Author SHA1 Message Date
Jens
eda06334d1 docs: generate README usage section (#1191)
## ℹ️ Description

- Link to the related issue(s): N/A
- Keeps the README Usage command block in sync with the CLI help by
generating it as part of the artifact pipeline.

## 📋 Changes Summary

- Added a README Usage generator that renders normalized English CLI
help between explicit README markers.
- Added `generate-readme-commands` and wired it into
`generate-artifacts`.
- Extended generated artifact checks to detect README Usage drift
without mutating files.
- Added focused unit tests for marker replacement, locale/executable
normalization, ANSI stripping, and command coverage.
- No dependency changes introduced.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [x]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Kept the README “Usage” section automatically synchronized with the
app’s CLI help.
* Added a new `--preserve-local-settings` option to the CLI help output.

* **Bug Fixes**
* Improved and clarified CLI command/option descriptions (notably around
`verify`, `extend`, and `update` behavior).
* CI now fails early if the generated README “Usage” content is out of
date.

* **Documentation**
* Regenerated the README “Usage” console block to reflect the latest
help text and command details.
* Updated agent guidance to match the expanded README generation
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-01 16:36:03 +02:00
Jens
780743aee2 refactor: separate download_ads workflow modes (#1166)
## ℹ️ Description

Extract four private async helpers from `download_ads()` to separate
per-mode workflow logic.

- Motivation: Reduce function complexity, enable tighter pylint
thresholds

## 📋 Changes Summary

- Extracted `_fetch_published_ads_by_id` — fetch and build published ads
lookup dict
- Extracted `_download_all_ads` — download all ads from overview page
- Extracted `_download_new_ads` — download only unsaved ads (preserving
URL dedup behavior)
- Extracted `_download_ads_by_ids` — download specific ads by numeric ID
(preserving distinct warning semantics)
- Slimmed `download_ads()` to ~48-line orchestrator (was ~124 lines)
- Restructured `translations.de.yaml` — messages moved to per-function
sections
- Ratcheted pylint thresholds: max-statements 90→76, max-branches 35→31

### ⚙️ Type of Change
- [ ] 🐞 Bug fix
- [ ]  New feature
- [ ] 💥 Breaking change

Internal refactor, no user-visible behavior change.

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`): 1286 passed, 4 skipped
- [x] I have formatted the code (`pdm run format`)
- [x] I have verified that linting passes (`pdm run lint`)
- [x] I have updated documentation where necessary.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Strengthened code quality standards and refactored internal ad
downloading logic for improved maintainability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 11:28:23 +02:00
Jens
32c6d3985f refactor: split published ads pagination (#1164)
## ℹ️ Description

Extract content-decoding and paging-navigation logic from
`fetch_published_ads` into focused module-level helpers, reducing
function complexity while preserving all behavior.

## 📋 Changes Summary

- **`published_ads.py`**: Extracted `_parse_published_ads_page()`
(content decode, JSON parse, payload validation, ad filtering) and
`_determine_next_page()` (paging metadata resolution,
last-page/empty-ads guards). Main `fetch_published_ads` is now a clean
orchestration loop: request → response check → parse → extend →
paginate.
- **`translations.de.yaml`**: Split the single `fetch_published_ads:`
translation section into three sections (`fetch_published_ads:`,
`_parse_published_ads_page:`, `_determine_next_page:`) matching the new
caller function names. Added missing translation for "No paging dict
found on page %s".
- **`pyproject.toml`**: Lowered `max-statements` from 115 to 90.
- **`pydantics.py`**: Added `PLR0915` noqa for `__get_message_template`
(already an approved exception for the generated pydantic error-code
mapping).

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Improved published-ads fetching by separating response decoding and
pagination handling into dedicated helpers, while preserving existing
behavior.
* Reworked strict-mode validation and stopping conditions for clearer
control flow and more consistent error handling.

* **Chores**
  * Updated linting limits to enforce stricter code quality.
  * Added targeted lint suppression for an overly complex section.

* **Tests**
* Expanded unit test coverage for published-ads content decoding and
strict-mode failure cases across multiple malformed/edge responses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-21 22:39:36 +02:00
Jens
b5e2769f2d refactor: extract special attribute helpers from publishing form (#1161)
## ℹ️ Description

Extract three module-level helpers from `set_special_attributes()` in
`publishing_form.py` to reduce local variable count and improve
readability. `set_special_attributes` had 27 locals — after extraction
it has 10.

- Behavior-preserving: no change to exception propagation, logging, or
control flow.
- `condition_s` probe stays in main to avoid translation-key churn.

## 📋 Changes Summary

- Extract `_build_special_attribute_xpath()` — Pure XPath construction
(fix comment: five → six patterns)
- Extract `_resolve_special_attribute_element()` — Wraps `web_find_all`
+ candidate pick, propagates exceptions uncaught
- Extract `_set_special_attribute_input()` — 6-branch input-type
dispatch; hidden-input returns without logging, caller logs once
- Rewire `set_special_attributes` to call the three helpers
- Ratchet `pyproject.toml`: PLR0914 max-locals 27 → 20
- Update `translations.de.yaml`: move 4 debug messages + add `Failed to
set attribute` under `_set_special_attribute_input`

### ⚙️ Type of Change
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)
- [x] ♻️ Refactor (non-breaking restructuring of existing code)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`). → 1268 passed, 4 skipped
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary. → Translation file
updated


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **Chores**
* Tightened linting rules by lowering the allowed maximum number of
local variables.

* **Refactor**
* Reworked special-attribute DOM handling to use clearer selection and
value-setting paths for different field types, including improved
fallback behavior.

* **Tests**
* Added async unit test coverage for `set_special_attributes`, including
input fallback behavior and error cases for unsupported checkbox values
and missing hidden input metadata.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-21 19:36:56 +02:00
Jens
c8fb72ae9e refactor: split command orchestration handlers (#1155)
## ℹ️ Description
- Link to the related issue(s): Issue #
- Refactors the CLI command orchestration boundary so command dispatch
remains visible while command execution lives in focused handlers.

## 📋 Changes Summary
- Split command execution paths in `KleinanzeigenBot.run()` into named
handlers for configuration, verification, update checks, content
hashing, and ad workflows.
- Preserved literal command dispatch cases and command-specific behavior
for update checks, download validation, browser login, and cleanup.
- Moved German translation keys to the new handler caller names for
existing runtime messages.
- Added focused tests for update-check behavior, download validation
ordering, workspace state usage, and translation key placement.
- Lowered the configured branch and cyclomatic complexity limits now
that the command orchestration boundary is simpler.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Strengthened code quality standards by implementing stricter
complexity thresholds in the development configuration.

* **Refactor**
* Reorganized command handler architecture to improve internal code
structure and maintainability.

* **Tests**
* Significantly expanded test coverage to validate command execution,
argument handling, and translation data integrity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-20 12:33:06 +02:00
Jens
75f34775e2 refactor: reduce return-count complexity (#1154)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Reduces return-count complexity in the update checker while preserving
existing update-check behavior.

## 📋 Changes Summary

- Extracted GitHub release selection and commit-ish validation from
`check_for_updates` into a private helper.
- Moved German translation keys for logger calls that now originate from
the helper.
- Ratcheted Ruff/Pylint `max-returns` from 10 to 8.

### ⚙️ Type of Change

- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist

- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* The update checker now correctly handles the latest update channel by
excluding pre-release versions and recommending only stable releases.

* **Chores**
  * Code quality configuration settings were adjusted.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-20 10:49:22 +02:00
Jens
66cab91898 refactor: reduce return-count complexity (#1153)
## ℹ️ Description

This refactor reduces return-count complexity for the next PLR0911
ratchet while preserving existing behavior.

No related issue.

## 📋 Changes Summary

- Extract German and English pluralization helpers while keeping
translation and prefix handling in `pluralize()`.
- Remove one early return from `check_for_updates()` by sharing the
existing final state update/save path.
- Ratchet Ruff `PLR0911` `max-returns` from 12 to 10.
- Document the one-lower discovery result: `check_for_updates()` is the
next limiter at 10 returns for `max-returns = 9`.
- No dependencies, configuration requirements, documentation updates, or
generated artifacts are introduced.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary. No documentation
changes were needed.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Reorganized pluralization logic and simplified update-checking control
flow for improved code maintainability.

* **Tests**
* Expanded test coverage for pluralization with additional German and
English language variations.

* **Chores**
  * Updated code quality configuration standards.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 23:15:27 +02:00
Jens
e8752ce10e refactor: reduce price reduction complexity (#1152)
## ℹ️ Description

- Link to the related issue(s): Issue #
- Extracts the first complexity hotspot in the ratcheting sequence by
separating the final auto price-reduction application flow from
`apply_auto_price_reduction`.
- This keeps behavior unchanged while allowing the Ruff `max-returns`
transitional limit to move from 13 to 12.

## 📋 Changes Summary

- Extracted `_apply_price_reduction_decision` for the final
price-reduction mutation/logging phase.
- Moved German translations for the relocated `LOG.info` calls to the
new helper function key.
- Lowered `tool.ruff.lint.pylint.max-returns` from 13 to 12.
- No dependency or runtime configuration changes introduced.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

Refactor-only change; none of the listed user-facing change types apply.

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated linting configuration thresholds for code quality standards.

* **Refactor**
  * Improved internal code organization for better maintainability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 16:19:33 +02:00
Jens
801ed0e223 refactor: remove KleinanzeigenBot re-export from package root (#1150)
## ℹ️ Description

- Link to the related issue(s): N/A
- Removes the unsupported package-root `KleinanzeigenBot` re-export
after tests were moved to their owning modules.
- Updates remaining tests and `conftest.py` to import `KleinanzeigenBot`
from `kleinanzeigen_bot.app` directly.
- Re-enables Ruff's mccabe complexity checks and tightens high Pylint
complexity thresholds left over from the old package-root monolith.

## 📋 Changes Summary

- Removed `KleinanzeigenBot` from `src/kleinanzeigen_bot/__init__.py`.
- Updated all remaining test imports to use `from kleinanzeigen_bot.app
import KleinanzeigenBot`.
- Enabled `C90`/mccabe complexity linting with an explicit
`max-complexity`.
- Lowered the broad Pylint limits for branches, locals, returns,
statements, and public methods.
- Kept the generated/static pydantic error-code mapping explicitly
exempted from complexity checks.
- No runtime behavior, CLI, config, translations, or ad YAML changes.
- No new dependencies.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [x] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Reorganized internal module import structure for consistency across
the codebase.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 15:00:45 +02:00
Jens
faedfad23c refactor: extract CLI bootstrap (#1089)
## ℹ️ Description
This pull request extracts CLI parsing and runtime bootstrap into
dedicated modules while keeping the existing command behavior intact.

- Link to the related issue(s): Issue #
- Describe the motivation and context for this change.

## 📋 Changes Summary

- Moved CLI parsing/help logic into `src/kleinanzeigen_bot/cli.py`.
- Moved workspace resolution, config loading, browser config
application, and file logging setup into
`src/kleinanzeigen_bot/runtime_config.py`.
- Kept `python -m kleinanzeigen_bot` behavior unchanged via
`src/kleinanzeigen_bot/__main__.py`.
- Preserved frozen build startup by importing `runtime_config`
statically.
- Added focused tests for CLI parsing and runtime bootstrap behavior.
- Updated contributor docs and translation entries for the moved
user-facing messages.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
  * Clarified bootstrap docs to state the app’s CLI-driven entry point.

* **New Features**
* Localized CLI with improved argument parsing, help text and banner
control.
* Runtime bootstrapping: default-config creation, env-var credential
placeholders, category loading, workspace-aware browser profile
handling, and optional file logging.

* **Refactor**
* Centralized startup into the CLI/runtime layer; package entrypoint now
delegates to the CLI.

* **Tests**
* Added/updated unit and smoke tests covering CLI, runtime config,
workspace behavior and create-config.

* **Chores**
* Updated project script wrappers to print tool headers before running
linters.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-07 07:05:53 +02:00
Jens
82020fb3dc ci: publish multi-arch docker manifest (#1080)
## ℹ️ Description

- Link to the related issue(s): Issue #1077
- Fixes GHCR `:latest` publishing so the amd64 and arm64 Docker matrix
jobs no longer race by pushing the same single-arch tag.

## 📋 Changes Summary

- Publish architecture-specific Docker image tags from the Ubuntu matrix
jobs (`:latest-amd64` / `:latest-arm64`).
- Add a dependent `publish-docker-manifest` job that stitches those
per-arch tags into a multi-arch `:latest` manifest using Docker Buildx.
- Add workflow-level concurrency so `main` publishing runs are
serialized while non-main runs cancel older attempts.
- Add `actionlint-py` as a PDM-managed dev dependency and include
`actionlint` in `pdm run lint`.
- Fix existing workflow shellcheck findings reported by `actionlint`.

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [ ] I have updated documentation where necessary. — N/A, CI-only
behavior and dev dependency wiring.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Optimized CI/CD workflow serialization and concurrent run handling for
improved build efficiency.
* Enhanced Docker publishing to support multi-architecture image
manifest generation.
* Added GitHub Actions workflow validation to the development tooling
pipeline.
* Updated development dependencies and extended linting script coverage.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-05 10:15:14 +02:00
Jens
debc517c8a chore: rename fix_nodriver_encoding.py to fix_nodriver.py (#1073)
## ℹ️ Description

The script `fix_nodriver_encoding.py` now applies multiple patches
(encoding fix, flat-mode session retry on `-32601`). The old name was
misleading.

### ⚙️ Type of Change
- [x] 🏗️ Chore / maintenance

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated build configuration for improved setup process.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-02 09:01:52 +02:00
Jens
36f5fc18c1 build: migrate nodriver to upstream 0.50.3 with post-install compat fixes (#1071) 2026-06-01 22:42:58 +02:00
Jens
bd60a317c5 refactor: centralize condition normalization (#1055)
## ℹ️ Description
* Centralize `condition_s` normalization before dialog-specific
handling.

- Link to the related issue(s): Fix #1023
- Describe the motivation and context for this change.

## 📋 Changes Summary

- Added a shared `_normalize_condition()` helper and reused it in both
condition code paths.
- Preserved legacy warning behavior and canonical-first radio probing
with legacy fallback.
- Removed a redundant snapshot-style unit test and updated lint
configuration for existing PLW0717 noise.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Normalized item condition handling and added deprecation warnings for
legacy condition formats; fallbacks now prefer canonical condition
codes.

* **Bug Fixes**
* Probe/lookup timeouts during condition handling now propagate instead
of being silently skipped.

* **Tests**
* Removed an outdated condition-dialog test and added tests verifying
missing-control warning behavior and timeout propagation.

* **Chores**
  * Updated linting configuration.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/Second-Hand-Friends/kleinanzeigen-bot/pull/1055?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-24 18:13:18 +02:00
Jens
bbbf1d0f2b chore: pin nodriver to GitHub fork (#1035)
## ℹ️ Description
*Replace the PyPI-pinned nodriver dependency with a GitHub-only fork
pinned to a commit SHA.*

- Link to the related issue(s): #1033
- Motivation:
  - Upstream has no update for issue.
  - Upstream still has UTF-8 issues.
- There is no practical way to coordinate changes upstream because of
the non-contributor policy (no issues/PRs from non-contributors).
- Fixing UTF-8 handling and keeping CDP mappings current is easy in a
custom fork.
- We can switch back to upstream once it ships an update that addresses
these issues.
- Tracking of this temporary change is done in #1036  

## 📋 Changes Summary

- Updated the nodriver dependency in `pyproject.toml` to a GitHub direct
reference pinned to commit `c465695769a0e99d9154b90e61c521d593732789`.
- Regenerated `pdm.lock` for the new VCS dependency.
- Updated a nodriver source comment to point at the fork repository.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated browser automation dependency to use a specific fork version
for improved web scraping compatibility.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/Second-Hand-Friends/kleinanzeigen-bot/pull/1035)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-11 21:35:04 +02:00
Jens
71028ea844 fix: serialize downloaded ad timestamps as schema-compliant strings (#863)
## ℹ️ Description
- Link to the related issue(s): Issue #
- Fixes drift where `pdm run app download` wrote timestamp values in
YAML-native datetime form that could violate `schemas/ad.schema.json`
string expectations.
- Ensures downloaded ads persist `created_on`/`updated_on` as
JSON-serialized ISO-8601 strings and adds a regression test validating
written YAML against the schema.

## 📋 Changes Summary
- Updated downloader save path to use `ad_cfg.model_dump(mode =
\"json\")` before writing YAML in `src/kleinanzeigen_bot/extract.py`.
- Updated existing `download_ad` unit assertion to match JSON-mode
serialization.
- Added `test_download_ad_writes_schema_compliant_yaml` in
`tests/unit/test_extract.py` that writes a real tmp YAML file and
validates it against `schemas/ad.schema.json` with `jsonschema`.
- Added dev dependency `jsonschema>=4.26.0` (and lockfile updates).
- Dependencies/config updates introduced: new dev dependency
(`jsonschema`) for full schema validation in tests.

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

# Release Notes

* **Bug Fixes**
* Improved ad data serialization to ensure consistent JSON format when
saving ad configurations.

* **Tests**
  * Added schema validation tests to verify ad YAML output compliance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-08 23:10:16 +01:00
Jens
930b3f6028 feat: unify pdm test defaults and verbosity controls (#836) 2026-02-23 16:44:13 +01:00
Jens
398286bcbc ci: check generated schema and default config artifacts (#825)
## ℹ️ Description
- Link to the related issue(s): N/A
- Add a CI guard that fails when generated artifacts are out of sync,
motivated by preventing missing schema updates and keeping generated
reference files current.
- Add a committed `docs/config.default.yaml` as a user-facing default
configuration reference.

## 📋 Changes Summary
- Add `scripts/check_generated_artifacts.py` to regenerate schema
artifacts and compare tracked outputs (`schemas/*.json` and
`docs/config.default.yaml`) against generated content.
- Run the new artifact consistency check in CI via
`.github/workflows/build.yml`.
- Add `pdm run generate-config` and `pdm run generate-artifacts` tasks,
with a cross-platform-safe delete in `generate-config`.
- Add generated `docs/config.default.yaml` and document it in
`docs/CONFIGURATION.md`.
- Update `schemas/config.schema.json` with the
`diagnostics.timing_collection` property generated from the model.

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [x]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Added a reference link to the default configuration snapshot for
easier access to baseline settings.

* **Chores**
* Added a CI build-time check that validates generated schemas and the
default config and alerts when regeneration is needed.
* Added scripts to generate the default config and to sequence artifact
generation.
* Added a utility to produce standardized schema content and compare
generated artifacts.
  * Minor tweak to schema generation success messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-16 16:56:31 +01:00
Jens
eda1b4d0ec feat: add browser profile XDG support and documentation (#777) 2026-01-23 22:45:22 +01:00
Jens
7468ef03dc feat: add core XDG path resolution module (#775)
## ℹ️ Description
Core module for XDG Base Directory specification support.

- Link to the related issue(s): N/A (new feature)
- Adds portable and XDG installation mode path resolution

## 📋 Changes Summary

- New `xdg_paths.py` module with 11 path resolution functions
- Comprehensive test suite (32 tests, 95% coverage)
- German translations for all user-facing strings
- Moved `platformdirs` from dev to runtime dependencies

**Part 1 of 3 for XDG support**
- Depends on: None
- Preserves: extend command, ContactDefaults.location

### ⚙️ Type of Change
- [x]  New feature (adds new functionality without breaking existing
usage)

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for portable and XDG-standard installation modes for
flexible config, cache, and state storage.

* **Chores**
* Added a runtime dependency to handle platform-specific directory
locations.

* **Tests**
* Added comprehensive unit tests covering path resolution,
installation-mode detection, interactive prompts, and Unicode path
handling.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-23 06:39:44 +01:00
Jens
183f0ab4e1 fix: raise pydantic version for compatibility (#773)
## ℹ️ Description
Raise Pydantic version cause we use features that require at least
v2.11.

- Link to the related issue(s): PR #772

## 📋 Changes Summary

- Set min version to v2.11

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated project dependencies to improve compatibility and stability.

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

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-01-18 17:36:37 +01:00
Jens
920ddf5533 feat: Add automatic price reduction on reposts (#691) 2025-12-17 20:31:58 +01:00
Jens
220c01f257 fix: eliminate async safety violations and migrate to pathlib (#697)
## ℹ️ Description
Eliminate all blocking I/O operations in async contexts and modernize
file path handling by migrating from os.path to pathlib.Path.

- Link to the related issue(s): #692 
- Get rid of the TODO in pyproject.toml
- The added debug logging will ease the troubleshooting for path related
issues.

## 📋 Changes Summary

- Enable ASYNC210, ASYNC230, ASYNC240, ASYNC250 Ruff rules
- Wrap blocking urllib.request.urlopen() in run_in_executor
- Wrap blocking file operations (open, write) in run_in_executor
- Replace blocking os.path calls with async helpers using
run_in_executor
- Replace blocking input() with await ainput()
- Migrate extract.py from os.path to pathlib.Path
- Use Path() constructor and / operator for path joining
- Use Path.mkdir(), Path.rename() in executor instead of os functions
- Create mockable _path_exists() and _path_is_dir() helpers
- Add debug logging for all file system operations

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [X] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [X] I have reviewed my changes to ensure they meet the project's
standards.
- [X] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [X] I have formatted the code (`pdm run format`).
- [X] I have verified that linting passes (`pdm run lint`).
- [X] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
  * Made user prompt non‑blocking to improve responsiveness.
* Converted filesystem/path handling and prefs I/O to async‑friendly
operations; moved blocking network and file work to background tasks.
* Added async file/path helpers and async port‑check before browser
connections.

* **Tests**
* Expanded unit tests for path helpers, image download success/failure,
prefs writing, and directory creation/renaming workflows.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-05 20:53:40 +01:00
Jens
4870bc223f chore: improve coverage reporting (#683)
## ℹ️ Description
* Restrict coverage reporting to library files and collect per-suite
coverage data for Codecov’s flags.
- Link to the related issue(s): Issue #N/A
- Describe the motivation and context for this change.

## 📋 Changes Summary
- add `coverage:prepare` and per-suite `COVERAGE_FILE`s so each test
group writes its own sqlite and XML artifacts without appending
- replace the shell scripts with `scripts/coverage_helper.py`, scope the
report to `src/kleinanzeigen_bot/*`, and add logging/validation around
cleanup, pytest runs, and data combining
- ensure the helper works in CI (accepts extra pytest args, validates
file presence)

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.
2025-11-16 17:46:02 +01:00
Jens
33d1964f86 feat: speed up and stabilise test suite (#676)
## ℹ️ Description
*Provide a concise summary of the changes introduced in this pull
request.*

- Link to the related issue(s): Issue #
- Describe the motivation and context for this change.

Refactors the test harness for faster and more reliable feedback: adds
deterministic time freezing for update checks, accelerates and refactors
smoke tests to run in-process, defaults pytest to xdist with durations
tracking, and adjusts CI triggers so PRs run the test matrix only once.

## 📋 Changes Summary

- add pytest-xdist + durations reporting defaults, force deterministic
locale and slow markers, and document the workflow adjustments
- run smoke tests in-process (no subprocess churn), mock update
checks/logging, and mark slow specs appropriately
- deflake update check interval tests by freezing datetime and simplify
FixedDateTime helper
- limit GitHub Actions `push` trigger to `main` so feature branches rely
on the single pull_request run

### ⚙️ Type of Change
Select the type(s) of change(s) included in this pull request:
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [x]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Ensure tests run in a consistent English locale and restore prior
locale after each run
  * Mark integration scraping tests as slow for clearer categorization
* Replace subprocess-based CLI tests with an in-process runner that
returns structured results and captures combined stdout/stderr/logs;
disable update checks during smoke tests
* Freeze current time in update-check tests for deterministic assertions
* Add mock for process enumeration in web‑scraping unit tests to
stabilize macOS-specific warnings

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-12 21:29:51 +01:00
Jens
71feedc700 fix: pin nodriver to 0.47 (#675)
## ℹ️ Description
- Link to the related issue(s): Issue #N/A
- Describe the motivation and context for this change.

Pin `nodriver` to the last known good 0.47 series so we can avoid the
UTF-8 decoding regression in 0.48.x that currently breaks our local
mypy/linting runs.

## 📋 Changes Summary
- lock runtime dependency `nodriver` to `0.47.*` with an inline comment
describing the upstream regression
- refresh `pdm.lock` so local/CI installs stay on the pinned version

### ⚙️ Type of Change
- [x] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ]  New feature (adds new functionality without breaking existing
usage)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)


##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [ ] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [ ] I have formatted the code (`pdm run format`).
- [ ] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2025-11-10 11:36:44 +01:00
Sebastian Thomschke
dadd08aedb build: upgrade to Python 3.14 (#636)
Co-authored-by: Jens <1742418+1cu@users.noreply.github.com>
2025-10-14 15:56:35 +02:00
Jens
36ca178574 feat: upgrade nodriver from 0.39 to 0.47 (#635)
## ℹ️ Description
Upgrade nodriver dependency from pinned version 0.39.0 to latest 0.47.0
to resolve browser startup issues and JavaScript evaluation problems
that affected versions 0.40-0.44.

- Link to the related issue(s): Resolves nodriver compatibility issues
- This upgrade addresses browser startup problems and window.BelenConf
evaluation failures that were blocking the use of newer nodriver
versions.

## 📋 Changes Summary

- Updated nodriver dependency from pinned 0.39.0 to >=0.47.0 in
pyproject.toml
- Fixed RemoteObject handling in web_execute method for nodriver 0.47
compatibility
- Added comprehensive BelenConf test fixture with real production data
structure
- Added integration test to validate window.BelenConf evaluation works
correctly
- Added German translation for new error message
- Replaced real user data with privacy-safe dummy data in test fixtures

### 🔧 Type Safety Improvements

**Added explicit `str()` conversions to resolve type inference issues:**

The comprehensive BelenConf test fixture contains deeply nested data
structures that caused pyright's type checker to infer complex
dictionary types throughout the codebase. To ensure type safety and
prevent runtime errors, I added explicit `str()` conversions in key
locations:

- **CSRF tokens**: `str(csrf_token)` - Ensures CSRF tokens are treated
as strings
- **Special attributes**: `str(special_attribute_value)` - Converts
special attribute values to strings
- **DOM attributes**: `str(special_attr_elem.attrs.id)` - Ensures
element IDs are strings
- **URL handling**: `str(current_img_url)` and `str(href_attributes)` -
Converts URLs and href attributes to strings
- **Price values**: `str(ad_cfg.price)` - Ensures price values are
strings

These conversions are defensive programming measures that ensure
backward compatibility and prevent type-related runtime errors, even if
the underlying data structures change in the future.

### ⚙️ Type of Change
- [x]  New feature (adds new functionality without breaking existing
usage)
- [ ] 🐞 Bug fix (non-breaking change which fixes an issue)
- [ ] 💥 Breaking change (changes that might break existing user setups,
scripts, or configurations)

##  Checklist
Before requesting a review, confirm the following:
- [x] I have reviewed my changes to ensure they meet the project's
standards.
- [x] I have tested my changes and ensured that all tests pass (`pdm run
test`).
- [x] I have formatted the code (`pdm run format`).
- [x] I have verified that linting passes (`pdm run lint`).
- [x] I have updated documentation where necessary.

By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice.
2025-10-12 21:22:46 +02:00
Jens
a2745c03b2 fix: resolve linting errors after dependency updates (#634) 2025-10-12 14:45:59 +02:00
Jens Bergmann
91a40b0116 feat: enhanced folder naming (#599) 2025-08-12 10:43:26 +02:00
Jens Bergmann
1a1633e12d feat: introduce smoke test group and fail-fast test orchestration (#572) 2025-07-09 19:23:52 +02:00
Jens Bergmann
5430f5cdc6 feat: update check (#561)
feat(update-check): add robust update check with interval support, state management, and CLI integration

- Implement version and interval-based update checks with configurable settings
- Add CLI command `kleinanzeigen-bot update-check` for manual checks
- Introduce state file with versioning, UTC timestamps, and migration logic
- Validate and normalize intervals (1d–4w) with fallback for invalid values
- Ensure correct handling of timezones and elapsed checks
- Improve error handling, logging, and internationalization (i18n)
- Add comprehensive test coverage for config, interval logic, migration, and CLI
- Align default config, translations, and schema with new functionality
- Improve help command UX by avoiding config/log loading for `--help`
- Update documentation and README with full feature overview
2025-06-27 07:52:40 +02:00
Heavenfighter
f69ebef643 feat: add new update command to update published ads (#549)
Co-authored-by: Jens Bergmann <1742418+1cu@users.noreply.github.com>
2025-06-16 11:46:51 +02:00
sebthom
23910ffbf5 ci: publish code coverage reports 2025-05-15 22:13:38 +02:00
sebthom
83c0d6adf0 refact: move temp files to /.temp/ 2025-05-15 19:52:41 +02:00
sebthom
85a5cf5224 feat: improve content_hash calculation 2025-05-15 12:07:49 +02:00
sebthom
f1cd597dd8 fix: reduce distribution size 2025-05-15 12:07:49 +02:00
sebthom
6ede14596d feat: add type safe Ad model 2025-05-15 12:07:49 +02:00
sebthom
1369da1c34 feat: add type safe Config model 2025-05-15 12:07:49 +02:00
sebthom
f2e6f0b20b chore: update pyproject.toml 2025-05-12 14:08:50 +02:00
sebthom
314285583e ci: add pip-audit check 2025-05-11 20:14:38 +02:00
sebthom
3e8072973a build: use yamlfix for yaml formatting 2025-04-28 13:17:23 +02:00
sebthom
bda0acf943 refact: enable ruff preview rules 2025-04-28 13:17:23 +02:00
sebthom
ef923a8337 refact: apply consistent formatting 2025-04-28 12:55:28 +02:00
sebthom
fe33a0e461 refact: replace pyright with basedpyright 2025-04-28 12:52:18 +02:00
sebthom
376ec76226 refact: use ruff instead of autopep8,bandit,pylint for linting 2025-04-28 12:51:51 +02:00
sebthom
f0b84ab335 build: simplify pytest config 2025-04-28 12:43:53 +02:00
sebthom
27c7bb56ca fix: downgrading nodriver to 0.39 to address failing browser launch #470 2025-04-07 22:40:41 +02:00
Jens Bergmann
1b004a2a3e Revert "feat: Introduce isort and Python-based code quality tools (#446)"
This reverts commit cfe2b900c7.

The custom scripts introduced to auto-format imports (to enforce project guidelines) caused issues. Specifically, isort’s hardcoded behavior for expanded standard library imports with “as” imports led to unintended formatting. This commit reverts those changes and removes the custom scripts, restoring the project to its previous, stable state.
2025-03-13 11:55:31 +01:00
Jens Bergmann
cfe2b900c7 feat: Introduce isort and Python-based code quality tools (#446) 2025-03-10 06:09:49 +01:00