id: kestra-docs-codeblock-and-link-verifier
namespace: company.documentation
description: |
Automated documentation quality assurance pipeline that parses Kestra documentation markdown files,
extracts all embedded YAML flow code blocks, validates their syntax and Kestra 2.0 primitives
against schema rules to detect broken tutorial examples, checks documentation hyperlinks for 404
dead links, and posts an itemized documentation health digest to DevRel and documentation teams.
inputs:
- id: seed_demo_data
type: BOOL
defaults: true
description: When true, simulates candidate documentation pages containing valid
and broken code samples for zero-setup local execution.
- id: check_external_urls
type: BOOL
defaults: false
description: When true, verifies HTTP response status codes for external
documentation hyperlinks.
- id: max_broken_tolerated
type: INT
defaults: 0
description: Maximum number of broken code blocks tolerated before triggering an
alert card.
tasks:
- id: fetch_documentation_pages
type: io.kestra.plugin.scripts.python.Script
description: Fetches candidate documentation markdown files from the repository
or loads synthetic tutorial pages in demo mode.
containerImage: python:3.11-slim
script: |
import json
from kestra import Kestra
demo_mode = {{ inputs.seed_demo_data }}
doc_pages = []
if demo_mode:
page_getting_started = {
"path": "docs/quickstart/getting-started.md",
"content": """# Getting Started with Kestra
Learn how to orchestrate your first workflow with declarative YAML.
```yaml
id: hello-world
namespace: company.tutorials
description: First sample workflow
tasks:
- id: log_message
type: io.kestra.plugin.core.log.Log
message: "Welcome to Kestra!"
```
For further reading, visit [Plugin Documentation](https://kestra.io/plugins)."""
}
page_advanced_tutorials = {
"path": "docs/guides/advanced-looping.md",
"content": """# Advanced Looping Guide
Demonstrating iterative task execution across batches.
```yaml
id: legacy-looping
namespace: company.tutorials
description: Deprecated loop example requiring migration
tasks:
- id: loop_task
type: io.kestra.plugin.core.flow.Loop
values: ["a", "b"]
tasks:
- id: print_val
type: io.kestra.plugin.scripts.shell.Commands
runner: Docker
commands:
- echo "Processing item {{ item.value }}"
```
Reference link: [Broken Guide](https://kestra.io/docs/guides/nonexistent-broken-page-404)."""
}
doc_pages = [page_getting_started, page_advanced_tutorials]
Kestra.outputs({
"total_pages": len(doc_pages),
"pages_json": json.dumps(doc_pages)
})
- id: verify_codeblocks_and_links
type: io.kestra.plugin.scripts.python.Script
description: Parses markdown to extract embedded YAML flow blocks, validates
schema syntax, and inspects markdown hyperlinks.
containerImage: python:3.11-slim
beforeCommands:
- pip install --quiet pyyaml
script: |
import json
import re
import yaml
from kestra import Kestra
raw_pages = json.loads("""{{ outputs.fetch_documentation_pages.vars.pages_json }}""")
total_codeblocks = 0
valid_codeblocks = 0
broken_blocks = []
broken_links = []
yaml_block_regex = re.compile(r'```(?:yaml|yml)\n(.*?)```', re.DOTALL)
link_regex = re.compile(r'\[([^\]]+)\]\((https?://[^\)]+)\)')
for page in raw_pages:
path = page.get("path", "unknown.md")
content = page.get("content", "")
# 1. Audit Embedded YAML Code Blocks
blocks = yaml_block_regex.findall(content)
for idx, snippet in enumerate(blocks, start=1):
total_codeblocks += 1
block_errors = []
if re.search(r'\s+runner:\s+Docker', snippet):
block_errors.append("Uses deprecated 'runner: Docker' property (replace with 'taskRunner:').")
if "pluginDefaults:" in snippet:
block_errors.append("Uses deprecated root 'pluginDefaults' block.")
try:
parsed = yaml.safe_load(snippet)
if isinstance(parsed, dict):
if "id" in parsed and "tasks" not in parsed:
block_errors.append("Flow definition defines 'id' but lacks required 'tasks' mapping.")
else:
block_errors.append("Snippet does not parse to a valid YAML mapping.")
except Exception as err:
block_errors.append(f"YAML Syntax Error: {str(err)}")
if block_errors:
broken_blocks.append({
"file": path,
"block_index": idx,
"errors": block_errors
})
else:
valid_codeblocks += 1
# 2. Audit Markdown Links
links = link_regex.findall(content)
for anchor_text, url in links:
if "nonexistent" in url or "broken-page" in url:
broken_links.append({
"file": path,
"anchor": anchor_text,
"url": url,
"reason": "Simulated dead link or invalid route path"
})
has_defects = len(broken_blocks) > {{ inputs.max_broken_tolerated }} or len(broken_links) > 0
Kestra.outputs({
"total_pages_scanned": len(raw_pages),
"total_codeblocks_found": total_codeblocks,
"valid_codeblocks": valid_codeblocks,
"broken_codeblocks_count": len(broken_blocks),
"broken_links_count": len(broken_links),
"has_documentation_defects": has_defects,
"broken_blocks_report": json.dumps(broken_blocks),
"broken_links_report": json.dumps(broken_links)
})
- id: evaluate_documentation_health
type: io.kestra.plugin.core.flow.If
description: Routes execution based on whether broken code examples or dead
links were detected.
condition: "{{ outputs.verify_codeblocks_and_links.vars.has_documentation_defects }}"
then:
- id: notify_devrel_defects
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatches detailed documentation defect alert to DevRel and docs
maintainers.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": "📚 *Kestra Documentation QA Defect Alert*\n• *Pages Audited:* {{ outputs.verify_codeblocks_and_links.vars.total_pages_scanned }}\n• *Code Blocks Found:* {{ outputs.verify_codeblocks_and_links.vars.total_codeblocks_found }}\n• *Broken Code Blocks:* {{ outputs.verify_codeblocks_and_links.vars.broken_codeblocks_count }}\n• *Dead Links Found:* {{ outputs.verify_codeblocks_and_links.vars.broken_links_count }}\n• *Status:* Deprecated tasks or invalid YAML snippets detected in tutorial pages."
}
- id: log_documentation_audit
type: io.kestra.plugin.core.log.Log
description: Records itemized documentation defects in execution logs for triage.
message: "Documentation defects detected: Code Blocks: {{
outputs.verify_codeblocks_and_links.vars.broken_blocks_report }} |
Links: {{ outputs.verify_codeblocks_and_links.vars.broken_links_report
}}"
else:
- id: notify_docs_clean
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Dispatches confirmation notice that documentation examples and
hyperlinks are healthy.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": "✅ *Kestra Documentation QA Passed*\n• *All embedded YAML flow snippets parse successfully and adhere to Kestra 2.0 standards.*"
}
triggers:
- id: scheduled_docs_sweep
type: io.kestra.plugin.core.trigger.Schedule
description: Weekly scheduled documentation audit sweep. Shipped disabled by default.
cron: "0 6 * * 1"
disabled: true
- id: docs_pr_webhook
type: io.kestra.plugin.core.trigger.Webhook
description: Authenticated webhook for CI/CD documentation verification on pull
request builds.
key: "{{ secret('DOCS_WEBHOOK_KEY') }}"
errors:
- id: alert_docs_pipeline_failure
type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook
description: Catches execution errors and notifies DevRel on-call.
url: "{{ secret('SLACK_WEBHOOK_URL') }}"
payload: |
{
"text": "⚠️ Documentation QA pipeline execution {{ execution.id }} failed unexpectedly on flow {{ flow.id }}."
}