GitHub Action

hahwul/deadfinder is a composite action that downloads the matching release binary, verifies its sha256, and executes the scan. Runs on Linux (x86_64/aarch64) and macOS (arm64). Intel macOS runners (macos-13) are not supported — use macos-latest.

Pin a version

Always pin a released ref. @latest is not a valid Actions ref (GitHub has no auto-resolver for it).

- uses: hahwul/deadfinder@v2       # tracks latest 2.x — gets bug-fix patches automatically
# or
- uses: hahwul/deadfinder@2.0.2    # exact pin — fully reproducible

The version input can override the binary independently of the action ref:

- uses: hahwul/deadfinder@v2
  with:
    version: "2.0.2"   # download binary from this release tag

Full example

steps:
  - name: Run DeadFinder
    uses: hahwul/deadfinder@v2
    id: scan
    with:
      command: sitemap
      target: https://www.example.com/sitemap.xml
      # Optional:
      # timeout: 10
      # concurrency: 50
      # include30x: false
      # headers: "X-API-Key: secret"
      # worker_headers: "User-Agent: Deadfinder Bot"
      # user_agent: "MyBot/1.0"
      # proxy: "http://localhost:8080"
      # proxy_auth: "user:pass"
      # insecure: false
      # match: "^https://example\\.com/"
      # ignore: "\\.png$"
      # limit: 0
      # output_format: json
      # coverage: true
      # visualize: report.png
      # silent: false
      # verbose: false

  - name: Handle results
    run: echo '${{ steps.scan.outputs.output }}' | jq '.'

Inputs

Input Required Default Notes
command url / file / sitemap (pipe is not usable here — the action always passes target as an argument)
target URL, file path, or sitemap URL
version latest Release tag; latest resolves to most recent release
timeout 10 seconds
concurrency 50 workers
silent false string "true" to enable
verbose false
include30x false
headers "" comma-separated "Key: Value" pairs
worker_headers "" headers for link-check requests
user_agent "" overrides default UA
proxy "" HTTP/HTTPS proxy URL
proxy_auth "" user:pass
insecure false skip TLS certificate verification
match "" regex
ignore "" regex
limit "" cap the input URL count; empty or 0 means no limit
output_format json json / yaml / toml / csv / sarif
coverage false
visualize "" file path (implies coverage)

Outputs

Output Shape
output The scan result, serialized with output_format. json (the default) is compacted onto one line; every other format is passed through verbatim, newlines included.

The JSON maps each scanned target to its list of dead links (with coverage: true, the map moves under a dead_links key and a coverage section is added). For example, fail the job when any dead link was found:

- name: Fail on dead links
  run: echo '${{ steps.scan.outputs.output }}' | jq -e 'all(.[]; length == 0)'

Migrating from v1

The v1 action was Docker-based and bundled the Ruby gem. v2 is a composite action that downloads the Crystal binary directly. All v1 inputs are preserved. worker_headers was previously undeclared but wired through args — it's now a formal input. version, insecure, limit and output_format are new. No inputs were renamed or removed.

Pin to @1.10.0 to keep the v1 behavior; use @v2 (or pin a specific 2.x tag like @2.0.2) for v2.