diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml new file mode 100644 index 0000000..5e3255b --- /dev/null +++ b/.github/workflows/links.yml @@ -0,0 +1,32 @@ +name: Links + +on: + push: + pull_request: + schedule: + - cron: "0 13 * * 1" # weekly, to catch external link rot without a commit + workflow_dispatch: + +permissions: + contents: read + +jobs: + linkChecker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup mise + uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1 + with: + install: false + + # Install only lychee (not the repo's full toolchain) and run the check. + - name: Check links + env: + MISE_AUTO_INSTALL: "false" + run: | + mise install lychee + mise run check-links diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4af9f1a..3d2b9f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,10 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # 8.1.0 + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + # Disable caching in the release workflow (zizmor cache-poisoning). + enable-cache: false - name: Build run: uv build @@ -47,4 +50,4 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # 1.14.0 + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce4cbab..1ea93f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: submodules: true persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # 8.1.0 + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - name: Install tox run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv --with tox-gh - name: Install Python diff --git a/.gitignore b/.gitignore index 78e305c..553cd4f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ MANIFEST *.swp .tox /venv +.lycheecache diff --git a/CLAUDE.md b/CLAUDE.md index 27e8c58..0c7dd2c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -404,7 +404,7 @@ uv run tox ## Additional Resources -- [API Documentation](https://geoip2.readthedocs.org/) -- [GeoIP Web Services Docs](https://dev.maxmind.com/geoip/docs/web-services) +- [API Documentation](https://geoip2.readthedocs.io/en/latest/) +- [GeoIP Web Services Docs](https://dev.maxmind.com/geoip/docs/web-services/) - [MaxMind DB Format](https://maxmind.github.io/MaxMind-DB/) - GitHub Issues: https://github.com/maxmind/GeoIP2-python/issues diff --git a/README.rst b/README.rst index c6b539d..a20e771 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,8 @@ Description ----------- This package provides an API for the GeoIP and GeoLite `web services -`_ and `databases -`_. +`_ and `databases +`_. Installation ------------ @@ -164,7 +164,7 @@ Web Service Client Exceptions ----------------------------- For details on the possible errors returned by the web service itself, see -https://dev.maxmind.com/geoip/docs/web-services?lang=en for the GeoIP web +https://dev.maxmind.com/geoip/docs/web-services/?lang=en for the GeoIP web service docs. If the web service returns an explicit error document, this is thrown as a @@ -515,7 +515,8 @@ Reporting Data Problems ----------------------- If the problem you find is that an IP address is incorrectly mapped, please -`submit your correction to MaxMind `_. +`submit your correction to MaxMind +`_. If you find some other sort of mistake, like an incorrect spelling, please check the `GeoNames site `_ first. Once you've @@ -526,7 +527,7 @@ will be automatically incorporated into future MaxMind releases. If you are a paying MaxMind customer and you're not sure where to submit a correction, please `contact MaxMind support -`_ for help. +`_ for help. Versioning ---------- @@ -541,4 +542,4 @@ Please report all issues with this code using the `GitHub issue tracker If you are having an issue with a MaxMind service that is not specific to the client API, please contact `MaxMind support -`_ for assistance. +`_ for assistance. diff --git a/lychee.toml b/lychee.toml new file mode 100644 index 0000000..df89290 --- /dev/null +++ b/lychee.toml @@ -0,0 +1,64 @@ +# Lychee link checker configuration +# https://lychee.cli.rs/#/usage/config +# +# Run locally with: +# lychee './**/*.md' './**/*.rst' './src/**/*.py' './pyproject.toml' + +# Include URL fragments in checks +include_fragments = true + +# Don't allow any redirects, so links that have moved are surfaced and can be +# updated to their canonical destination. +max_redirects = 0 + +# Accept these HTTP status codes +# 100-103: Informational responses +# 200-299: Success responses +# 403: Forbidden (some sites use this for rate limiting) +# 429: Too Many Requests +# 500-599: Server errors (temporary issues shouldn't fail CI) +# 999: LinkedIn's custom status code +accept = ["100..=103", "200..=299", "403", "429", "500..=599", "999"] + +# Exclude URL patterns from checking (treated as regular expressions) +exclude = [ + # GitHub blob URLs with line-number fragments (not parseable as page anchors) + '^https://github\.com/[^/]+/[^/]+/blob/[0-9a-fA-F]+/.+#L\d+$', + # Live / auth-gated MaxMind endpoints: appear as code string literals or + # require login, so they can't be verified by an anonymous request. + '^https://geoip\.maxmind\.com', + '^https://geolite\.info', + '^https://minfraud\.maxmind\.com', + '^https://sandbox\.maxmind\.com', + '^https://updates\.maxmind\.com', + '^https://www\.maxmind\.com/en/accounts/', + '^https://www\.maxmind\.com/en/account/login', + # Local / placeholder URLs (e.g. the proxy example in docstrings) + '^file://', + '^https?://example\.(com|org|net)', + '^http://localhost', + '127\.0\.0\.1', +] + +# Exclude file paths from getting checked (regular expressions, matched against +# the path relative to the working directory). Patterns are segment-anchored +# with (^|/) so short names like "build" don't match unintended paths. +exclude_path = [ + '(^|/)node_modules/', + '(^|/)\.venv/', + '(^|/)venv/', + '(^|/)build/', + '(^|/)dist/', + '(^|/)\.eggs/', + '(^|/)[^/]*\.egg-info/', + '(^|/)docs/_build/', + # Changelog: historical entries are preserved as-is, not rewritten + '(^|/)HISTORY\.rst$', +] + +# Cache results for 1 day to speed up repeated checks +cache = true +max_cache_age = "1d" + +# Skip missing input files instead of erroring +skip_missing = true diff --git a/mise.lock b/mise.lock index da1365a..6cd3567 100644 --- a/mise.lock +++ b/mise.lock @@ -1,5 +1,33 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html +[[tools.lychee]] +version = "0.23.0" +backend = "aqua:lycheeverse/lychee" + +[tools.lychee."platforms.linux-arm64"] +checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" + +[tools.lychee."platforms.linux-arm64-musl"] +checksum = "sha256:97eb93b02a7d78a752fc33e5b0983439ccaadbf3db952b68a0a4401acd92e6e0" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-aarch64-unknown-linux-gnu.tar.gz" + +[tools.lychee."platforms.linux-x64"] +checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" + +[tools.lychee."platforms.linux-x64-musl"] +checksum = "sha256:5538440d2c69a45a0a09983271e5dee0c2fe7137d8035d25b2632e10a66a090a" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-unknown-linux-musl.tar.gz" + +[tools.lychee."platforms.macos-arm64"] +checksum = "sha256:4c8034900e11083b68ac6f6582c377ff1f704e268991999e09d717973e493e7f" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-arm64-macos.dmg" + +[tools.lychee."platforms.windows-x64"] +checksum = "sha256:0fda7ff0a60c0250939fc25361c2d4e6e7853c31c996733fdd5a1dd760bcb824" +url = "https://github.com/lycheeverse/lychee/releases/download/lychee-v0.23.0/lychee-x86_64-windows.exe" + [[tools.python]] version = "3.13.11" backend = "core:python" diff --git a/mise.toml b/mise.toml index 729bb1d..6861cee 100644 --- a/mise.toml +++ b/mise.toml @@ -9,6 +9,7 @@ disable_backends = [ [tools] python = "latest" uv = "latest" +lychee = "latest" [hooks] enter = "mise install --quiet --locked" @@ -16,3 +17,7 @@ enter = "mise install --quiet --locked" [[watch_files]] patterns = ["mise.toml", "mise.lock"] run = "mise install --quiet --locked" + +[tasks.check-links] +description = "Check links with lychee" +run = "lychee --no-progress './**/*.md' './**/*.rst' './src/**/*.py' './pyproject.toml'" diff --git a/pyproject.toml b/pyproject.toml index a268c23..0c79607 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,8 +57,8 @@ source-include = [ ] [project.urls] -Homepage = "https://www.maxmind.com/" -Documentation = "https://geoip2.readthedocs.org/" +Homepage = "https://www.maxmind.com/en/home" +Documentation = "https://geoip2.readthedocs.io/en/latest/" "Source Code" = "https://github.com/maxmind/GeoIP2-python" "Issue Tracker" = "https://github.com/maxmind/GeoIP2-python/issues" diff --git a/src/geoip2/models.py b/src/geoip2/models.py index 9822f53..0a72f6c 100644 --- a/src/geoip2/models.py +++ b/src/geoip2/models.py @@ -2,7 +2,7 @@ The only difference between the City and Insights model classes is which fields in each record may be populated. See -https://dev.maxmind.com/geoip/docs/web-services?lang=en for more details. +https://dev.maxmind.com/geoip/docs/web-services/?lang=en for more details. """ from __future__ import annotations diff --git a/src/geoip2/records.py b/src/geoip2/records.py index 1daffda..a517500 100644 --- a/src/geoip2/records.py +++ b/src/geoip2/records.py @@ -547,7 +547,7 @@ class Traits(Record): .. deprecated:: 2.2.0 Use our `GeoIP Anonymous IP database - `_ + `_ instead. """ is_anonymous_vpn: bool diff --git a/src/geoip2/webservice.py b/src/geoip2/webservice.py index 924854d..ec51ded 100644 --- a/src/geoip2/webservice.py +++ b/src/geoip2/webservice.py @@ -237,8 +237,8 @@ class AsyncClient(BaseClient): :param account_id: Your MaxMind account ID. :param license_key: Your MaxMind license key. - Go to https://www.maxmind.com/en/my_license_key to see your MaxMind - account ID and license key. + Go to https://www.maxmind.com/en/accounts/current/license-key to see + your MaxMind account ID and license key. The following keyword arguments are also accepted: @@ -412,8 +412,8 @@ class Client(BaseClient): :param account_id: Your MaxMind account ID. :param license_key: Your MaxMind license key. - Go to https://www.maxmind.com/en/my_license_key to see your MaxMind - account ID and license key. + Go to https://www.maxmind.com/en/accounts/current/license-key to see + your MaxMind account ID and license key. The following keyword arguments are also accepted: