{"id":22016348,"url":"https://github.com/simeg/urlsup-action","last_synced_at":"2026-05-04T06:37:32.863Z","repository":{"id":65161782,"uuid":"325613384","full_name":"simeg/urlsup-action","owner":"simeg","description":"Github Action to verify that links in your repo are up and available","archived":false,"fork":false,"pushed_at":"2020-12-31T07:17:05.000Z","size":6,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-01T18:18:56.040Z","etag":null,"topics":["awesome-bot","ci","http","links","urls","validation"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simeg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-30T17:49:05.000Z","updated_at":"2022-05-07T05:11:34.000Z","dependencies_parsed_at":"2023-01-04T12:39:00.837Z","dependency_job_id":null,"html_url":"https://github.com/simeg/urlsup-action","commit_stats":{"total_commits":7,"total_committers":2,"mean_commits":3.5,"dds":0.4285714285714286,"last_synced_commit":"a88379b7e46e347d663a669bf31069aab6c08ef1"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simeg%2Furlsup-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simeg%2Furlsup-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simeg%2Furlsup-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simeg%2Furlsup-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simeg","download_url":"https://codeload.github.com/simeg/urlsup-action/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245078201,"owners_count":20557283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["awesome-bot","ci","http","links","urls","validation"],"created_at":"2024-11-30T04:34:34.594Z","updated_at":"2026-05-04T06:37:32.855Z","avatar_url":"https://github.com/simeg.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# urlsup-action\n\n**Fast, concurrent URL validation for your repositories** 🚀\n\nGitHub Action that validates URL availability in your files using the powerful [urlsup](https://github.com/simeg/urlsup) Rust binary. Perfect for catching broken links in documentation, ensuring all URLs return successful HTTP status codes.\n\n**There's plenty of examples in the [examples/](examples/) directory, including real-world configurations and pre-built templates that you can use to quickly set up URL validation in your workflows.**\n\n\u003cimg src=\"banner.png\" alt=\"Dotfiles Banner\" width=\"100%\" style=\"display: block; margin: 0 auto;\"\u003e\n\n## 📑 Table of Contents\n\n- [✨ Features](#-features)\n- [🚀 Quick Start](#-quick-start)\n- [📋 Inputs](#-inputs)\n- [📤 Outputs](#-outputs)\n- [📖 Usage Examples](#-usage-examples)\n- [⚠️ Common Use Cases](#-common-use-cases)\n- [❓ FAQ](#-faq)\n- [📚 Documentation \u0026 Examples](#-documentation--examples)\n- [🔧 Migration from v1](#-migration-from-v1)\n- [🎯 GitHub Integration](#-github-integration)\n- [🔧 Internal Scripts](#-internal-scripts)\n- [🧪 Testing \u0026 Development](#-testing--development)\n- [🔗 Related](#-related)\n- [📄 License](#-license)\n\n---\n\n## ✨ Features\n\n- **⚡ Lightning Fast**: Composite action with binary caching (5-10x faster than Docker)\n- **🔄 Concurrent**: Multi-threaded URL checking with configurable concurrency\n- **🎯 Smart Filtering**: Allowlists, status code filtering, and regex exclusions\n- **📊 Rich Reports**: GitHub annotations, job summaries, and detailed JSON reports\n- **🔧 Highly Configurable**: 20+ inputs mapping to all urlsup features\n\n## 🚀 Quick Start\n\n```yaml\nname: Validate URLs are up\n\non:\n  push:\n  pull_request:\n  schedule:\n    - cron: '0 9 * * 1'  # Weekly on Monday\n\njobs:\n  url-validate:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Validate URLs\n        id: validate-urls\n        uses: simeg/urlsup-action@v2\n        with:\n          files: '.'\n          recursive: true\n          timeout-seconds: 5\n          retry: 2\n```\n\n\u003e **💡 Performance Note**: The action automatically caches the urlsup binary for lightning-fast subsequent runs (5-10x speedup after first execution).\n\n## 📋 Inputs\n\n### File Selection\n| Input                | Description                                     | Default             |\n|----------------------|-------------------------------------------------|---------------------|\n| `files`              | Files or directories to check (space-separated) | `'.'`               |\n| `recursive`          | Recursively process directories                 | `true`              |\n| `include-extensions` | File extensions to process (comma-separated)    | `'md,rst,txt,html'` |\n\n### Network Configuration\n| Input             | Description                            | Default     |\n|-------------------|----------------------------------------|-------------|\n| `timeout-seconds` | Connection timeout in seconds          | `5`         |\n| `concurrency`     | Number of concurrent requests          | # CPU cores |\n| `retry`           | Retry attempts for failed requests     | `2`         |\n| `retry-delay-ms`  | Delay between retries in milliseconds  | `1000`      |\n| `rate-limit-ms`   | Delay between requests in milliseconds | `100`       |\n\n### URL Filtering\n| Input               | Description                                                                                            | Default         |\n|---------------------|--------------------------------------------------------------------------------------------------------|-----------------|\n| `allowlist`         | URLs to allow (comma-separated patterns)                                                               |                 |\n| `allow-status`      | HTTP status codes to allow (comma-separated)                                                           | `'200,202,204'` |\n| `exclude-pattern`   | URL patterns to exclude (regex)                                                                        |                 |\n| `allow-timeout`     | Allow URLs that timeout                                                                                | `false`         |\n| `failure-threshold` | Fail only if more than X% of URLs are broken (0-100). Leave empty to fail on any broken URL (default). | `''`            |\n\n### Output Configuration\n| Input     | Description                                        | Default  |\n|-----------|----------------------------------------------------|----------|\n| `quiet`   | Suppress progress output                           | `false`  |\n| `verbose` | Enable verbose logging                             | `false`  |\n\n### Advanced Options\n| Input        | Description                       | Default                            |\n|--------------|-----------------------------------|------------------------------------|\n| `user-agent` | Custom User-Agent header          | `'urlsup-action/{urlsup-version}'` |\n| `proxy`      | HTTP/HTTPS proxy URL              |                                    |\n| `insecure`   | Skip SSL certificate verification | `false`                            |\n\n### Action-Specific Options\n| Input                | Description                                                         | Default    |\n|----------------------|---------------------------------------------------------------------|------------|\n| `urlsup-version`     | Version of urlsup to use                                            | `'latest'` |\n| `create-annotations` | Create GitHub annotations for broken URLs                           | `true`     |\n| `fail-on-error`      | Fail the action if broken URLs are found                            | `true`     |\n| `show-performance`   | Show performance metrics in job summaries                           | `false`    |\n| `telemetry`          | Enable anonymous performance telemetry and metrics in job summaries | `true`     |\n\n## 📤 Outputs\n\n| Output         | Description                         |\n|----------------|-------------------------------------|\n| `total-urls`   | Total number of URLs checked        |\n| `broken-urls`  | Number of broken URLs found         |\n| `success-rate` | Percentage of working URLs          |\n| `report-path`  | Path to detailed JSON report        |\n| `exit-code`    | Exit code from urlsup (0 = success) |\n\n## 📖 Usage Examples\n\n### Basic URL Checking\n```yaml\n- name: Check all markdown files\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: '**/*.md'\n    include-extensions: 'md'\n```\n\n### Advanced Configuration\n```yaml\n- name: Check URLs with custom settings\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'docs/ README.md CHANGELOG.md'\n    timeout-seconds: 15\n    retry: 3\n    concurrency: 20\n    allow-status: '200,202,204,404'\n    exclude-pattern: 'localhost|127\\.0\\.0\\.1|example\\.com'\n    allowlist: 'github.com,docs.github.com'\n    user-agent: 'MyBot/1.0'\n```\n\n### Non-Blocking URL Check\n```yaml\n- name: Check URLs (non-blocking)\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'docs/'\n    fail-on-error: false\n    create-annotations: true\n```\n\n### Complete Workflow with Manual Trigger\n```yaml\nname: URL Validation\n\non:\n  push:\n    branches: [main]\n  pull_request:\n  schedule:\n    - cron: '0 9 * * 1'  # Weekly\n  workflow_dispatch:\n    inputs:\n      files:\n        description: 'Files to check'\n        default: '**/*.md'\n      strict:\n        description: 'Strict mode (fail on any broken URL)'\n        type: boolean\n        default: true\n\npermissions:\n  contents: read\n\njobs:\n  validate-urls:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Validate URLs\n        id: validate-urls\n        uses: simeg/urlsup-action@v2\n        with:\n          files: ${{ inputs.files || '**/*.md' }}\n          timeout-seconds: 5\n          retry: 2\n          rate-limit-ms: 100\n          allow-status: ${{ inputs.strict \u0026\u0026 '200' || '200,202,204' }}\n          failure-threshold: ${{ inputs.strict \u0026\u0026 '' || '3' }}  # Allow 3% broken URLs in non-strict mode\n          show-performance: true                                # Show detailed metrics\n          fail-on-error: ${{ inputs.strict || true }}\n\n      - name: Comment on PR\n        if: github.event_name == 'pull_request' \u0026\u0026 failure()\n        uses: actions/github-script@v7\n        with:\n          script: |\n            const { data: comments } = await github.rest.issues.listComments({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.issue.number,\n            });\n\n            const botComment = comments.find(comment =\u003e\n              comment.user.type === 'Bot' \u0026\u0026 comment.body.includes('URL validation failed')\n            );\n\n            const body = '🔗 **URL validation failed** - Some links in your changes are broken. Please check the workflow run for details.';\n\n            if (botComment) {\n              await github.rest.issues.updateComment({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                comment_id: botComment.id,\n                body\n              });\n            } else {\n              await github.rest.issues.createComment({\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                issue_number: context.issue.number,\n                body\n              });\n            }\n```\n\n## ⚠️ Common Use Cases\n\n### Documentation Sites\n```yaml\n- name: Validate documentation URLs\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'docs/ *.md'\n    include-extensions: 'md,rst'\n    allow-status: '200,202'\n    exclude-pattern: 'localhost|127\\.0\\.0\\.1'\n```\n\n### API Documentation\n```yaml\n- name: Validate API documentation URLs\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'api-docs/'\n    timeout-seconds: 60\n    retry: 3\n    allowlist: 'api.example.com,docs.example.com'\n```\n\n### Lenient Checking with Thresholds\n```yaml\n# Traditional lenient approach (never fails)\n- name: Non-blocking URL validation\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    allow-status: '200,202,204,301,302,429'\n    allow-timeout: true\n    fail-on-error: false\n\n# Modern threshold approach (fails only if too many URLs are broken)\n- name: URL validation with 10% tolerance\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    failure-threshold: \"10\"                 # Allow up to 10% broken URLs\n    allow-status: '200,202,204,301,302,429'\n    show-performance: true                  # Track performance metrics\n    retry: 2\n```\n\n### Failure Threshold and Performance Monitoring\n```yaml\n# Allow some broken URLs with detailed performance tracking\n- name: Validate URLs with tolerance and metrics\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'docs/ README.md'\n    failure-threshold: \"5\"           # Allow up to 5% broken URLs\n    show-performance: true           # Show detailed performance metrics\n    timeout-seconds: 10\n    retry: 3\n    rate-limit-ms: 1500              # Be gentle with external sites\n    allow-status: '200,202,204,429'  # Include rate-limited responses\n```\n\n\n## 📚 Documentation \u0026 Examples\n\n- **[Examples Directory](examples/)** - Real-world workflow examples and configurations\n- **[Configuration Guide](examples/configs/)** - Pre-built configuration templates\n- **[Changelog](CHANGELOG.md)** - Version history and migration guides\n- **[Testing Guide](TESTING.md)** - Testing infrastructure and contribution guidelines\n\n\n## ❓ FAQ\n\n### **Q: How fast is the action after the first run?**\n**A:** After the first run, the action is 5-10x faster due to automatic binary caching. First run may take 1-2 minutes (compiling urlsup), subsequent runs take 10-20 seconds.\n\n### **Q: Do I need to set up caching myself?**\n**A:** No! The action automatically handles binary caching for you. No additional configuration needed.\n\n### **Q: What file types are supported?**\n**A:** By default: Markdown (`.md`), reStructuredText (`.rst`), plain text (`.txt`), and HTML (`.html`). You can customize this with the `include-extensions` input.\n\n### **Q: How do I exclude certain URLs from validation?**\n**A:** Use the `exclude-pattern` input with a regex pattern:\n```yaml\nexclude-pattern: 'localhost|127\\.0\\.0\\.1|example\\.com|internal\\.company\\.com'\n```\n\n### **Q: Can I validate private/internal URLs?**\n**A:** Yes, but they need to be accessible from GitHub Actions runners. For private URLs, consider using `allowlist` or `exclude-pattern` to skip them.\n\n### **Q: The action found broken URLs but they work in my browser. Why?**\n**A:** This is common! Here's how to debug and fix:\n\n**Common causes:**\n- **Rate limiting**: Some sites block automated requests\n- **User-Agent blocking**: Try setting a custom `user-agent`\n- **Geoblocking**: GitHub runners are in different locations\n- **Authentication required**: URLs requiring login will fail\n\n**Debugging steps:**\n1. **Check the annotations** - They now include specific suggestions for each URL\n2. **Test with a custom user-agent:**\n   ```yaml\n   user-agent: 'Mozilla/5.0 (compatible; Documentation Bot)'\n   ```\n3. **Add rate limiting:**\n   ```yaml\n   rate-limit-ms: 2000  # 2 seconds between requests\n   retry: 3\n   ```\n4. **Allow common \"false positive\" status codes:**\n   ```yaml\n   allow-status: '200,202,204,403,429'  # Include 403 (Forbidden) and 429 (Rate Limited)\n   ```\n\n### **Q: How do I handle rate limiting from websites?**\n**A:** Use these inputs to be more respectful:\n```yaml\nrate-limit-ms: 1000     # 1 second between requests\nretry: 3                # Retry failed requests\nallow-status: '200,429' # Accept 429 (Too Many Requests)\n```\n\n### **Q: Can I run this action on a schedule?**\n**A:** Yes! Perfect for monitoring link rot:\n```yaml\non:\n  schedule:\n    - cron: '0 9 * * 1'  # Every Monday at 9 AM\n```\n\n### **Q: How do I troubleshoot specific URL failures?**\n**A:** The action now provides actionable suggestions in annotations. Here are common scenarios:\n\n**GitHub URLs failing:**\n```yaml\n# GitHub often rate limits, be gentle\nrate-limit-ms: 1500\nretry: 2\nallow-status: '200,429'  # Allow rate limit responses\n```\n\n**API documentation with auth:**\n```yaml\n# Skip authenticated endpoints\nexclude-pattern: 'api\\.internal\\.com|admin\\.|\\/auth\\/'\n```\n\n**International/CDN sites:**\n```yaml\n# Some CDNs are geographically restricted\ntimeout-seconds: 10      # Increase timeout\nallow-status: '200,403'  # Allow forbidden for geo-blocking\n```\n\n**Development/staging URLs:**\n```yaml\n# Exclude development environments\nexclude-pattern: 'localhost|127\\.0\\.0\\.1|dev\\.|staging\\.|\\.local'\n```\n\n### **Q: How do I use failure thresholds to allow some broken URLs?**\n**A:** Use the `failure-threshold` parameter to only fail the action when the percentage of broken URLs exceeds a specific threshold:\n\n```yaml\n# Allow up to 5% of URLs to be broken\n- name: Validate URLs with tolerance\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    failure-threshold: \"5\"  # Fail only if \u003e5% of URLs are broken\n    files: 'docs/'\n```\n\n**Common use cases:**\n- **Documentation sites**: Allow 2-5% broken links for external dependencies\n- **Large repositories**: Set 1-3% threshold for legacy or external URLs\n- **CI/CD pipelines**: Use higher thresholds (10-20%) for non-critical checks\n\n**How it works:**\n- If 100 URLs are found and 3 are broken (3%), action passes with `failure-threshold: \"5\"`\n- If 100 URLs are found and 8 are broken (8%), action fails with `failure-threshold: \"5\"`\n- Threshold information is displayed in job summaries with clear pass/fail status\n- Leave empty for default behavior (any broken URL fails the action)\n\n### **Q: How do I see performance metrics in job summaries?**\n**A:** Performance metrics are automatically enabled by default and appear in GitHub job summaries when `telemetry: true` (default). The metrics include:\n\n- **Setup Time** - How long it took to install/cache the urlsup binary\n- **Validation Time** - Duration of URL checking process\n- **Cache Status** - Whether the binary was cached (✅ Hit) or downloaded fresh (❌ Miss)\n\nTo explicitly enable performance metrics:\n```yaml\n- name: Validate URLs with telemetry\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    telemetry: true  # Enable performance tracking (default: true)\n```\n\nTo disable performance metrics:\n```yaml\n- name: Validate URLs without telemetry\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    telemetry: false  # Disable performance tracking\n```\n\n### **Q: How do I combine failure thresholds with performance monitoring?**\n**A:** You can use both features together for comprehensive URL validation:\n\n```yaml\n- name: Validate URLs with threshold and metrics\n  id: validate-urls\n  uses: simeg/urlsup-action@v2\n  with:\n    files: 'docs/ *.md'\n    failure-threshold: \"3\"      # Allow up to 3% broken URLs\n    show-performance: true      # Show detailed performance metrics\n    retry: 2                    # Retry failed URLs twice\n    rate-limit-ms: 1000         # Be gentle with rate limiting\n```\n\nThis configuration will:\n- ✅ Show detailed performance metrics in the job summary\n- ✅ Display failure threshold status (3% tolerance)\n- ✅ Only fail if more than 3% of URLs are broken\n- ✅ Provide actionable recommendations for broken URLs\n\n### **Q: How do I contribute or report issues?**\n**A:**  Report bugs or feature requests on [GitHub Issues](https://github.com/simeg/urlsup-action/issues)\n\n\n## 🔧 Migration from v1\n\n**v1 (Docker-based):**\n```yaml\n- uses: simeg/urlsup-action@v1\n  with:\n    args: '*.md --threads 10 --allow 429'\n```\n\n**v2 (Composite with binary caching, 5-10x faster):**\n```yaml\n- uses: simeg/urlsup-action@v2\n  with:\n    files: '*.md'\n    concurrency: 10\n    allow-status: '200,429'\n```\n\n### Performance Improvements in v2\n- **🚀 Binary Caching**: Automatic caching of urlsup binary across workflow runs\n- **⚡ Faster Startup**: 5-10x faster than Docker-based v1 (seconds vs minutes)\n- **🔄 Smart Cache Keys**: Version and platform-specific caching for reliability\n\n### Breaking Changes\n- `args` input removed → Use structured inputs\n- Default timeout changed from 10s → 5s\n- Now creates annotations by default\n- Requires `actions/checkout@v4`\n\n## 🎯 GitHub Integration\n\n### Annotations\nBroken URLs appear as inline annotations in your files:\n```\n❌ example.md:15 Broken URL: https://example.com/dead-link (HTTP 404)\n```\n\n### Job Summaries\nRich HTML summaries with:\n- 📊 Success rate visualization\n- 📋 Broken URL details table\n- 💡 Actionable recommendations\n- 📁 Downloadable JSON reports\n\n### Artifacts\nDetailed JSON reports are uploaded as workflow artifacts containing:\n- Complete URL validation results\n- File locations and line numbers\n- HTTP status codes and error messages\n- Timing and performance metrics\n\n## 🔧 Internal Scripts\n\nThe action uses several Python scripts located in the `scripts/` directory that handle the core functionality:\n\n### **`scripts/validate.py`**\nThe main validation script that orchestrates the URL checking process.\n\n**Key features:**\n- Translates GitHub Action inputs to urlsup CLI arguments\n- Executes urlsup binary with proper error handling\n- Parses JSON output to extract metrics (total URLs, broken URLs, success rate)\n- Sets GitHub Action outputs for use in subsequent steps\n- Handles both successful and failed validation scenarios\n\n### **`scripts/annotate.py`**\nCreates GitHub annotations for broken URLs found during validation.\n\n**Key features:**\n- Parses urlsup JSON output to identify broken URLs\n- Creates inline file annotations showing broken URLs with line numbers\n- Supports multiple urlsup output formats for backward compatibility\n- Formats error messages with HTTP status codes and detailed error information\n- Gracefully handles parsing errors with fallback methods\n\n### **`scripts/summary.py`**\nGenerates rich HTML job summaries for the GitHub Actions interface.\n\n**Key features:**\n- Creates formatted job summary with success metrics and visual progress bars\n- Displays detailed broken URL information in organized tables\n- Provides actionable recommendations for fixing different types of issues\n- Includes expandable sections with technical details and metadata\n- Handles both successful runs and error scenarios\n\n### **`scripts/common.py`**\nShared utilities and helper functions used across all scripts.\n\n**Key features:**\n- Centralized logging with consistent formatting and colors\n- JSON report parsing with support for multiple urlsup output formats\n- File path normalization and GitHub workspace handling\n- Markdown escaping for safe display in job summaries\n- GitHub Actions integration utilities\n\nThe action also includes inline setup logic that:\n- Installs the Rust toolchain if needed\n- Downloads and installs the urlsup binary via `cargo install`\n- Handles version pinning and caching through GitHub's built-in mechanisms\n\nThese components work together to provide a seamless URL validation experience with rich GitHub integration, automatic binary management, and comprehensive error reporting.\n\n## 🧪 Testing \u0026 Development\n\nThis action includes comprehensive testing infrastructure:\n\n- **Unit Tests** - Full coverage of Python scripts with pytest\n- **End-to-End Tests** - Real-world validation scenarios with generated test data\n- **CI Pipeline** - Multi-platform testing across Python versions\n- **Example Workflows** - Real-world configurations in `examples/`\n- **Configuration Templates** - Pre-built configs for common scenarios\n\n### For Contributors\n\n```bash\n# Install Poetry (if not already installed)\ncurl -sSL https://install.python-poetry.org | python3 -\n\n# Install development dependencies\nmake install\n\n# Run tests\nmake test\n\n# Check code quality\nmake lint\n\n# Format code\nmake format\n\n# Run full CI simulation\nmake ci-local\n```\n\nSee [TESTING.md](TESTING.md) for detailed testing documentation.\n\n## 🔗 Related\n\n- **[urlsup](https://github.com/simeg/urlsup)** - The underlying Rust CLI tool\n- **[Actions Marketplace](https://github.com/marketplace/actions/url-validator)** - Find this action\n- **[GitHub Actions Documentation](https://docs.github.com/en/actions)** - Learn more about workflows\n\n## 📄 License\n\nMIT © [Simon Egersand](https://github.com/simeg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimeg%2Furlsup-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimeg%2Furlsup-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimeg%2Furlsup-action/lists"}