Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/.typos.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# .typos.toml
[files]
extend-exclude = [
"smoketest/**",
"mibs/**"
]
77 changes: 77 additions & 0 deletions .github/workflows/check-typos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: Typos Check Reusable

on:
workflow_call:

jobs:
typos:
runs-on: ubuntu-latest
env:
TYPOS_VERSION: 1.44.0

steps:

- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref || github.ref }}

- name: Checkout .github repository
uses: actions/checkout@v6
with:
repository: vyos/.github
path: .github-repo

# Get changed files (robust)
- name: Get changed files
id: changed
uses: tj-actions/changed-files@v47
with:
separator: "\n"

# Cache cargo registry and built binaries
- name: Cache cargo
uses: actions/cache@v5.0.4
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-typos-${{ env.TYPOS_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-typos-${{ env.TYPOS_VERSION }}-

# Install typos CLI
- name: Install typos CLI
run: |
# Install typos from crates.io using cargo with pinned version
cargo install typos-cli@${{ env.TYPOS_VERSION }}

# Run typos
- name: Run typos
id: typos
env:
FILES: ${{ steps.changed.outputs.all_changed_files }}
run: |
chmod +x .github-repo/scripts/check-typos.sh
.github-repo/scripts/check-typos.sh "$FILES" 2>&1

# Parse and format typos output to comment
- name: Process typos output
id: format_comments
if: always()
env:
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
REPO_URL: https://github.com/${{ github.repository }}
run: |
echo "=== Debug: Raw typos output ==="
cat typos-output.json
echo "=== End raw output ==="
python3 .github-repo/scripts/process-typos.py

# Post comment with inline details (auto-updates on head changes)
- name: Add PR comment with typo details
if: always()
uses: mshick/add-pr-comment@v3
with:
message: ${{ steps.format_comments.outputs.message }}
message-id: "TYPOS_CHECK"
allow-repeats: false
70 changes: 70 additions & 0 deletions scripts/check-typos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

# Script to check for typos in changed files
# Usage: ./check-typos.sh <files-list>

# Initialize variables to prevent context access warnings
echo "status=clean" >> $GITHUB_OUTPUT
echo "message=No typos found." >> $GITHUB_OUTPUT

FILES="${1:?Files list not provided}"

if [ -z "$FILES" ]; then
echo "status=clean" >> $GITHUB_OUTPUT
echo "message=No changed files in PR." >> $GITHUB_OUTPUT
exit 0
fi

# Remove trailing backslashes from YAML line continuations and normalize whitespace
FILES=$(echo "$FILES" | sed 's/\\$//g' | tr '\n' ' ' | xargs)

# Use config from repo, fall back to default from reusable repo
if [ -f ".typos.toml" ]; then
CONFIG="--config .typos.toml"
elif [ -f ".github-repo/.github/.typos.toml" ]; then
CONFIG="--config .github-repo/.github/.typos.toml"
else
CONFIG=""
fi

# Filter out excluded files from the file list
# Extract exclude patterns from .typos.toml and filter files
FILTERED_FILES=""
for file in $FILES; do
# Skip files matching smoketest/** and mibs/**
if [[ ! "$file" =~ ^smoketest/ ]] && [[ ! "$file" =~ ^mibs/ ]]; then
FILTERED_FILES="$FILTERED_FILES $file"
fi
done

# Use filtered list for typos
FILES_TO_CHECK=$FILTERED_FILES

if [ -z "$FILES_TO_CHECK" ]; then
echo "status=clean" >> $GITHUB_OUTPUT
echo "message=No files to check after applying exclusions." >> $GITHUB_OUTPUT
exit 0
fi

# Run typos with JSON output
typos $CONFIG --format json $FILES_TO_CHECK > typos-output.json 2>&1 || EXIT_CODE=$?
EXIT_CODE=${EXIT_CODE:-0}

if [ "$EXIT_CODE" = "0" ]; then
echo "status=clean" >> $GITHUB_OUTPUT
echo "message=No typos found in changed files." >> $GITHUB_OUTPUT
echo "✅ No typos found in changed files."
elif [ "$EXIT_CODE" = "2" ]; then
echo "status=issues" >> $GITHUB_OUTPUT
echo "message=Typos detected in PR." >> $GITHUB_OUTPUT
echo "❌ Typos detected:"
cat typos-output.json
else
echo "status=error" >> $GITHUB_OUTPUT
echo "message=Error running typos check." >> $GITHUB_OUTPUT
echo "ERROR: Typos check failed with exit code $EXIT_CODE" >&2
cat typos-output.json >&2
exit 1
fi


90 changes: 90 additions & 0 deletions scripts/process-typos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""
Generate Copilot-style PR comment from typos JSON output.

Reads typos-output.json (native typos --format json output),
parses it to extract issues, and generates formatted markdown
comment with clickable file:line links.
"""

import json
import os
import sys

def parse_typos_json(json_data):
"""Parse typos JSON output (jsonlines format)."""
issues = []

# Process each line of jsonlines output
for line in json_data.strip().split('\n'):
if not line.strip():
continue
try:
entry = json.loads(line)
# Extract issue information
if 'typo' in entry and 'corrections' in entry:
issue = {
"path": entry.get('path', ''),
"line": entry.get('line_num', 0),
"typo": entry['typo'],
"fix": entry['corrections'][0] if entry['corrections'] else entry['typo']
}
issues.append(issue)
except json.JSONDecodeError:
continue

return issues

def format_comment(issues, commit_sha=None, repo_url=None):
"""Format typos issues as Copilot-style markdown comment."""
if not issues:
return "✅ No typos found in changed files."

message = f"❌ Typos detected in PR ({len(issues)} found)\n\n"
message += "| File | Typo | Suggestion |\n"
message += "|------|------|-------------|\n"

for issue in issues:
# Create clickable link to file and line
if commit_sha and repo_url:
# Use full GitHub blob URL
file_url = f"{repo_url}/blob/{commit_sha}/{issue['path']}#L{issue['line']}"
file_link = f"[{issue['path']}:{issue['line']}]({file_url})"
else:
# Fallback to relative link
file_link = f"[{issue['path']}:{issue['line']}]({issue['path']}#L{issue['line']})"

message += f"| {file_link} | `{issue['typo']}` | `{issue['fix']}` |\n"

return message

def main():
"""Main entry point."""
# Get environment variables
commit_sha = os.getenv('COMMIT_SHA')
repo_url = os.getenv('REPO_URL')

# Read JSON output
if not os.path.exists('typos-output.json'):
comment = "✅ No typos found in changed files."
else:
with open('typos-output.json', 'r') as f:
json_data = f.read()

if json_data.strip():
# Parse issues
issues = parse_typos_json(json_data)

# Generate comment with proper links
comment = format_comment(issues, commit_sha, repo_url)
else:
comment = "✅ No typos found in changed files."

# Output to GitHub environment
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"message<<EOF\n{comment}\nEOF\n")

return 0

if __name__ == '__main__':
sys.exit(main())
Loading