Skip to content

ci: add reusable markdown link check workflow and default config#49

Open
agent-of-mkmeral wants to merge 3 commits intostrands-agents:mainfrom
agent-of-mkmeral:ci/centralize-markdown-link-check
Open

ci: add reusable markdown link check workflow and default config#49
agent-of-mkmeral wants to merge 3 commits intostrands-agents:mainfrom
agent-of-mkmeral:ci/centralize-markdown-link-check

Conversation

@agent-of-mkmeral
Copy link
Copy Markdown

Description

Centralizes the markdown link check workflow as a reusable workflow in devtools, so all Strands Agents repositories can call it instead of duplicating the workflow definition.

What's included

File Purpose
.github/workflows/check-markdown-links.yml Reusable workflow with workflow_call trigger
markdown-link-check/default-config.json Default config used when repos don't have their own
markdown-link-check/README.md Usage documentation

How repos use it

Each repo just needs a thin caller workflow:

name: Check Markdown Links

on:
  schedule:
    - cron: '0 9 * * 1'
  workflow_dispatch:

jobs:
  check-links:
    uses: strands-agents/devtools/.github/workflows/check-markdown-links.yml@main

Config override

Repos can optionally add their own .markdown-link-check.json for custom settings. If none exists, the default config from this repo is used.

Related

Type of Change

CI


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

Add centralized markdown link check workflow that can be called from
any Strands Agents repository. Includes:

- Reusable workflow (.github/workflows/check-markdown-links.yml)
  with workflow_call trigger and configurable config-file input
- Default config (markdown-link-check/default-config.json) used
  when repos don't have their own .markdown-link-check.json
- Documentation (markdown-link-check/README.md)

Related: strands-agents/sdk-python#1905
agent-of-mkmeral added a commit to agent-of-mkmeral/sdk-typescript that referenced this pull request Apr 8, 2026
Replace inline workflow with thin caller that references the reusable
workflow in strands-agents/devtools. Config is now centralized there too.

See: strands-agents/devtools#49
agent-of-mkmeral added a commit to agent-of-mkmeral/tools that referenced this pull request Apr 8, 2026
Replace inline workflow with thin caller that references the reusable
workflow in strands-agents/devtools. Config is now centralized there too.

See: strands-agents/devtools#49
agent-of-mkmeral added a commit to agent-of-mkmeral/evals that referenced this pull request Apr 8, 2026
Replace inline workflow with thin caller that references the reusable
workflow in strands-agents/devtools. Config is now centralized there too.

See: strands-agents/devtools#49
agent-of-mkmeral added a commit to agent-of-mkmeral/sdk-python that referenced this pull request Apr 8, 2026
Replace inline workflow with thin caller that references the reusable
workflow in strands-agents/devtools. Config is now centralized there too.

See: strands-agents/devtools#49
@mkmeral
Copy link
Copy Markdown
Contributor

mkmeral commented Apr 9, 2026

@agent-of-mkmeral does this setup work? can you set this in your forks first (remote repo setup with config) test it and then let me know?

Copy link
Copy Markdown
Member

@Unshure Unshure left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought. Instead of having each of our repos call to the workflow in this one, can this workflow run from this repo, and then read the markdown files of all the other repos at once?

@mkmeral
Copy link
Copy Markdown
Contributor

mkmeral commented Apr 10, 2026

That's a good question. We can, but the problem i see there is cross-repo issue creation. it'll be hard to do with github actions. we can enable issues in devtools, but it would be for a really specific use case

That said, we might want to have an alternative central place for our automated systems to drop information, notification, etc.

I also want to increase these automations to include stale PR detection (when issue is resolved, but there's still open PR referring to it), and similar maintenance scripts (on top of agentic scans, reviews, etc)

Let me know if you have an idea for a better place, but I suspect we'll want same repo actions for safety

The previous approach downloaded the default config via curl from
strands-agents/devtools/main, but this creates a chicken-and-egg
problem since the file doesn't exist on main until this PR is merged.

Inline the default config directly in the workflow to eliminate the
external dependency and make the fallback self-contained.
@agent-of-mkmeral
Copy link
Copy Markdown
Author

✅ Tested in fork — works with a fix

@mkmeral I tested the full setup in my forks. Here's what I found:

Test Setup

  • Reusable workflow: Merged to agent-of-mkmeral/devtools main branch
  • Caller repo: agent-of-mkmeral/tools with a thin caller workflow
  • Caller workflow:
name: Check Markdown Links
on:
  schedule:
    - cron: '0 9 * * 1'
  workflow_dispatch:
jobs:
  check-links:
    uses: agent-of-mkmeral/devtools/.github/workflows/check-markdown-links.yml@main

Results

Run Config Result Details
1 No local config (fallback) Failed curl: (22) The requested URL returned error: 404 — fallback URL strands-agents/devtools/main/markdown-link-check/default-config.json doesn't exist yet
2 Local .markdown-link-check.json Passed All steps green, link checker ran successfully

Bug Found & Fixed

The curl fallback in the workflow downloads from https://raw.githubusercontent.com/strands-agents/devtools/main/markdown-link-check/default-config.json, but this file doesn't exist on strands-agents/devtools main until this PR is merged — classic chicken-and-egg.

Fix (pushed to this branch): Replaced the curl download with an inline JSON echo. The default config is now self-contained in the workflow, no external dependency:

echo '{"retryOn429":true,"retryCount":3,"fallbackRetryDelay":"30s","aliveStatusCodes":[200,206]}' \\
  > ".markdown-link-check-default.json"

This is actually cleaner — the markdown-link-check/default-config.json file in this repo now serves as documentation/reference rather than a runtime dependency.


Re: @Unshure's suggestion

Instead of having each of our repos call to the workflow in this one, can this workflow run from this repo, and then read the markdown files of all the other repos at once?

That's an interesting idea. Tradeoffs:

Approach Pros Cons
Current (reusable workflow) Each repo owns its check, gets its own issue, simple config override Thin caller workflow needed per repo
Central from devtools Single workflow, no per-repo setup Needs explicit repo list maintenance, can't easily create issues in target repos (token scoping), harder to customize per-repo

I'd lean toward keeping the reusable workflow approach since:

  1. Issues get created in the right repo where the broken links live
  2. Each repo can customize with their own .markdown-link-check.json
  3. No need to maintain a repo list in devtools
  4. The caller workflow is only 10 lines

But @mkmeral should weigh in on the preference.


Test runs: Run 1 (failed) | Run 2 (passed)

Fork artifacts cleaned up ✅ (reverted test commits from both forks)

@agent-of-mkmeral
Copy link
Copy Markdown
Author

Re: Central notification/automation hub

@mkmeral Good points on the broader automation strategy. Here are my thoughts:

Central notification place

I agree same-repo actions is the safest default. For cross-repo notifications, here are options ranked by practicality:

Option Setup Cross-repo? Token scoping
1. Same-repo issues (current) Zero ✅ Native GITHUB_TOKEN (safe)
2. devtools issues as central log Enable issues in devtools Needs PAT or GitHub App PAT_TOKEN (wider scope)
3. GitHub Discussions in devtools Enable discussions Same as above PAT_TOKEN
4. Dedicated strands-agents/.github repo Create repo Same as above PAT_TOKEN

My recommendation: Option 1 (same-repo) for actionable items (broken links, failing checks), and Option 2 or 3 only for digest/summary notifications that are informational. This keeps the blast radius small — a misbehaving workflow can only create issues in its own repo.

Stale PR detection

For the stale PR automation (issue resolved but PR still open), this could live in devtools as a scheduled workflow that:

  1. Queries all strands-agents/* repos for open PRs
  2. Checks if linked issues (via Fixes #N / closingIssuesReferences) are already closed
  3. Posts a comment on the stale PR or creates a summary issue in devtools

This is a read-heavy operation (queries across repos) with minimal writes (comments), so it's a good fit for a central devtools workflow with a PAT. I can prototype this if you want.

Broader maintenance scripts

Similar pattern — devtools as the orchestration hub for read/scan operations, with writes going back to the source repo:

  • Stale PR detection → comment on stale PR in source repo
  • Link checking → issue in source repo (current approach) ✅
  • Dependency audit → issue in source repo
  • Agentic scans/reviews → already handled by my scheduled jobs

TL;DR: Keep actionable items in same-repo (safe), use devtools as orchestration hub for cross-repo scans, and I can prototype the stale PR detection workflow next.

echo "Using default config (inline)"
fi

- uses: gaurav-nelson/github-action-markdown-link-check@3c3b66f1f7d0900e37b71eca45b63ea9eedfce31 # v1.0.17
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont love that this is deprecated. Can we copy the logic and just maintain it in this repo as a python script or something?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! You're right — gaurav-nelson/github-action-markdown-link-check is deprecated (description literally says ⛔️ DEPRECATED). It points to tcort/github-action-markdown-link-check as the maintained successor.

I just pushed a fix (aec1c0c) that swaps to the tcort fork:

- gaurav-nelson/github-action-markdown-link-check@3c3b66f1...  # v1.0.17
+ tcort/github-action-markdown-link-check@e7c7a183...          # v1.1.2

The tcort fork is a drop-in replacement — same inputs (use-quiet-mode, use-verbose-mode, config-file), same behavior. It's pinned to a commit SHA for supply-chain safety.

Re: maintaining our own Python script — I considered that, but the tcort fork is actively maintained (last release Nov 2025, only 3 open issues) and does the job well. Rolling our own link checker means maintaining HTTP retries, redirect handling, rate limiting, concurrency, etc. If the tcort fork ever goes stale too, then building a lightweight Python script would make sense as a next step.

Replace deprecated gaurav-nelson/github-action-markdown-link-check (v1.0.17)
with the maintained fork tcort/github-action-markdown-link-check (v1.1.2).

The gaurav-nelson action is marked deprecated and points to tcort as the
successor. The tcort fork is a drop-in replacement with the same interface.

Pinned to commit SHA e7c7a18 for supply-chain safety.

Addresses review feedback from @Unshure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants