mirror of
https://github.com/soxoj/maigret.git
synced 2026-08-17 19:25:41 +02:00
+120
@@ -1,5 +1,125 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.2] - 2026-07-01
|
||||
|
||||
## What's Changed
|
||||
* Bump to 0.6.1 by @soxoj in https://github.com/soxoj/maigret/pull/2661
|
||||
* Document Tor/proxy usage, add Advanced usage docs section (closes #544) by @soxoj in https://github.com/soxoj/maigret/pull/2663
|
||||
* fix(checking): reject URLs and emails extracted as usernames by @soxoj in https://github.com/soxoj/maigret/pull/2673
|
||||
* fix(Instagram): refresh rate-limit marker for stale Login title by @soxoj in https://github.com/soxoj/maigret/pull/2674
|
||||
* build(deps-dev): bump black from 26.3.1 to 26.5.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2676
|
||||
* build(deps): bump aiodns from 4.0.0 to 4.0.3 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2677
|
||||
* build(deps-dev): bump tuna from 0.5.13 to 0.5.15 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2683
|
||||
* build(deps): bump lxml from 6.1.0 to 6.1.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2681
|
||||
* build(deps-dev): bump black from 26.5.0 to 26.5.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2680
|
||||
* Added donation links by @soxoj in https://github.com/soxoj/maigret/pull/2686
|
||||
* fix(web): sanitize username in report file paths to prevent path traversal by @sebastiondev in https://github.com/soxoj/maigret/pull/2678
|
||||
* Added 3 sites by @soxoj in https://github.com/soxoj/maigret/pull/2687
|
||||
* build(deps): bump yarl from 1.23.0 to 1.24.2 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2685
|
||||
* Windows version improvements and docs by @soxoj in https://github.com/soxoj/maigret/pull/2690
|
||||
* Enhance README with AI profiling and analysis details by @soxoj in https://github.com/soxoj/maigret/pull/2691
|
||||
* build(deps): bump aiodns from 4.0.3 to 4.0.4 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2684
|
||||
* build(deps): bump idna from 3.15 to 3.16 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2692
|
||||
* build(deps): bump certifi from 2026.4.22 to 2026.5.20 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2689
|
||||
* Add docs FAQ for top search queries; document translation workflow by @soxoj in https://github.com/soxoj/maigret/pull/2694
|
||||
* Fix docs build: dedup link targets, gate SVG badges to HTML by @soxoj in https://github.com/soxoj/maigret/pull/2695
|
||||
* build(deps-dev): bump pytest-rerunfailures from 16.2 to 16.3 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2698
|
||||
* Fix Chinese name false positives: add CJK regexCheck to 27 sites (#2633) by @soxoj in https://github.com/soxoj/maigret/pull/2699
|
||||
* Add Cloudflare bypass (FlareSolverr) support to web UI + docs by @soxoj in https://github.com/soxoj/maigret/pull/2700
|
||||
* build(deps): bump soupsieve from 2.8.3 to 2.8.4 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2697
|
||||
* feature: add keywords parameter and filter by its matching #979 by @dmarakom6 in https://github.com/soxoj/maigret/pull/2702
|
||||
* build(deps): bump socid-extractor from 0.0.28 to 0.1.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2703
|
||||
* build(deps-dev): bump pytest-asyncio from 1.3.0 to 1.4.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2704
|
||||
* build(deps-dev): bump coverage from 7.14.0 to 7.14.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2705
|
||||
* build(deps): bump platformdirs from 4.9.6 to 4.10.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2708
|
||||
* Docker build workflow: update README in DockerHub, change order of labels by @soxoj in https://github.com/soxoj/maigret/pull/2712
|
||||
* Dockerhub build update by @soxoj in https://github.com/soxoj/maigret/pull/2713
|
||||
* build(deps): bump idna from 3.16 to 3.17 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2711
|
||||
* Fix site checks: 4 fixed, 1 disabled by @soxoj in https://github.com/soxoj/maigret/pull/2715
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2716
|
||||
* Fix DNS resolver failures: classify aiodns errors, add --dns-resolver threaded fallback (#2688) by @soxoj in https://github.com/soxoj/maigret/pull/2717
|
||||
* Graceful Ctrl+C + error UX improvements by @soxoj in https://github.com/soxoj/maigret/pull/2719
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2720
|
||||
* Fix site checks: 1 fixed, 8 disabled, 2 added by @soxoj in https://github.com/soxoj/maigret/pull/2722
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2723
|
||||
* Added sponsor logo and text to the README files by @soxoj in https://github.com/soxoj/maigret/pull/2728
|
||||
* Added sponsor logo and CLI proxy recommendation by @soxoj in https://github.com/soxoj/maigret/pull/2729
|
||||
* refactor: update query for leetcode by @kaifcodec in https://github.com/soxoj/maigret/pull/2727
|
||||
* Sponsorship text update for README by @soxoj in https://github.com/soxoj/maigret/pull/2730
|
||||
* refactor: update instagram entry structure by @kaifcodec in https://github.com/soxoj/maigret/pull/2731
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2733
|
||||
* build(deps): bump aiohttp from 3.13.5 to 3.14.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2736
|
||||
* Update PDF dependency for web variant by @7677865466 in https://github.com/soxoj/maigret/pull/2739
|
||||
* build(deps): bump idna from 3.17 to 3.18 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2735
|
||||
* Warn on engine dict merge conflicts by @puneetdixit200 in https://github.com/soxoj/maigret/pull/2737
|
||||
* Fix site checks: 2 fixed, 2 disabled by @soxoj in https://github.com/soxoj/maigret/pull/2745
|
||||
* Refactor error detection and username extraction by @ashton-andersonaap in https://github.com/soxoj/maigret/pull/2701
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2747
|
||||
* fix: update_site() now replaces list element instead of local variable by @ashvinctrl in https://github.com/soxoj/maigret/pull/2751
|
||||
* build(deps): bump aiohttp from 3.14.0 to 3.14.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2748
|
||||
* fix: pass value v not key k to is_country_tag in generate_report_context by @ashvinctrl in https://github.com/soxoj/maigret/pull/2753
|
||||
* Add Tor entries for Whonix Forum and Stacker News by @nyxst4ck in https://github.com/soxoj/maigret/pull/2755
|
||||
* build(deps): bump pypdf from 6.10.2 to 6.12.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2767
|
||||
* Fix async activation retry handling by @fancyboi999 in https://github.com/soxoj/maigret/pull/2765
|
||||
* build(deps-dev): bump pytest from 9.0.3 to 9.1.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2772
|
||||
* build(deps): bump pyinstaller from 6.20.0 to 6.21.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2773
|
||||
* fix(data): fix absence string for myjane by @0xseal in https://github.com/soxoj/maigret/pull/2770
|
||||
* build(deps): bump cryptography from 46.0.7 to 48.0.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2775
|
||||
* build(deps): bump pypdf from 6.12.0 to 6.13.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2776
|
||||
* build(deps): bump svglib from 1.6.0 to 2.0.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2777
|
||||
* Add AI Wiki links by @soxoj in https://github.com/soxoj/maigret/pull/2778
|
||||
* build(deps): bump svglib from 2.0.0 to 2.0.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2782
|
||||
* docs: fix typos by @juliosuas in https://github.com/soxoj/maigret/pull/2779
|
||||
* build(deps): bump certifi from 2026.5.20 to 2026.6.17 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2781
|
||||
* Fix HackerNews and Rajce.net false positives by @Ashley-996 in https://github.com/soxoj/maigret/pull/2780
|
||||
* build(deps): bump svglib from 2.0.1 to 2.0.2 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2783
|
||||
* build(deps): bump pypdf from 6.13.0 to 6.13.3 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2784
|
||||
* Update README with sponsorship details by @soxoj in https://github.com/soxoj/maigret/pull/2785
|
||||
* ORCID support by @soxoj in https://github.com/soxoj/maigret/pull/2786
|
||||
* Automated Sites List Update by @github-actions[bot] in https://github.com/soxoj/maigret/pull/2787
|
||||
* Fix error-percentage rounding tripping thresholds at whole-percent granularity by @jichaowang02-lang in https://github.com/soxoj/maigret/pull/2788
|
||||
* Code cleanup and refactoring after ponytail audit by @soxoj in https://github.com/soxoj/maigret/pull/2789
|
||||
* build(deps-dev): bump pytest from 9.1.0 to 9.1.1 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2793
|
||||
* test: cover #2666 TODO-test paths (cookie_jar, extract_ids_from_results) by @aznikline in https://github.com/soxoj/maigret/pull/2795
|
||||
* build(deps): update stem requirement from >=1.8.1 to >=1.8.2 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2790
|
||||
* build(deps): update chardet requirement from >=5.0.0 to >=7.4.3 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2797
|
||||
* refactor: drop dead 'if not dictionary' guards across report paths by @aznikline in https://github.com/soxoj/maigret/pull/2796
|
||||
* build(deps-dev): bump coverage from 7.14.1 to 7.14.3 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2798
|
||||
* build(deps): update requests-futures requirement from >=1.0.0 to >=1.0.2 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2794
|
||||
* build(deps): update mock requirement from >=4.0.3 to >=5.2.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2791
|
||||
* build(deps): update future requirement from >=0.18.3 to >=1.0.0 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2792
|
||||
* Refactor sponsor section in README.md by @soxoj in https://github.com/soxoj/maigret/pull/2799
|
||||
* Add archive.org and archive.is links to profile URL blocks by @yyq1043-cloud in https://github.com/soxoj/maigret/pull/2803
|
||||
* Fix get_dict_ascii_tree ignoring new_line=False by @jichaowang02-lang in https://github.com/soxoj/maigret/pull/2805
|
||||
* Compare raw error percentage against threshold (don't round before the check) by @jichaowang02-lang in https://github.com/soxoj/maigret/pull/2809
|
||||
* Fix URLMatcher eating leading host characters (unescaped dots in www./m. prefix) by @jichaowang02-lang in https://github.com/soxoj/maigret/pull/2808
|
||||
* fix: preserve report error reasons by @Sushanth012 in https://github.com/soxoj/maigret/pull/2802
|
||||
* Add Neo4j Cypher export (--neo4j) by @thunderstornX in https://github.com/soxoj/maigret/pull/2774
|
||||
* Added docs for Neo4j integration by @soxoj in https://github.com/soxoj/maigret/pull/2818
|
||||
* Make include-tag site filter case-insensitive (match the exclude filter) by @jichaowang02-lang in https://github.com/soxoj/maigret/pull/2811
|
||||
* build(deps): bump python-bidi from 0.6.10 to 0.6.11 by @dependabot[bot] in https://github.com/soxoj/maigret/pull/2825
|
||||
* Fix site checks: 6 fixed, 5 disabled by @soxoj in https://github.com/soxoj/maigret/pull/2826
|
||||
|
||||
## New Contributors
|
||||
* @sebastiondev made their first contribution in https://github.com/soxoj/maigret/pull/2678
|
||||
* @dmarakom6 made their first contribution in https://github.com/soxoj/maigret/pull/2702
|
||||
* @kaifcodec made their first contribution in https://github.com/soxoj/maigret/pull/2727
|
||||
* @7677865466 made their first contribution in https://github.com/soxoj/maigret/pull/2739
|
||||
* @puneetdixit200 made their first contribution in https://github.com/soxoj/maigret/pull/2737
|
||||
* @ashton-andersonaap made their first contribution in https://github.com/soxoj/maigret/pull/2701
|
||||
* @ashvinctrl made their first contribution in https://github.com/soxoj/maigret/pull/2751
|
||||
* @nyxst4ck made their first contribution in https://github.com/soxoj/maigret/pull/2755
|
||||
* @fancyboi999 made their first contribution in https://github.com/soxoj/maigret/pull/2765
|
||||
* @0xseal made their first contribution in https://github.com/soxoj/maigret/pull/2770
|
||||
* @Ashley-996 made their first contribution in https://github.com/soxoj/maigret/pull/2780
|
||||
* @jichaowang02-lang made their first contribution in https://github.com/soxoj/maigret/pull/2788
|
||||
* @aznikline made their first contribution in https://github.com/soxoj/maigret/pull/2795
|
||||
* @yyq1043-cloud made their first contribution in https://github.com/soxoj/maigret/pull/2803
|
||||
* @Sushanth012 made their first contribution in https://github.com/soxoj/maigret/pull/2802
|
||||
* @thunderstornX made their first contribution in https://github.com/soxoj/maigret/pull/2774
|
||||
|
||||
**Full Changelog**: https://github.com/soxoj/maigret/compare/v0.6.1...v0.6.2
|
||||
|
||||
## [0.6.1] - 2026-05-15
|
||||
|
||||
## What's Changed
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ project = 'Maigret'
|
||||
copyright = '2025, soxoj'
|
||||
author = 'soxoj'
|
||||
|
||||
release = '0.6.1'
|
||||
release = '0.6.2'
|
||||
version = '0.6'
|
||||
|
||||
# -- Internationalization
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""Maigret version file"""
|
||||
|
||||
__version__ = '0.6.1'
|
||||
__version__ = '0.6.2'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 1,
|
||||
"updated_at": "2026-07-01T12:34:18Z",
|
||||
"updated_at": "2026-07-01T14:59:32Z",
|
||||
"sites_count": 3166,
|
||||
"min_maigret_version": "0.6.1",
|
||||
"min_maigret_version": "0.5.0",
|
||||
"data_sha256": "efc68dd64a37004bab8cc90a3d3bfe845605a8570a72d9ebdcbf1ebe54362111",
|
||||
"data_url": "https://raw.githubusercontent.com/soxoj/maigret/main/maigret/resources/data.json"
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry]
|
||||
name = "maigret"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
description = "🕵️♂️ Collect a dossier on a person by username from thousands of sites."
|
||||
authors = ["Soxoj <soxoj@protonmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ description: |
|
||||
|
||||
Currently supported more than 3000 sites, search is launched against 500 popular sites in descending order of popularity by default. Also supported checking of Tor sites, I2P sites, and domains (via DNS resolving).
|
||||
|
||||
version: 0.6.1
|
||||
version: 0.6.2
|
||||
license: MIT
|
||||
base: core22
|
||||
confinement: strict
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Unit tests for the pure CHANGELOG-manipulation logic in utils/generate_changelog.
|
||||
|
||||
The GitHub-fetching part (fetch_release_notes) is intentionally not tested here —
|
||||
it is a thin `gh api` wrapper. Everything that decides *what the file becomes* is
|
||||
pure and covered below.
|
||||
"""
|
||||
|
||||
from utils.generate_changelog import build_section, upsert_section
|
||||
|
||||
NOTES = (
|
||||
"## What's Changed\n"
|
||||
"* Fix site checks by @soxoj in https://github.com/soxoj/maigret/pull/2826\n"
|
||||
"* Add Neo4j export by @soxoj in https://github.com/soxoj/maigret/pull/2774\n\n"
|
||||
"**Full Changelog**: https://github.com/soxoj/maigret/compare/v0.6.1...v0.6.2"
|
||||
)
|
||||
|
||||
|
||||
def test_build_section_shape():
|
||||
section = build_section("0.6.2", "2026-07-01", NOTES)
|
||||
assert section.startswith("## [0.6.2] - 2026-07-01\n\n## What's Changed\n")
|
||||
assert section.endswith("compare/v0.6.1...v0.6.2\n")
|
||||
|
||||
|
||||
def test_upsert_replaces_empty_placeholder():
|
||||
changelog = (
|
||||
"# Changelog\n\n"
|
||||
"## [0.6.2] - 2026-07-01\n\n"
|
||||
"## What's Changed\n\n"
|
||||
"## [0.6.1] - 2026-05-15\n\n"
|
||||
"## What's Changed\n"
|
||||
"* old release entry\n"
|
||||
)
|
||||
section = build_section("0.6.2", "2026-07-01", NOTES)
|
||||
out = upsert_section(changelog, "0.6.2", section)
|
||||
|
||||
# The placeholder is filled with the real notes...
|
||||
assert "* Fix site checks by @soxoj" in out
|
||||
# ...exactly once (no duplicate heading)...
|
||||
assert out.count("## [0.6.2]") == 1
|
||||
# ...and the previous release entry is untouched.
|
||||
assert "* old release entry" in out
|
||||
assert out.count("## [0.6.1]") == 1
|
||||
# New entry precedes the old one.
|
||||
assert out.index("## [0.6.2]") < out.index("## [0.6.1]")
|
||||
|
||||
|
||||
def test_upsert_inserts_when_absent():
|
||||
changelog = "# Changelog\n\n## [0.6.1] - 2026-05-15\n\n* old\n"
|
||||
section = build_section("0.7.0", "2026-08-01", NOTES)
|
||||
out = upsert_section(changelog, "0.7.0", section)
|
||||
|
||||
assert out.startswith("# Changelog\n")
|
||||
assert out.index("## [0.7.0]") < out.index("## [0.6.1]")
|
||||
assert "* old" in out
|
||||
|
||||
|
||||
def test_upsert_is_idempotent():
|
||||
changelog = "# Changelog\n\n## [0.6.1] - 2026-05-15\n\n* old\n"
|
||||
section = build_section("0.6.2", "2026-07-01", NOTES)
|
||||
once = upsert_section(changelog, "0.6.2", section)
|
||||
twice = upsert_section(once, "0.6.2", section)
|
||||
assert once == twice
|
||||
assert once.count("## [0.6.2]") == 1
|
||||
@@ -9,8 +9,10 @@ import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from utils.generate_db_meta import (
|
||||
DEFAULT_MIN_VERSION,
|
||||
build_meta,
|
||||
meta_payload_equals,
|
||||
resolve_min_version,
|
||||
write_meta_if_changed,
|
||||
)
|
||||
from utils.update_site_data import (
|
||||
@@ -148,6 +150,28 @@ def test_write_meta_writes_when_existing_file_is_corrupt(tmp_path):
|
||||
json.loads(meta_path.read_text()) # now parseable
|
||||
|
||||
|
||||
def test_resolve_min_version_prefers_cli_value(tmp_path):
|
||||
meta_path = tmp_path / "db_meta.json"
|
||||
meta_path.write_text(json.dumps({"min_maigret_version": "0.6.1"}))
|
||||
|
||||
assert resolve_min_version("0.9.0", str(meta_path)) == "0.9.0"
|
||||
|
||||
|
||||
def test_resolve_min_version_preserves_existing_when_no_cli_value(tmp_path):
|
||||
# Routine regeneration (e.g. after a version bump) must keep the existing
|
||||
# floor, not raise it to the current release.
|
||||
meta_path = tmp_path / "db_meta.json"
|
||||
meta_path.write_text(json.dumps({"min_maigret_version": "0.5.0"}))
|
||||
|
||||
assert resolve_min_version(None, str(meta_path)) == "0.5.0"
|
||||
|
||||
|
||||
def test_resolve_min_version_falls_back_to_default_when_no_meta(tmp_path):
|
||||
meta_path = tmp_path / "db_meta.json" # does not exist
|
||||
|
||||
assert resolve_min_version(None, str(meta_path)) == DEFAULT_MIN_VERSION
|
||||
|
||||
|
||||
def test_build_meta_uses_provided_now(tmp_path):
|
||||
data_path = tmp_path / "data.json"
|
||||
_write_data_json(data_path, {"GitHub": {}})
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
"""Fill the CHANGELOG.md "What's Changed" section for a release.
|
||||
|
||||
Reproduces exactly what the GitHub "Generate release notes" button produces
|
||||
(``* <PR title> by @<author> in <url>`` bullets, plus New Contributors and the
|
||||
Full Changelog footer) and drops it under a ``## [<version>] - <date>`` heading
|
||||
at the top of CHANGELOG.md — the same shape every prior release entry uses.
|
||||
|
||||
The notes are fetched via the GitHub REST API through the ``gh`` CLI, so it uses
|
||||
your existing ``gh auth`` credentials (or GITHUB_TOKEN in CI). No PR data is
|
||||
invented locally: the source of truth is GitHub, identical to how the section
|
||||
was produced before.
|
||||
|
||||
Usage:
|
||||
python utils/generate_changelog.py # version from __version__.py, prev tag auto
|
||||
python utils/generate_changelog.py --version 0.6.2 --previous-tag v0.6.1
|
||||
python utils/generate_changelog.py --print # print notes, don't touch CHANGELOG.md
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os.path as path
|
||||
import re
|
||||
import subprocess
|
||||
from datetime import date as date_cls
|
||||
from typing import Optional
|
||||
|
||||
REPO_ROOT = path.dirname(path.dirname(path.abspath(__file__)))
|
||||
VERSION_FILE = path.join(REPO_ROOT, "maigret", "__version__.py")
|
||||
CHANGELOG_PATH = path.join(REPO_ROOT, "CHANGELOG.md")
|
||||
|
||||
|
||||
def get_current_version() -> str:
|
||||
"""Read __version__ from maigret/__version__.py."""
|
||||
with open(VERSION_FILE) as f:
|
||||
for line in f:
|
||||
if line.startswith("__version__"):
|
||||
return line.split("=")[1].strip().strip("'\"")
|
||||
raise RuntimeError(f"__version__ not found in {VERSION_FILE}")
|
||||
|
||||
|
||||
def _run(cmd: list) -> str:
|
||||
return subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
||||
|
||||
|
||||
def get_repo_slug() -> str:
|
||||
"""Return 'owner/repo' parsed from the origin remote."""
|
||||
url = _run(["git", "-C", REPO_ROOT, "remote", "get-url", "origin"]).strip()
|
||||
m = re.search(r"github\.com[:/]([^/]+/[^/]+?)(?:\.git)?$", url)
|
||||
if not m:
|
||||
raise RuntimeError(f"Cannot parse GitHub repo from remote url: {url}")
|
||||
return m.group(1)
|
||||
|
||||
|
||||
def get_latest_tag() -> Optional[str]:
|
||||
"""Latest v* tag by creation date, or None if there are no version tags."""
|
||||
out = _run(["git", "-C", REPO_ROOT, "tag", "--list", "v*", "--sort=-creatordate"])
|
||||
tags = [t for t in out.splitlines() if t.strip()]
|
||||
return tags[0] if tags else None
|
||||
|
||||
|
||||
def fetch_release_notes(repo: str, tag_name: str, previous_tag: Optional[str], commitish: str) -> str:
|
||||
"""Fetch GitHub auto-generated release notes body for the given tag range.
|
||||
|
||||
Works even when ``tag_name`` does not exist yet by pinning ``target_commitish``.
|
||||
"""
|
||||
cmd = [
|
||||
"gh", "api", "--method", "POST",
|
||||
f"repos/{repo}/releases/generate-notes",
|
||||
"-f", f"tag_name={tag_name}",
|
||||
"-f", f"target_commitish={commitish}",
|
||||
]
|
||||
if previous_tag:
|
||||
cmd += ["-f", f"previous_tag_name={previous_tag}"]
|
||||
body = json.loads(_run(cmd))["body"].strip()
|
||||
return body
|
||||
|
||||
|
||||
def build_section(version: str, release_date: str, notes_body: str) -> str:
|
||||
"""Assemble one CHANGELOG entry: '## [version] - date' + the notes body."""
|
||||
return f"## [{version}] - {release_date}\n\n{notes_body.strip()}\n"
|
||||
|
||||
|
||||
# Matches a version heading like "## [0.6.2] - 2026-07-01" up to (not including)
|
||||
# the next version heading or end of file.
|
||||
_SECTION_RE_TMPL = r"^## \[{version}\][^\n]*\n.*?(?=^## \[|\Z)"
|
||||
|
||||
|
||||
def upsert_section(changelog: str, version: str, section: str) -> str:
|
||||
"""Insert ``section`` for ``version`` into ``changelog``.
|
||||
|
||||
If an entry for ``version`` already exists (e.g. an empty placeholder left by
|
||||
the version bump) it is replaced; otherwise the section is inserted directly
|
||||
after the top-level '# Changelog' title. Idempotent.
|
||||
"""
|
||||
section = section.rstrip("\n") + "\n"
|
||||
pattern = re.compile(_SECTION_RE_TMPL.format(version=re.escape(version)), re.DOTALL | re.MULTILINE)
|
||||
if pattern.search(changelog):
|
||||
return pattern.sub(lambda _: section + "\n", changelog, count=1)
|
||||
|
||||
# No existing entry: insert after the "# Changelog" header.
|
||||
title = re.compile(r"^# Changelog[^\n]*\n", re.MULTILINE)
|
||||
m = title.search(changelog)
|
||||
if not m:
|
||||
return section + "\n" + changelog
|
||||
idx = m.end()
|
||||
return changelog[:idx] + "\n" + section + "\n" + changelog[idx:].lstrip("\n")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Fill CHANGELOG.md What's Changed from GitHub release notes")
|
||||
parser.add_argument("--version", default=None, help="Release version (default: read from __version__.py)")
|
||||
parser.add_argument("--previous-tag", default=None, help="Previous git tag (default: latest v* tag)")
|
||||
parser.add_argument("--date", default=None, help="Release date YYYY-MM-DD (default: today)")
|
||||
parser.add_argument("--commitish", default="HEAD", help="Target commit/branch for the release (default: HEAD)")
|
||||
parser.add_argument("--repo", default=None, help="owner/repo (default: parsed from origin remote)")
|
||||
parser.add_argument("--print", dest="print_only", action="store_true", help="Print notes only; do not edit CHANGELOG.md")
|
||||
args = parser.parse_args()
|
||||
|
||||
version = args.version or get_current_version()
|
||||
tag_name = f"v{version}"
|
||||
previous_tag = args.previous_tag or get_latest_tag()
|
||||
release_date = args.date or date_cls.today().isoformat()
|
||||
repo = args.repo or get_repo_slug()
|
||||
|
||||
notes = fetch_release_notes(repo, tag_name, previous_tag, args.commitish)
|
||||
section = build_section(version, release_date, notes)
|
||||
|
||||
if args.print_only:
|
||||
print(section)
|
||||
return
|
||||
|
||||
with open(CHANGELOG_PATH, "r", encoding="utf-8") as f:
|
||||
changelog = f.read()
|
||||
updated = upsert_section(changelog, version, section)
|
||||
with open(CHANGELOG_PATH, "w", encoding="utf-8") as f:
|
||||
f.write(updated)
|
||||
|
||||
n_prs = notes.count("\n* ") + (1 if notes.lstrip().startswith("* ") else 0)
|
||||
print(f"Updated {CHANGELOG_PATH}: {version} ({previous_tag or 'start'}..{tag_name}), ~{n_prs} entries")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+32
-11
@@ -12,18 +12,16 @@ DATA_JSON_PATH = path.join(RESOURCES_DIR, "data.json")
|
||||
META_JSON_PATH = path.join(RESOURCES_DIR, "db_meta.json")
|
||||
DEFAULT_DATA_URL = "https://raw.githubusercontent.com/soxoj/maigret/main/maigret/resources/data.json"
|
||||
|
||||
# Backward-compatibility floor: the first maigret version that shipped the db_meta
|
||||
# auto-update mechanism (data format "version": 1). min_maigret_version must NOT
|
||||
# track the current release — it only rises when a breaking data-format change
|
||||
# genuinely requires a newer client. Used as the fallback when no db_meta.json
|
||||
# exists yet to read the value from.
|
||||
DEFAULT_MIN_VERSION = "0.5.0"
|
||||
|
||||
_TIMESTAMP_KEY = "updated_at"
|
||||
|
||||
|
||||
def get_current_version():
|
||||
version_file = path.join(path.dirname(path.dirname(path.abspath(__file__))), "maigret", "__version__.py")
|
||||
with open(version_file) as f:
|
||||
for line in f:
|
||||
if line.startswith("__version__"):
|
||||
return line.split("=")[1].strip().strip("'\"")
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
def build_meta(data_path: str, min_version: str, data_url: str, now: Optional[datetime] = None) -> dict:
|
||||
"""Build a db_meta dict for the given data.json. Does not touch the filesystem."""
|
||||
with open(data_path, "rb") as f:
|
||||
@@ -80,13 +78,36 @@ def write_meta_if_changed(
|
||||
return new_meta, True
|
||||
|
||||
|
||||
def resolve_min_version(cli_min_version: Optional[str], meta_path: str) -> str:
|
||||
"""Resolve the min_maigret_version to write.
|
||||
|
||||
Priority: explicit CLI value > value already in db_meta.json > DEFAULT_MIN_VERSION.
|
||||
Crucially this does NOT fall back to the current release version, so routine
|
||||
regenerations (e.g. after a version bump) never silently raise the compatibility
|
||||
floor. It only moves when a maintainer passes --min-version for a real
|
||||
data-format change.
|
||||
"""
|
||||
if cli_min_version:
|
||||
return cli_min_version
|
||||
existing = _read_meta(meta_path)
|
||||
if existing and existing.get("min_maigret_version"):
|
||||
return existing["min_maigret_version"]
|
||||
return DEFAULT_MIN_VERSION
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate db_meta.json from data.json")
|
||||
parser.add_argument("--min-version", default=None, help="Minimum compatible maigret version (default: current version)")
|
||||
parser.add_argument(
|
||||
"--min-version",
|
||||
default=None,
|
||||
help="Minimum compatible maigret version. Default: preserve the value already "
|
||||
f"in db_meta.json, or {DEFAULT_MIN_VERSION} if none exists. Do NOT let this track "
|
||||
"the release version — only raise it on a breaking data-format change.",
|
||||
)
|
||||
parser.add_argument("--data-url", default=DEFAULT_DATA_URL, help="URL where data.json can be downloaded")
|
||||
args = parser.parse_args()
|
||||
|
||||
min_version = args.min_version or get_current_version()
|
||||
min_version = resolve_min_version(args.min_version, META_JSON_PATH)
|
||||
meta, written = write_meta_if_changed(DATA_JSON_PATH, META_JSON_PATH, min_version, args.data_url)
|
||||
|
||||
if written:
|
||||
|
||||
Reference in New Issue
Block a user