リンク切れのURLを検知するGithub Actionsを作りました

schedule 2025年1月12日category 所感

はじめに

リポジトリ内のURLを抽出し、指定されたステータスコードのURLを検知するGithub Actionsを作りました!!
リンク切れのURLを検知する使い方を想定しています。

on:
  push:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: test
        uses: PenguinCabinet/action-to-detect-404-URLs@v0.0.2
        with:
          working_directory: "./" # The directory path to check
          filter_mode: "deny" # "allow" mode or "deny" mode
          filter_status_code: "4.." # Detects URLs with status codes in the 400 range. Can be written using regular expressions.
          detect_URLs_that_cannot_connect_to_the_server: true # Whether to detect when the server cannot be connected
          wait_time: 1 # Request sending interval(second)

https://github.com/marketplace/actions/action-to-detect-404-urls

https://github.com/PenguinCabinet/action-to-detect-404-URLs

開発過程で困った点

三項式とlambda式の優先順位

filter_func=lambda a,b:re.match(b,a)==None if filter_mode == "allow" else lambda a,b:re.match(b,a)!=None

上記のコードは、三項式を使い、allowモードの場合合致しないものを検知し、denyモードの場合合致するものを検知することを意図しています。
しかし、これだとうまくいきません。
三項式の優先順位がlambda式より高いため、下記のような挙動になってしまうのです。

filter_func=lambda a,b:(re.match(b,a)==None if filter_mode == "allow" else lambda a,b:re.match(b,a)!=None)

そのため、意図するコードにしたい場合、下記のコードにします。

filter_func=(lambda a,b:re.match(b,a)==None) if filter_mode == "allow" else (lambda a,b:re.match(b,a)!=None)

URLを検知する方法

自前の正規表現で行うこともできるのですが、付け焼き刃で作るよりライブラリ化されたものを探したほうが信頼できるだろうということで、外部ライブラリを使用する方法を採用しました。
linkify-it-pyを使用しています。

最初、下記のようなコードを実装しました。

def search_urls(text: str) -> list[str]:
    linkify = LinkifyIt()
    matches = linkify.match(text)

    if not matches:
        return []

    return [m.url for m in matches]

しかしこれだと、下記のスキーマのないURLやメールアドレスも検知してしまいます。

example.com
mailto:mail@example.com

そのため、LinkifyIt呼び出し時のコードを下記の通りに変更しました。

    linkify = LinkifyIt(
        schemas={
            "ftp:": None,
            "//": None,
            "mailto:": None,
        },
        options={
            "fuzzy_link": False,
            "fuzzy_email": False,
            "fuzzy_ip": False,
        }
    )

上記のテキストが検知されなくなり、意図した挙動になりました。

Powered by Nextjs,Catppuccin Theme

© 2026 PenguinCabinet All Rights Reserved.

※引用した商標・著作物は各権利者に帰属します。
ペンギン内閣名義の発言は、所属組織を代表するものではありません。個人の所感です。