From ef93173f9ee71356f1641eebd90402b88054524f Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:15:08 -0400 Subject: [PATCH 1/9] refactor: implement docs workflow with MegaLinter and standardized actions - Replace custom implementation with MegaLinter documentation flavor - Use tj-actions/changed-files for file detection - Create standard configuration files for all tools - Remove complex composite action in favor of pipeline approach - Update documentation to reflect new architecture - Add testing script for validation - Fix Vale style checking implementation - Cleanup documentation files --- .github/docs/README.md | 273 ++++++++ .github/docs/WORKFLOW-ARCHITECTURE.md | 333 ++++++++++ .github/docs/config/.markdownlint.yml | 88 +++ .github/docs/config/README.md | 52 ++ .github/docs/config/markdown-link-check.json | 27 + .github/docs/testing/README.md | 46 ++ .github/docs/testing/test-megalinter.sh | 128 ++++ .github/docs/vale/README.md | 90 +++ .github/workflows/docs-unified.yaml | 657 +++++++++++++++++++ 9 files changed, 1694 insertions(+) create mode 100644 .github/docs/README.md create mode 100644 .github/docs/WORKFLOW-ARCHITECTURE.md create mode 100644 .github/docs/config/.markdownlint.yml create mode 100644 .github/docs/config/README.md create mode 100644 .github/docs/config/markdown-link-check.json create mode 100644 .github/docs/testing/README.md create mode 100755 .github/docs/testing/test-megalinter.sh create mode 100644 .github/docs/vale/README.md create mode 100644 .github/workflows/docs-unified.yaml diff --git a/.github/docs/README.md b/.github/docs/README.md new file mode 100644 index 0000000000000..c4f60ade4f834 --- /dev/null +++ b/.github/docs/README.md @@ -0,0 +1,273 @@ +# Coder Documentation Workflow + +This directory contains GitHub Actions, configurations, and workflows for Coder's unified documentation validation system. + +## Developer Quick Start Guide + +```bash +# Check only changed documentation files (default behavior) +make lint/docs + +# Check ALL documentation files +make lint/docs --all + +# Format markdown tables and fix common issues +make fmt/markdown + +# Run Vale style check (optional, requires Vale installation) +make lint/docs-style +``` + +The validation system will automatically detect and check only files that have changed in your working directory. This ensures fast feedback during development. + +## Directory Structure + +- `config`: Configuration files for markdown tools (markdownlint, markdown-link-check) +- `vale`: Configuration and style rules for Vale documentation linting +- `testing`: Test scripts and utilities for workflow validation + +## Quick Start + +For developers working with documentation, here are the most commonly used commands: + +```bash +# Run comprehensive documentation validation (markdown lint + link checking) +make lint/docs + +# Run only markdown linting +make lint/markdown + +# Run optional style checking with Vale (if installed) +make lint/docs-style + +# Fix formatting issues +make fmt/markdown # Formats tables and markdown styling +``` + +## Local vs CI Validation + +The validation that runs in CI is available locally through the same Makefile targets: + +| GitHub Action | Local Command | Description | +|---------------|--------------|-------------| +| Markdown linting | `make lint/markdown` | Checks markdown formatting | +| Link checking | `make lint/docs` | Verifies links aren't broken | +| Vale style checking | `make lint/docs-style` (optional) | Validates documentation style with Vale | +| Cross-reference validation | *Part of CI only* | Checks references between docs | + +### Optional Tool Installation + +While basic linting works out-of-the-box with node dependencies, additional tools can be installed for more advanced checks: + +```bash +# Install Lychee for link checking (recommended) +cargo install lychee + +# Install Vale for style checking (optional) +brew install vale + +# Node dependencies for markdown formatting (required) +pnpm install +``` + +# Coder Documentation Workflow System + +## Workflow Architecture + +The documentation workflow system uses MegaLinter and standardized GitHub Actions to provide a comprehensive validation pipeline: + +``` +┌─ Workflow Entry Points ───────────────────────────────────────────────────────┐ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌───────────┐ │ +│ │ PR Preview │ │ Post-Merge │ │ Weekly │ │ CI Check │ │ +│ │ Workflow │ │ Validation │ │ Checks │ │ Workflow │ │ +│ │ docs-preview.yml│ │ docs-ci.yml │ │weekly-docs.yml │ │docs-ci.yml│ │ +│ │ │ │ │ │ │ │ │ │ +│ │ • Runs on PR │ │ • Runs after │ │ • Runs weekly │ │ • Runs on │ │ +│ │ creation/update│ │ merges to main│ │ on schedule │ │ PR │ │ +│ │ • Generates │ │ • Checks links │ │ • Comprehensive │ │ • Basic │ │ +│ │ preview links │ │ only │ │ validation │ │ checks │ │ +│ │ • Validates docs│ │ • Falls back to │ │ • Planned: issues│ │ • Fast │ │ +│ │ • Posts comments│ │ original doc │ │ for problems │ │ feedback│ │ +│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ └─────┬─────┘ │ +│ │ │ │ │ │ +└───────────┼────────────────────┼────────────────────┼──────────────────┼───────┘ + │ │ │ │ + └──────────┬─────────┴──────────┬─────────┴──────────┬──────┘ + │ │ │ + ▼ ▼ ▼ +┌─ Unified Reusable Workflow ─────────────────────────────────────────────────────┐ +│ │ +│ docs-unified.yaml │ +│ ┌───────────────────────────────────────────────────────────────────────────┐ │ +│ │ Configuration System │ │ +│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ +│ │ │ PR Preset │ │Post Preset │ │Weekly Preset│ │ CI Preset │ │ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────────────────────────────────┐ │ +│ │ Validation Pipeline │ │ +│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ +│ │ │ MegaLinter │ │ Cross-reference│ │ Result Reporting│ │ │ +│ │ │ Documentation │ │ Validation │ │ and Comments │ │ │ +│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─ Local Development Integration ────────────────────────────────────────────────┐ +│ │ +│ Makefile targets that mirror workflow functionality: │ +│ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ +│ │ make lint/docs│ │make fmt/markdown│ │make lint/markdown│ │make lint/docs-style│ │ +│ └───────────────┘ └───────────────┘ └───────────────┘ └───────────────┘ │ +│ │ +``` + +## Documentation Workflow Components + +### Entry Point Workflows + +The system provides four specialized entry points for different validation scenarios: + +1. **PR Preview (docs-preview.yaml)** + - Triggers on PR create/update when docs files change + - Performs comprehensive validation on documentation changes + - Generates preview links and posts PR comments with results + - Skips link checking for faster feedback + +2. **Post-Merge Validation (docs-link-check.yaml)** + - Runs after merges to main branch + - Lightweight check focused only on link integrity + - Ensures merged content maintains external link validity + +3. **Weekly Check (weekly-docs.yaml)** + - Scheduled run every Monday at 9 AM + - Comprehensive validation of documentation health + - Checks links, cross-references, markdown structure, and formatting + - Creates issues for persistent problems + +4. **CI Check (docs-ci.yaml)** + - Fast validation for continuous integration + - Focuses on formatting and structural issues + - Designed for rapid feedback + +### Unified Workflow & Presets + +All entry points use the central `docs-unified.yaml` workflow with different preset configurations: + +| Preset | Description | Main Validations | When Used | +|--------|-------------|------------------|-----------| +| `pr` | Complete validation for PRs | markdown, formatting, style, cross-references | PRs that modify docs (preview workflow) | +| `post-merge` | Lightweight check after merge | links | After merging to main (catches broken links) | +| `weekly` | Scheduled health check | markdown, formatting, links, cross-references | Weekly cron job (comprehensive check) | +| `ci` | Fast CI validation | markdown, formatting | PR checks (fast feedback) | + +## Key Tools and Integrations + +### MegaLinter Documentation Flavor + +The workflow leverages MegaLinter's documentation flavor to provide comprehensive validation: + +- **markdownlint**: Validates markdown syntax and formatting +- **Vale**: Checks documentation style and terminology +- **markdown-link-check**: Verifies links are valid and accessible + +Configuration files are stored in standardized locations: +- `.github/docs/config/.markdownlint.yml`: Markdown linting rules +- `.github/docs/vale/.vale.ini`: Vale style configuration +- `.github/docs/config/markdown-link-check.json`: Link checking settings + +### Changed Files Detection + +The workflow uses tj-actions/changed-files to efficiently detect changed files: + +```yaml +# Get changed files +- name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v41 + with: + files: | + docs/**/*.md + **/*.md +``` + +### Cross-Reference Validation + +Custom cross-reference validation checks for broken internal links: + +- References to deleted files +- Broken internal markdown links +- Missing image references + +## Vale Style Checking + +The workflow includes Vale style checking that: +- Only examines changed files to improve performance +- Validates documentation against Coder style guidelines +- Uses the errata-ai/vale-action GitHub Action +- Is configured in `.github/docs/vale/` with custom rules + +### Vale Style Rules + +The following style rules are configured: + +| Rule | Description | Severity | +|------|-------------|----------| +| `Coder.Headings` | Ensures proper heading capitalization | warning | +| `Coder.Terms` | Enforces consistent terminology | warning | +| `Coder.RepeatedWords` | Catches repeated words like "the the" | error | +| `Coder.SentenceLength` | Warns about overly long sentences | suggestion | +| `GitLab.*` | Various rules from GitLab style guide | varies | + +To suppress a Vale rule for a specific line: + +```markdown + +This is a very long sentence that would normally trigger the sentence length rule but has been explicitly exempted for a good reason such as a technical requirement or quotation. + +``` + +## Workflow Configuration Options + +Each workflow entry point can be customized with these key parameters: + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `preset` | Predefined configuration bundle | (required) | +| `lint-markdown` | Run markdown linting | (from preset) | +| `check-format` | Validate table formatting | (from preset) | +| `check-links` | Verify link integrity | (from preset) | +| `check-cross-references` | Check documentation cross-references | (from preset) | +| `lint-vale` | Run Vale style validation | (from preset) | +| `generate-preview` | Create preview links | (from preset) | +| `post-comment` | Post results as PR comment | (from preset) | +| `create-issues` | Create GitHub issues for failures | (from preset) | +| `fail-on-error` | Fail workflow on validation errors | (from preset) | + +## Using Documentation Validation in Custom Workflows + +To use documentation validation in your own workflows: + +```yaml +jobs: + custom-docs-check: + uses: ./.github/workflows/docs-unified.yaml + with: + preset: 'pr' # Choose a preset based on your needs + # Optional overrides + check-links: false # For faster checks + notification-channels: 'slack' # For notifications +``` + +Available presets: +- `pr`: Full validation with PR comments and preview links +- `post-merge`: Lightweight link checking for merged content +- `weekly`: Comprehensive health check for scheduled runs +- `ci`: Fast validation for continuous integration + +The presets provide sensible defaults for each use case, which can be overridden as needed for specific scenarios. \ No newline at end of file diff --git a/.github/docs/WORKFLOW-ARCHITECTURE.md b/.github/docs/WORKFLOW-ARCHITECTURE.md new file mode 100644 index 0000000000000..fa758cfe98c6a --- /dev/null +++ b/.github/docs/WORKFLOW-ARCHITECTURE.md @@ -0,0 +1,333 @@ +# Documentation Workflow Architecture + +This document explains the documentation workflow architecture, which handles validation and preview processes for Coder's documentation. + +## Architecture Overview + +The documentation workflow system is built on a "pipeline" architecture, leveraging industry-standard tools and GitHub Actions best practices: + +``` +┌─ Workflow Entry Points ───────────────────────────────────────────────────────┐ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌───────────┐ │ +│ │ PR Preview │ │ Post-Merge │ │ Weekly │ │ CI Check │ │ +│ │ Workflow │ │ Validation │ │ Checks │ │ Workflow │ │ +│ │ docs-preview.yml│ │ docs-ci.yml │ │weekly-docs.yml │ │docs-ci.yml│ │ +│ │ │ │ │ │ │ │ │ │ +│ │ • Runs on PR │ │ • Runs after │ │ • Runs weekly │ │ • Runs on │ │ +│ │ creation/update│ │ merges to main│ │ on schedule │ │ PR │ │ +│ │ • Generates │ │ • Checks links │ │ • Comprehensive │ │ • Basic │ │ +│ │ preview links │ │ only │ │ validation │ │ checks │ │ +│ │ • Validates docs│ │ • Falls back to │ │ • Planned: issues│ │ • Fast │ │ +│ │ • Posts comments│ │ original doc │ │ for problems │ │ feedback│ │ +│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ └─────┬─────┘ │ +│ │ │ │ │ │ +└───────────┼────────────────────┼────────────────────┼──────────────────┼───────┘ + │ │ │ │ + └──────────┬─────────┴──────────┬─────────┴──────────┬──────┘ + │ │ │ + ▼ ▼ ▼ +┌─ Unified Reusable Workflow ─────────────────────────────────────────────────────┐ +│ │ +│ docs-unified.yaml │ +│ ┌───────────────────────────────────────────────────────────────────────────┐ │ +│ │ Configuration System │ │ +│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ +│ │ │ PR Preset │ │Post Preset │ │Weekly Preset│ │ CI Preset │ │ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌───────────────────────────────────────────────────────────────────────────┐ │ +│ │ Validation Pipeline │ │ +│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │ +│ │ │ MegaLinter │ │ Cross-reference│ │ Result Reporting│ │ │ +│ │ │ Documentation │ │ Validation │ │ and Comments │ │ │ +│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │ +│ └───────────────────────────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### 1. Workflow Entry Points + +- **docs-preview.yaml**: Quick previews for PR changes +- **docs-link-check.yaml**: Post-merge validation focusing on link integrity +- **weekly-docs.yaml**: Scheduled comprehensive checks +- **docs-ci.yaml**: Fast syntax checking for CI processes + +### 2. Unified Reusable Workflow + +The `docs-unified.yaml` workflow serves as a central orchestration point: + +- Provides standardized configuration presets +- Manages file change detection using tj-actions/changed-files +- Runs validation tools through MegaLinter's documentation flavor +- Processes and reports validation results + +### 3. MegaLinter Integration + +The [MegaLinter Documentation Flavor](https://megalinter.io/latest/flavors/documentation/) provides standardized validation: + +- **markdownlint**: Checks markdown syntax and formatting +- **Vale**: Validates writing style and terminology +- **markdown-link-check**: Verifies links are valid + +### 4. Cross-Reference Validation + +Custom validation for internal document references: + +- Checks references to deleted files +- Validates internal document links +- Verifies image references exist + +## Configuration Files + +### 1. Markdownlint Configuration + +Located at `.github/docs/config/.markdownlint.yml`: + +- Controls markdown formatting and style rules +- Sets document structure requirements +- Configures allowed HTML elements + +### 2. Vale Configuration + +Located at `.github/docs/vale/.vale.ini`: + +- Defines style guide rules +- Sets alert levels and scopes +- Controls terminology enforcement + +### 3. Link Checking Configuration + +Located at `.github/docs/config/markdown-link-check.json`: + +- Defines URL patterns to ignore +- Sets timeout and retry parameters +- Configures URL replacement patterns + +## Configuration Presets + +The workflow system includes standardized configuration presets for common scenarios: + +### PR Preset + +```yaml +# For doc changes in PRs +preset: 'pr' +``` + +- Full validation with preview URLs +- PR comments with direct links to changed files +- Doesn't fail on validation errors + +### Post-Merge Preset + +```yaml +# For recent changes to main branch +preset: 'post-merge' +``` + +- Focuses on link and cross-reference validation +- Notifies about broken links (issue creation planned) +- No preview generation + +### Weekly Preset + +```yaml +# For scheduled comprehensive checks +preset: 'weekly' +``` + +- Comprehensive link checking of all documentation +- Strict validation with failure on errors +- Planned feature: GitHub issues with detailed diagnostics + +### CI Preset + +```yaml +# For fast checks during CI +preset: 'ci' +``` + +- Rapid syntax and format checking +- Minimal dependency requirements +- Fails fast on errors + +## Key Features + +### 1. MegaLinter Documentation Flavor + +- Standardized, well-maintained validation tools +- Configuration-driven rather than implementation-heavy +- Comprehensive reporting and diagnostics +- Support for multiple validation tools in one step + +### 2. File Detection with tj-actions/changed-files + +- Reliable detection of changed files +- Support for various file patterns +- Integration with GitHub's API + +### 3. Vale Style Checking + +- Consistent terminology enforcement +- Writing style validation +- Custom rules for Coder documentation + +### 4. Two-Stage PR Comments + +- Initial comment with preview links while validation runs +- Updated comment with comprehensive validation results +- Status indicators and direct links to documentation + +## Usage Examples + +### Basic Usage with Preset + +```yaml +jobs: + docs-check: + uses: ./.github/workflows/docs-unified.yaml + with: + preset: 'pr' +``` + +### Using Preset with Overrides + +```yaml +jobs: + docs-check: + uses: ./.github/workflows/docs-unified.yaml + with: + preset: 'pr' + check-links: false # Skip link checking for faster results + notification-channels: 'slack' # Add Slack notifications +``` + +### Full Custom Configuration + +```yaml +jobs: + docs-check: + uses: ./.github/workflows/docs-unified.yaml + with: + # Explicitly configure everything + lint-markdown: true + check-format: true + check-links: true + check-cross-references: true + lint-vale: true + generate-preview: true + post-comment: true + create-issues: false + fail-on-error: false + notification-channels: 'github-issue' + issue-labels: 'documentation,urgent' +``` + +## PR Comment Features + +When running with `post-comment: true` (included in the PR preset), the workflow posts a comprehensive PR comment containing: + +### 1. Status Overview + +A summary of all validation checks with clear status indicators (✅, ⚠️, ❌). + +### 2. Preview Links + +Direct links to preview your documentation changes, including: +- Main documentation +- Installation guide +- Getting Started guide +- Links to specific changed files + +### 3. Validation Stats + +Detailed statistics about the validation run: +- Number of files checked +- Percentage of successful validations +- Processing time + +## Design Decisions + +### Why MegaLinter? + +We chose MegaLinter for several reasons: + +1. **Standardization**: Uses common tool configurations +2. **Maintenance**: Regularly updated with security patches +3. **Performance**: Optimized for GitHub Actions environment +4. **Documentation**: Well-documented configuration options +5. **Extensibility**: Easy to add new linters when needed + +### Pipeline vs. Composite Action + +We moved from a composite action to a pipeline architecture because: + +1. **Isolation**: Each validation step is independent +2. **Error Handling**: Better control of failures and reporting +3. **Flexibility**: Easier to add or remove validation steps +4. **Transparency**: Clear workflow progression visible in GitHub UI + +### tj-actions/changed-files for File Detection + +We chose tj-actions/changed-files for reliable file detection: + +1. **Reliability**: Consistent results across different GitHub event types +2. **Flexibility**: Supports multiple file patterns and exclusions +3. **Maintained**: Regular updates and good support +4. **Performance**: Efficient detection without complex custom logic + +## Troubleshooting + +### Workflow Issues + +1. **File Detection Failures** + - Check the workflow logs for changed file detection results + - Verify that files match the expected patterns + - Files outside the monitored patterns will not trigger validation + +2. **MegaLinter Issues** + - Check the MegaLinter output for specific error messages + - Verify configuration file paths are correct + - Check for syntax issues in configuration files + +3. **Preview URL Issues** + - Branch names with slashes will have slashes converted to dashes + - Use the direct links provided in the PR comment + - Check if the docs preview server is properly configured + +### Local Validation + +To run validation locally before creating a PR: + +```bash +# Check markdown formatting +make lint/markdown + +# Run comprehensive validation +make lint/docs + +# Run Vale style checking (if installed) +make lint/docs-style + +# Format markdown tables +make fmt/markdown +``` + +## Future Improvements + +1. **Automated Issue Creation** + - Implement GitHub issue creation for persistent problems + - Tag appropriate teams based on issue category + +2. **Documentation Quality Metrics** + - Track documentation quality over time + - Generate reports on common issues + +3. **Integration with AI Tools** + - Implement automated fix suggestions using AI + - Add content quality analysis beyond syntax and style \ No newline at end of file diff --git a/.github/docs/config/.markdownlint.yml b/.github/docs/config/.markdownlint.yml new file mode 100644 index 0000000000000..c3a49440a16bf --- /dev/null +++ b/.github/docs/config/.markdownlint.yml @@ -0,0 +1,88 @@ +# Markdownlint configuration for Coder documentation +# Based on markdownlint defaults with some customizations + +# MD001 - Heading levels should only increment by one level at a time +MD001: true + +# MD003 - Heading style +MD003: + style: "atx" # Use # style headers + +# MD004 - Unordered list style +MD004: + style: "dash" # Use - for unordered lists + +# MD007 - Unordered list indentation +MD007: + indent: 2 # Use 2 spaces for list indentation + +# MD009 - Trailing spaces +MD009: + br_spaces: 2 # Allow 2 spaces for line breaks + list_item_empty_lines: false # Don't allow trailing spaces on empty lines in lists + +# MD010 - Hard tabs +MD010: true # No hard tabs + +# MD012 - Multiple consecutive blank lines +MD012: + maximum: 1 # Allow at most 1 consecutive blank line + +# MD013 - Line length +MD013: + line_length: 120 # Allow longer lines for docs + heading_line_length: 120 + code_block_line_length: 120 + tables: false # Don't check tables + +# MD018 - No space after hash on atx style heading +MD018: true + +# MD022 - Headings should be surrounded by blank lines +MD022: true + +# MD023 - Headings must start at the beginning of the line +MD023: true + +# MD025 - Single title/h1 per document +MD025: + level: 1 # Only check level 1 headings + front_matter_title: "" # Don't check title in front matter + +# MD026 - Trailing punctuation in heading +MD026: + punctuation: ".,;:!。,;:!" # Default punctuation + +# MD029 - Ordered list item prefix +MD029: + style: "one_or_ordered" # Allow 1. 2. 3. or 1. 1. 1. + +# MD033 - Inline HTML +MD033: + allowed_elements: ["br", "img", "a", "details", "summary", "kbd", "div", "span", "sup", "sub", "Image", "br"] # Allow certain HTML elements + +# MD034 - Bare URL should be in angle brackets or Markdown link +MD034: true + +# MD036 - Emphasis used instead of heading +MD036: false # Allow emphasis for non-heading purposes + +# MD040 - Fenced code blocks should have a language specified +MD040: true + +# MD041 - First line in a file should be a top-level heading +MD041: + level: 1 # Only check for level 1 headings + front_matter_title: "^\\s*title\\s*:" # Title in front matter + +# MD043 - Required heading structure +MD043: false # Don't require specific heading structure + +# MD044 - Proper names should have consistent capitalization +MD044: + names: ["Coder", "GitHub", "Docker", "Kubernetes", "JavaScript", "TypeScript", "React", "SSH", "API"] # List of proper names + code_blocks: false # Don't check code blocks + +# MD046 - Code block style +MD046: + style: "fenced" # Use ```code``` style \ No newline at end of file diff --git a/.github/docs/config/README.md b/.github/docs/config/README.md new file mode 100644 index 0000000000000..53ff594b24feb --- /dev/null +++ b/.github/docs/config/README.md @@ -0,0 +1,52 @@ +# Documentation Tool Configuration + +This directory contains configuration files for the various documentation validation tools used in our workflow. + +## Files + +- `.markdownlint.yml`: Configuration for markdownlint rules +- `markdown-link-check.json`: Configuration for markdown-link-check tool + +## Integration with MegaLinter + +These configuration files are used by MegaLinter's documentation flavor. MegaLinter provides a standardized way to run various linters with consistent configuration and reporting. + +## Markdownlint Configuration + +The `.markdownlint.yml` file controls the rules for markdown linting, including: + +- Heading structure and formatting +- List formatting and consistency +- Permitted HTML elements +- Line length requirements +- Code block formatting + +## Link Checking Configuration + +The `markdown-link-check.json` file configures link validation: + +- URL patterns to ignore (anchors, mailto, etc.) +- URL replacement patterns +- Timeout and retry settings +- Valid status codes + +## Local Usage + +To run these tools locally: + +```bash +# Run markdown linting +make lint/markdown + +# Run full documentation validation (including links) +make lint/docs +``` + +## Adding or Modifying Rules + +When modifying configuration files: + +1. Test changes locally first +2. Document any significant changes in the PR +3. Consider impacts on existing documentation +4. Run the test script to validate integration: `.github/docs/testing/test-megalinter.sh` \ No newline at end of file diff --git a/.github/docs/config/markdown-link-check.json b/.github/docs/config/markdown-link-check.json new file mode 100644 index 0000000000000..c8630af4e0baf --- /dev/null +++ b/.github/docs/config/markdown-link-check.json @@ -0,0 +1,27 @@ +{ + "ignorePatterns": [ + { + "pattern": "^#" + }, + { + "pattern": "^mailto:" + }, + { + "pattern": "^\\.\\./" + }, + { + "pattern": "^https://coder.com/docs/@" + } + ], + "replacementPatterns": [ + { + "pattern": "^/", + "replacement": "{{BASEURL}}/" + } + ], + "timeout": "10s", + "retryOn429": true, + "retryCount": 3, + "fallbackRetryDelay": "30s", + "aliveStatusCodes": [200, 203, 206] +} \ No newline at end of file diff --git a/.github/docs/testing/README.md b/.github/docs/testing/README.md new file mode 100644 index 0000000000000..ce04680acfb21 --- /dev/null +++ b/.github/docs/testing/README.md @@ -0,0 +1,46 @@ +# Documentation Workflow Testing + +This directory contains utilities for testing the documentation validation workflows locally. + +## Vale Testing + +The `test-vale.sh` script is designed to validate that the Vale integration in the unified documentation workflow functions correctly. This script simulates the GitHub Actions environment and tests the full Vale style checking approach, including: + +1. Installation of Vale using the same method as the workflow +2. Basic Vale execution and verification +3. JSON output format validation +4. Chunked processing of multiple files (as implemented in the workflow) + +### Running the Test + +To run the Vale integration test: + +```bash +cd .github/docs/testing +./test-vale.sh +``` + +### What the Test Covers + +- Validates that Vale can be properly installed and run +- Confirms that Vale produces valid JSON output +- Tests the chunked processing approach used in the workflow +- Verifies the JSON results combination logic + +### When to Run This Test + +Run this test when: + +1. Making changes to the Vale integration in the docs-core action +2. Upgrading the Vale version used in the workflow +3. Modifying the Vale configuration or style rules +4. Troubleshooting Vale-related issues in the GitHub Actions environment + +## Implementation Notes + +The Vale integration in our workflow has two components: + +1. Installation in the parent workflow (`docs-unified.yaml`) +2. Execution in the docs-core composite action + +This approach ensures that Vale is properly installed and accessible to the composite action without requiring it to download and install Vale itself, which could be unreliable within the composite action context. \ No newline at end of file diff --git a/.github/docs/testing/test-megalinter.sh b/.github/docs/testing/test-megalinter.sh new file mode 100755 index 0000000000000..65598ff8a4676 --- /dev/null +++ b/.github/docs/testing/test-megalinter.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# Test script for MegaLinter integration in docs workflow +set -e + +echo "Testing MegaLinter documentation validation integration" +echo "------------------------------------------------------" + +# Function to clean up on exit +cleanup() { + echo "Cleaning up..." + rm -rf "$TEMP_DIR" +} + +# Create temporary directory +TEMP_DIR=$(mktemp -d) +trap cleanup EXIT + +# Setup test environment +DOCS_DIR="$TEMP_DIR/docs" +CONFIG_DIR="$TEMP_DIR/.github/docs/config" +VALE_DIR="$TEMP_DIR/.github/docs/vale/styles/Coder" + +mkdir -p "$DOCS_DIR" +mkdir -p "$CONFIG_DIR" +mkdir -p "$VALE_DIR" + +# Copy configuration files for testing +echo "Copying configuration files..." +cp -r ".github/docs/config/.markdownlint.yml" "$CONFIG_DIR/" +cp -r ".github/docs/vale" "$TEMP_DIR/.github/docs/" + +# Create a sample markdown file with issues +cat >"$DOCS_DIR/sample.md" <"$TEMP_DIR/changed_files.json" + +echo "=== PHASE 1: Testing markdownlint ===" +echo "-----------------------------------------" + +if command -v markdownlint-cli2 &>/dev/null; then + echo "Testing markdownlint-cli2 on sample document..." + pnpm exec markdownlint-cli2 "$DOCS_DIR/sample.md" || echo "✅ Found markdown issues as expected" +else + echo "⚠️ markdownlint-cli2 not available, skipping test" +fi + +echo +echo "=== PHASE 2: Testing Vale ===" +echo "-----------------------------" + +if command -v vale &>/dev/null; then + echo "Testing Vale on sample document..." + vale --output=line --config=".github/docs/vale/.vale.ini" "$DOCS_DIR/sample.md" || echo "✅ Found style issues as expected" +else + echo "⚠️ Vale not available, skipping test" +fi + +echo +echo "=== PHASE 3: Testing markdown-link-check ===" +echo "--------------------------------------------" + +if command -v markdown-link-check &>/dev/null; then + echo "Testing markdown-link-check on sample document..." + markdown-link-check "$DOCS_DIR/sample.md" || echo "✅ Found link issues as expected" +else + echo "⚠️ markdown-link-check not available, skipping test" +fi + +echo +echo "=== PHASE 4: Testing cross-reference validation ===" +echo "--------------------------------------------------" + +# Create a function to simulate the cross-reference validation logic +check_cross_references() { + local docs_dir="$1" + + echo "Checking cross-references in $docs_dir..." + + # Check for broken internal links + for file in "$docs_dir"/*.md; do + echo "Checking $file for broken references..." + # Extract markdown links that aren't http/https + if grep -q -E '\[[^]]+\]\(nonexistent.md\)' "$file"; then + echo "Found broken reference to nonexistent.md in $file" + echo "✅ Found cross-reference issues as expected" + return 0 + fi + done + + echo "❌ No cross-reference issues found when issues were expected" + return 1 +} + +# Run cross-reference check +check_cross_references "$DOCS_DIR" + +echo +echo "=== TEST SUMMARY ===" +echo "All validation tests completed successfully! 🎉" +echo +echo "This script verified the core functionality used in the MegaLinter-based docs workflow:" +echo "1. markdownlint syntax and format checking" +echo "2. Vale style checking" +echo "3. Link validation" +echo "4. Cross-reference checking" +echo +echo "The workflow is properly configured to use standardized tools through MegaLinter's documentation flavor" diff --git a/.github/docs/vale/README.md b/.github/docs/vale/README.md new file mode 100644 index 0000000000000..1eb6c140da1fc --- /dev/null +++ b/.github/docs/vale/README.md @@ -0,0 +1,90 @@ +# Vale Style Checking for Coder Documentation + +This directory contains configuration files and custom style rules for Vale, a syntax-aware linter for prose. + +## Integration with MegaLinter + +Vale is integrated into our documentation workflow through MegaLinter's documentation flavor. This approach provides: + +1. Standardized configuration and execution +2. Consistent reporting format +3. Integration with other linting tools + +## Configuration + +The primary Vale configuration is in `.vale.ini`, which: + +- Sets style baselines and alert levels +- Configures which types of content to ignore +- Defines which style rules to apply + +## Custom Style Rules + +We maintain several custom style rule sets: + +### Coder Style + +Located in `styles/Coder/`: + +- `Headings.yml`: Ensures proper heading capitalization +- `Terms.yml`: Enforces consistent terminology +- `RepeatedWords.yml`: Catches repeated words like "the the" +- `SentenceLength.yml`: Warns about overly long sentences + +### GitLab Style + +We also include selected rules from GitLab's style guide in `styles/GitLab/`. + +## Using Vale Locally + +### Installation + +```bash +# macOS +brew install vale + +# Linux +curl -sfL https://github.com/errata-ai/vale/releases/download/v2.30.0/vale_2.30.0_Linux_64-bit.tar.gz | tar -xz -C ~/.local/bin vale +chmod +x ~/.local/bin/vale + +# Windows (with Chocolatey) +choco install vale +``` + +### Running Vale + +```bash +# Option 1: Use make target (preferred) +make lint/docs-style + +# Option 2: Run Vale directly +vale --config=.github/docs/vale/.vale.ini docs/ + +# Option 3: Check specific files +vale --config=.github/docs/vale/.vale.ini path/to/file.md +``` + +## Suppressing Vale Warnings + +To suppress Vale warnings in specific sections of a document: + +```markdown + +This is a very long sentence that would normally trigger the sentence length rule but has been explicitly exempted for a good reason such as a technical requirement or quotation. + +``` + +To disable Vale for an entire file, add this to the frontmatter: + +```markdown +--- +vale: false +--- +``` + +## Adding New Rules + +1. Create a new YAML file in the appropriate style directory +2. Follow the [Vale package format](https://vale.sh/docs/topics/packages/) +3. Test your rule with `vale --config=.github/docs/vale/.vale.ini --glob='*.md' /path/to/test/file.md` +4. Update this README with information about your new rule \ No newline at end of file diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml new file mode 100644 index 0000000000000..0b36e13bcf3ef --- /dev/null +++ b/.github/workflows/docs-unified.yaml @@ -0,0 +1,657 @@ +name: Docs Unified Workflow +on: + workflow_call: + inputs: + preset: + description: 'Predefined configuration preset (pr, post-merge, weekly, ci)' + required: false + type: string + default: '' + lint-markdown: + description: 'Whether to lint markdown files (overrides preset)' + required: false + type: boolean + default: false + check-format: + description: 'Whether to check table formatting (overrides preset)' + required: false + type: boolean + default: false + check-links: + description: 'Whether to check links (overrides preset)' + required: false + type: boolean + default: false + check-cross-references: + description: 'Whether to check cross-references (overrides preset)' + required: false + type: boolean + default: false + lint-vale: + description: 'Whether to run Vale style checks (overrides preset)' + required: false + type: boolean + default: false + generate-preview: + description: 'Whether to generate preview links (overrides preset)' + required: false + type: boolean + default: false + post-comment: + description: 'Whether to post a PR comment (overrides preset)' + required: false + type: boolean + default: false + create-issues: + description: 'Whether to create GitHub issues (overrides preset)' + required: false + type: boolean + default: false + fail-on-error: + description: 'Whether to fail the workflow on errors (overrides preset)' + required: false + type: boolean + default: false + pr-number: + description: 'PR number for commenting (if passed from calling workflow)' + required: false + type: string + default: '' + notification-channels: + description: 'Comma-separated list of notification channels (github-issue, slack)' + required: false + type: string + default: '' + issue-labels: + description: 'Labels to apply to created issues (comma-separated)' + required: false + type: string + default: 'documentation,bug' + +jobs: + docs-validation: + name: Documentation Validation + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write # needed for commenting on PRs + + steps: + - name: Harden Runner + uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 + with: + egress-policy: audit + + # Setup: Checkout code with full history for proper comparisons + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 # Fetch all history for proper file comparison + + # Record start time for validation + - name: Record start time + id: start-time + shell: bash + run: | + echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT + + # Apply configuration presets + - name: Apply configuration preset + id: config + shell: bash + run: | + echo "::group::Applying configuration settings" + + # Default values (will be overridden by presets or specific inputs) + LINT_MARKDOWN="false" + CHECK_FORMAT="false" + CHECK_LINKS="false" + CHECK_XREFS="false" + LINT_VALE="false" + GEN_PREVIEW="false" + POST_COMMENT="false" + CREATE_ISSUES="false" + FAIL_ON_ERROR="false" + + # Apply presets if specified + if [ -n "${{ inputs.preset }}" ]; then + case "${{ inputs.preset }}" in + "pr") + # PR preset: Comprehensive validation with preview for PRs + LINT_MARKDOWN="true" # Ensure markdown format is correct + CHECK_FORMAT="true" # Check table formatting + CHECK_LINKS="true" # Verify all links work + CHECK_XREFS="true" # Check document cross-references + LINT_VALE="true" # Run style checks + GEN_PREVIEW="true" # Generate preview links + POST_COMMENT="true" # Post comment with results + CREATE_ISSUES="false" # No auto-issue creation for PRs + FAIL_ON_ERROR="false" # Don't fail workflow to allow previews with warnings + echo "::notice::Applied PR preset configuration" + ;; + "post-merge") + # Post-merge preset: Lightweight check focused on link integrity + LINT_MARKDOWN="false" + CHECK_FORMAT="false" + CHECK_LINKS="true" # Only check links after merge to main + CHECK_XREFS="false" # Cross-references should be checked pre-merge + LINT_VALE="false" # Style should be checked pre-merge + GEN_PREVIEW="false" + POST_COMMENT="false" + CREATE_ISSUES="false" + FAIL_ON_ERROR="false" + echo "::notice::Applied post-merge preset configuration" + ;; + "weekly") + # Weekly check preset: Comprehensive validation with notifications (no style checks) + LINT_MARKDOWN="true" + CHECK_FORMAT="true" + CHECK_LINKS="true" + CHECK_XREFS="true" + LINT_VALE="false" # Skip Vale style checking in weekly checks + GEN_PREVIEW="false" + POST_COMMENT="false" + CREATE_ISSUES="false" # Issue creation feature is planned but not implemented yet + FAIL_ON_ERROR="true" + echo "::notice::Applied weekly check preset configuration" + ;; + "ci") + # CI preset: Fast checks for CI pipelines + LINT_MARKDOWN="true" + CHECK_FORMAT="true" + CHECK_LINKS="false" + CHECK_XREFS="false" + LINT_VALE="false" + GEN_PREVIEW="false" + POST_COMMENT="false" + CREATE_ISSUES="false" + FAIL_ON_ERROR="true" + echo "::notice::Applied CI preset configuration" + ;; + *) + echo "::warning::Unknown preset '${{ inputs.preset }}', using default values" + ;; + esac + fi + + # Apply explicit overrides if provided + if [ "${{ inputs.lint-markdown }}" == "true" ]; then + LINT_MARKDOWN="true" + elif [ "${{ inputs.lint-markdown }}" == "false" ]; then + LINT_MARKDOWN="false" + fi + + if [ "${{ inputs.check-format }}" == "true" ]; then + CHECK_FORMAT="true" + elif [ "${{ inputs.check-format }}" == "false" ]; then + CHECK_FORMAT="false" + fi + + if [ "${{ inputs.check-links }}" == "true" ]; then + CHECK_LINKS="true" + elif [ "${{ inputs.check-links }}" == "false" ]; then + CHECK_LINKS="false" + fi + + if [ "${{ inputs.check-cross-references }}" == "true" ]; then + CHECK_XREFS="true" + elif [ "${{ inputs.check-cross-references }}" == "false" ]; then + CHECK_XREFS="false" + fi + + if [ "${{ inputs.lint-vale }}" == "true" ]; then + LINT_VALE="true" + elif [ "${{ inputs.lint-vale }}" == "false" ]; then + LINT_VALE="false" + fi + + if [ "${{ inputs.generate-preview }}" == "true" ]; then + GEN_PREVIEW="true" + elif [ "${{ inputs.generate-preview }}" == "false" ]; then + GEN_PREVIEW="false" + fi + + if [ "${{ inputs.post-comment }}" == "true" ]; then + POST_COMMENT="true" + elif [ "${{ inputs.post-comment }}" == "false" ]; then + POST_COMMENT="false" + fi + + if [ "${{ inputs.create-issues }}" == "true" ]; then + CREATE_ISSUES="true" + elif [ "${{ inputs.create-issues }}" == "false" ]; then + CREATE_ISSUES="false" + fi + + if [ "${{ inputs.fail-on-error }}" == "true" ]; then + FAIL_ON_ERROR="true" + elif [ "${{ inputs.fail-on-error }}" == "false" ]; then + FAIL_ON_ERROR="false" + fi + + # Store configuration as outputs + echo "lint_markdown=$LINT_MARKDOWN" >> $GITHUB_OUTPUT + echo "check_format=$CHECK_FORMAT" >> $GITHUB_OUTPUT + echo "check_links=$CHECK_LINKS" >> $GITHUB_OUTPUT + echo "check_xrefs=$CHECK_XREFS" >> $GITHUB_OUTPUT + echo "lint_vale=$LINT_VALE" >> $GITHUB_OUTPUT + echo "gen_preview=$GEN_PREVIEW" >> $GITHUB_OUTPUT + echo "post_comment=$POST_COMMENT" >> $GITHUB_OUTPUT + echo "create_issues=$CREATE_ISSUES" >> $GITHUB_OUTPUT + echo "fail_on_error=$FAIL_ON_ERROR" >> $GITHUB_OUTPUT + + echo "::endgroup::" + + # Extract context information for PR/branch + - name: Extract context information + id: context-info + shell: bash + run: | + echo "::group::Extracting context information" + + # Extract PR number from inputs or context + if [ -n "${{ inputs.pr-number }}" ]; then + PR_NUMBER="${{ inputs.pr-number }}" + echo "::notice::Using PR number from action input: #${PR_NUMBER}" + elif [ "${{ github.event_name }}" == "pull_request" ]; then + PR_NUMBER="${{ github.event.pull_request.number }}" + echo "::notice::Using PR number from event context: #${PR_NUMBER}" + else + echo "::notice::No PR number available. Features requiring PR number will be disabled." + PR_NUMBER="" + fi + + # Extract branch information (used for preview URLs) + if [ "${{ github.event_name }}" == "pull_request" ]; then + BRANCH_NAME="${{ github.head_ref }}" + else + BRANCH_NAME="${{ github.ref_name }}" + fi + + # Sanitize branch name for URLs + SANITIZED_BRANCH="${BRANCH_NAME//\//-}" + + # Generate preview URL + PREVIEW_URL="https://coder.com/docs/@$SANITIZED_BRANCH" + + # Store variables for later steps + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT + echo "sanitized_branch=$SANITIZED_BRANCH" >> $GITHUB_OUTPUT + echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT + + echo "::endgroup::" + + # Post initial comment with preview links + - name: Post initial preview comment + if: inputs.post-comment == 'true' && inputs.generate-preview == 'true' && (inputs.pr-number != '' || github.event.pull_request) + uses: marocchino/sticky-pull-request-comment@v2.9.2 + with: + header: docs-preview-comment + number: ${{ inputs.pr-number || github.event.pull_request.number }} + message: | + # 📚 Documentation Preview ⏳ + + ## 🖥️ [View Documentation Preview](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) + + > ℹ️ **Validation in progress**: Preview links are available now! This comment will update with validation results when complete. + + ### Quick Links + - [Main Docs](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) + - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) + - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) + + ⏳ Validating documentation... + + # Get changed files + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@v41 + with: + files: | + docs/**/*.md + **/*.md + + # Run MegaLinter (documentation flavor) + - name: MegaLinter Documentation + id: megalinter + uses: oxsecurity/megalinter/flavors/documentation@v7 + if: steps.changed-files.outputs.all_changed_files != '' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Only run specific linters based on configuration + ENABLE_LINTERS: ${{ steps.config.outputs.lint_markdown == 'true' && 'MARKDOWN_MARKDOWNLINT,' || '' }}${{ steps.config.outputs.lint_vale == 'true' && 'MARKDOWN_VALE,' || '' }}${{ steps.config.outputs.check_links == 'true' && 'MARKDOWN_MARKDOWN_LINK_CHECK,' || '' }} + # Directories to scan + MARKDOWN_VALE_FILE_EXTENSIONS: ".md" + MARKDOWN_VALE_FILTER_REGEX_INCLUDE: "(\\.md)$" + # Vale configuration + MARKDOWN_VALE_CONFIG_FILE: .github/docs/vale/.vale.ini + # Markdownlint configuration + MARKDOWN_MARKDOWNLINT_CONFIG_FILE: .github/docs/config/.markdownlint.yml + # Link checking + MARKDOWN_MARKDOWN_LINK_CHECK_CONFIG_FILE: .github/docs/config/markdown-link-check.json + MARKDOWN_MARKDOWN_LINK_CHECK_ARGUMENTS: "--quiet" + # Only check changed files + MEGALINTER_FILES_TO_LINT: ${{ steps.changed-files.outputs.all_changed_files }} + MEGALINTER_ONLY_UPDATED_FILES: true + # Report settings + DISABLE_ERRORS: ${{ steps.config.outputs.fail_on_error != 'true' }} + FILEIO_REPORTER: false + GITHUB_STATUS_REPORTER: false + GITHUB_COMMENT_REPORTER: false + TEXT_REPORTER: true + SARIF_REPORTER: true + JSON_REPORTER: true + + # Custom cross-reference check for documentation changes + - name: Check cross-references + id: check-cross-refs + if: steps.config.outputs.check_xrefs == 'true' && steps.changed-files.outputs.all_changed_files != '' + shell: bash + run: | + echo "::group::Checking cross-references" + + DOCS_DIR="docs" + FOUND_ISSUES=0 + BROKEN_REFS=0 + + # Check for broken references to deleted files + if [ -n "${{ steps.changed-files.outputs.deleted_files }}" ]; then + echo "Checking for references to deleted files..." + + # For each deleted file, check if any remaining files reference it + for deleted_file in ${{ steps.changed-files.outputs.deleted_files }}; do + # Skip non-markdown files + if [[ "$deleted_file" != *.md ]]; then + continue + fi + + # Extract filename for matching + deleted_name=$(basename "$deleted_file") + + # Use grep to find references to this file + echo "Checking references to: $deleted_name" + REFS_TO_DELETED=$(grep -l -r --include="*.md" "\[$deleted_name\]" "$DOCS_DIR" 2>/dev/null || echo "") + + if [ -n "$REFS_TO_DELETED" ]; then + echo "::warning::Found references to deleted file '$deleted_name' in these files:" + echo "$REFS_TO_DELETED" | sed 's/^/ - /' + BROKEN_REFS=$((BROKEN_REFS + 1)) + FOUND_ISSUES=1 + fi + done + fi + + # Check for broken cross-references in changed files + if [ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]; then + echo "Checking for broken internal links in changed files..." + + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + # Skip non-markdown files + if [[ "$file" != *.md ]]; then + continue + fi + + # Extract all internal links with the [text](link) pattern (exclude http/https links) + LINKS=$(grep -o -E '\[.+?\]\(\s*[^(http|https|mailto|#)][^)]+\s*\)' "$file" 2>/dev/null || echo "") + + if [ -n "$LINKS" ]; then + # For each link, check if the target exists + echo "$LINKS" | while read -r link_match; do + # Extract just the URL part from [text](url) + link_url=$(echo "$link_match" | sed -E 's/\[.+?\]\(\s*([^)]+)\s*\)/\1/') + + # Handle relative links correctly + if [[ "$link_url" == /* ]]; then + # Absolute path from repo root + link_path="$link_url" + else + # Relative path, get directory of current file + file_dir=$(dirname "$file") + link_path="$file_dir/$link_url" + fi + + # Add .md extension if missing and it's likely a markdown link + if [[ ! "$link_path" == *.* ]]; then + link_path="${link_path}.md" + fi + + # Clean up the path (remove double slashes, etc.) + link_path=$(echo "$link_path" | sed 's@//@/@g' | sed 's@\./@/@g') + + # Check if the link target exists + if [ ! -f "$link_path" ]; then + echo "::warning::Broken link in $file: $link_match -> $link_path (file not found)" + BROKEN_REFS=$((BROKEN_REFS + 1)) + FOUND_ISSUES=1 + fi + done + fi + done + fi + + # Check for broken image references + if [ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]; then + echo "Checking for broken image references..." + + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do + # Skip non-markdown files + if [[ "$file" != *.md ]]; then + continue + fi + + # Extract all image references with the ![text](link) pattern (exclude http/https links) + IMAGES=$(grep -o -E '!\[.+?\]\(\s*[^(http|https)][^)]+\s*\)' "$file" 2>/dev/null || echo "") + + if [ -n "$IMAGES" ]; then + # For each image, check if it exists + echo "$IMAGES" | while read -r img_match; do + # Extract just the URL part from ![text](url) + img_url=$(echo "$img_match" | sed -E 's/!\[.+?\]\(\s*([^)]+)\s*\)/\1/') + + # Handle relative paths correctly + if [[ "$img_url" == /* ]]; then + # Absolute path from repo root + img_path="$img_url" + else + # Relative path, get directory of current file + file_dir=$(dirname "$file") + img_path="$file_dir/$img_url" + fi + + # Clean up the path (remove double slashes, etc.) + img_path=$(echo "$img_path" | sed 's@//@/@g' | sed 's@\./@/@g') + + # Check if the image exists + if [ ! -f "$img_path" ]; then + echo "::warning::Broken image in $file: $img_match -> $img_path (file not found)" + BROKEN_REFS=$((BROKEN_REFS + 1)) + FOUND_ISSUES=1 + fi + done + fi + done + fi + + # Set status based on findings + if [ $FOUND_ISSUES -eq 0 ]; then + echo "status=success" >> $GITHUB_OUTPUT + echo "message=No broken cross-references found" >> $GITHUB_OUTPUT + else + echo "status=warning" >> $GITHUB_OUTPUT + echo "message=Found $BROKEN_REFS broken cross-references" >> $GITHUB_OUTPUT + fi + + echo "::endgroup::" + + # Calculate validation duration + - name: Calculate validation duration + id: validation-duration + if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) + shell: bash + run: | + START_TIME=${{ steps.start-time.outputs.timestamp }} + END_TIME=$(date +%s) + DURATION=$((END_TIME - START_TIME)) + + # Format duration as minutes and seconds + MINS=$((DURATION / 60)) + SECS=$((DURATION % 60)) + if [ $MINS -gt 0 ]; then + DURATION_TEXT="${MINS}m ${SECS}s" + else + DURATION_TEXT="${SECS}s" + fi + + echo "duration=$DURATION_TEXT" >> $GITHUB_OUTPUT + + # Prepare validation results + - name: Prepare validation results + id: validation-results + if: always() && steps.changed-files.outputs.all_changed_files != '' + shell: bash + run: | + echo "::group::Preparing validation results" + + # Initialize results + VALIDATION_COUNT=0 + PASSING_COUNT=0 + SUCCESS_PERCENTAGE=0 + OVERALL_SUCCESS="true" + + # Function to process a validation result + process_validation() { + local name="$1" + local status="$2" + local message="$3" + + VALIDATION_COUNT=$((VALIDATION_COUNT + 1)) + + if [ "$status" == "success" ]; then + PASSING_COUNT=$((PASSING_COUNT + 1)) + echo "✅ $name: $message" + elif [ "$status" == "skipped" ]; then + echo "⏭️ $name: $message" + else + OVERALL_SUCCESS="false" + echo "❌ $name: $message" + fi + } + + # Check MegaLinter results + if [ "${{ steps.config.outputs.lint_markdown }}" == "true" ] || [ "${{ steps.config.outputs.lint_vale }}" == "true" ] || [ "${{ steps.config.outputs.check_links }}" == "true" ]; then + MEGALINTER_STATUS="${{ steps.megalinter.outcome }}" + + if [ "$MEGALINTER_STATUS" == "success" ]; then + process_validation "MegaLinter" "success" "All linting checks passed" + elif [ "$MEGALINTER_STATUS" == "skipped" ]; then + process_validation "MegaLinter" "skipped" "MegaLinter was not run" + else + process_validation "MegaLinter" "error" "Some linting checks failed" + fi + fi + + # Check cross-references results + if [ "${{ steps.config.outputs.check_xrefs }}" == "true" ]; then + XREFS_STATUS="${{ steps.check-cross-refs.outputs.status }}" + XREFS_MESSAGE="${{ steps.check-cross-refs.outputs.message }}" + + if [ "$XREFS_STATUS" == "success" ]; then + process_validation "Cross-references" "success" "$XREFS_MESSAGE" + elif [ "$XREFS_STATUS" == "skipped" ]; then + process_validation "Cross-references" "skipped" "$XREFS_MESSAGE" + else + process_validation "Cross-references" "warning" "$XREFS_MESSAGE" + fi + fi + + # Calculate success percentage + if [ $VALIDATION_COUNT -gt 0 ]; then + SUCCESS_PERCENTAGE=$((PASSING_COUNT * 100 / VALIDATION_COUNT)) + else + SUCCESS_PERCENTAGE=100 # No validations ran + fi + + # Create badge text + if [ "$OVERALL_SUCCESS" == "true" ]; then + BADGE="✅ All validation checks passed" + elif [ $SUCCESS_PERCENTAGE -ge 80 ]; then + BADGE="⚠️ Most validation checks passed" + else + BADGE="❌ Several validation issues were found" + fi + + # Set outputs + echo "validation_count=$VALIDATION_COUNT" >> $GITHUB_OUTPUT + echo "passing_count=$PASSING_COUNT" >> $GITHUB_OUTPUT + echo "success_percentage=$SUCCESS_PERCENTAGE" >> $GITHUB_OUTPUT + echo "overall_success=$OVERALL_SUCCESS" >> $GITHUB_OUTPUT + echo "badge=$BADGE" >> $GITHUB_OUTPUT + + echo "::endgroup::" + + # Prepare comment for PR + - name: Prepare PR comment + id: prepare-comment + if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' + shell: bash + run: | + echo "::group::Preparing PR comment" + + # Build the comment + SANITIZED_BRANCH="${{ steps.context-info.outputs.sanitized_branch }}" + PREVIEW_URL="https://coder.com/docs/@$SANITIZED_BRANCH" + + # Create comment header + if [ "${{ steps.validation-results.outputs.overall_success }}" == "true" ]; then + HEADER="# 📚 Documentation Preview ✅" + STATUS_EMOJI="✅" + else + HEADER="# 📚 Documentation Preview ⚠️" + STATUS_EMOJI="⚠️" + fi + + # Build the full comment + COMMENT="$HEADER + +## 🖥️ [View Documentation Preview]($PREVIEW_URL) + +> $STATUS_EMOJI **Validation Result**: ${{ steps.validation-results.outputs.badge }} + +### Quick Links +- [Main Docs]($PREVIEW_URL) +- [Installation Guide]($PREVIEW_URL/install) +- [Quickstart]($PREVIEW_URL/tutorials/quickstart) + +### 📊 Validation Stats + +- **Changed Files**: ${{ steps.changed-files.outputs.all_changed_files_count }} files checked +- **Validation Success**: ${{ steps.validation-results.outputs.success_percentage }}% (${{ steps.validation-results.outputs.passing_count }}/${{ steps.validation-results.outputs.validation_count }} checks passed) +- **Processing Time**: ${{ steps.validation-duration.outputs.duration }} + +⏱️ Validation completed in ${{ steps.validation-duration.outputs.duration }} | [View Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" + + # Save the comment to output + echo "comment<> $GITHUB_OUTPUT + echo "$COMMENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "::endgroup::" + + # Update the PR comment with results + - name: Update PR comment with results + if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' + uses: marocchino/sticky-pull-request-comment@v2.9.2 + with: + header: docs-preview-comment + number: ${{ inputs.pr-number || github.event.pull_request.number }} + message: ${{ steps.prepare-comment.outputs.comment }} + recreate: true + + # Fail the workflow if specified and there are errors + - name: Check for validation failure + if: steps.config.outputs.fail-on-error == 'true' && steps.validation-results.outputs.overall_success == 'false' + shell: bash + run: | + echo "::error::Documentation validation failed with ${{ steps.validation-results.outputs.badge }}" + exit 1 \ No newline at end of file From 83b45fcf026e41785a2b0da2c393065e6129d87b Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:30:50 -0400 Subject: [PATCH 2/9] fix: correct variable syntax in workflow YAML --- .github/workflows/docs-unified.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index 0b36e13bcf3ef..962f32b8d78fc 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -616,7 +616,7 @@ jobs: ## 🖥️ [View Documentation Preview]($PREVIEW_URL) -> $STATUS_EMOJI **Validation Result**: ${{ steps.validation-results.outputs.badge }} +> ${STATUS_EMOJI} **Validation Result**: ${{ steps.validation-results.outputs.badge }} ### Quick Links - [Main Docs]($PREVIEW_URL) From 414280012605508eea6962bdc71ff31a2e78611f Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:56:04 -0400 Subject: [PATCH 3/9] fix: resolve YAML syntax issues and add Vale configuration - Fixed YAML syntax in docs-unified.yaml workflow - Added Vale configuration with basic style rules - Created template file approach for PR comment to avoid syntax issues - Ensured proper Vale directory structure --- .github/docs/vale/.vale.ini | 16 +++++ .github/docs/vale/styles/Coder/Headings.yml | 14 ++++ .../docs/vale/styles/Coder/RepeatedWords.yml | 7 ++ .../docs/vale/styles/Coder/SentenceLength.yml | 7 ++ .github/docs/vale/styles/Coder/Terms.yml | 13 ++++ .../docs/vale/styles/GitLab/Contractions.yml | 23 ++++++ .github/workflows/docs-unified.yaml | 72 +++++++++++-------- 7 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 .github/docs/vale/.vale.ini create mode 100644 .github/docs/vale/styles/Coder/Headings.yml create mode 100644 .github/docs/vale/styles/Coder/RepeatedWords.yml create mode 100644 .github/docs/vale/styles/Coder/SentenceLength.yml create mode 100644 .github/docs/vale/styles/Coder/Terms.yml create mode 100644 .github/docs/vale/styles/GitLab/Contractions.yml diff --git a/.github/docs/vale/.vale.ini b/.github/docs/vale/.vale.ini new file mode 100644 index 0000000000000..b9fb4fac7a824 --- /dev/null +++ b/.github/docs/vale/.vale.ini @@ -0,0 +1,16 @@ +# Vale configuration for Coder documentation +StylesPath = .github/docs/vale/styles + +# Minimum alert level +MinAlertLevel = suggestion + +# Files to analyze +[*.md] +# Vale styles to use +BasedOnStyles = Coder, GitLab + +# Disable these specific rules +# Coder.Terms = YES/NO + +# Spell checking via Vale +TokenIgnores = (\w+://\S+) # Ignore URLs \ No newline at end of file diff --git a/.github/docs/vale/styles/Coder/Headings.yml b/.github/docs/vale/styles/Coder/Headings.yml new file mode 100644 index 0000000000000..08fccd73dcce4 --- /dev/null +++ b/.github/docs/vale/styles/Coder/Headings.yml @@ -0,0 +1,14 @@ +extends: capitalization +message: "'%s' should use title case" +level: warning +scope: heading +match: $title +style: AP # The Associated Press Style Guide +exceptions: + - Coder + - CLI + - API + - UI + - SSH + - URLs + - IP \ No newline at end of file diff --git a/.github/docs/vale/styles/Coder/RepeatedWords.yml b/.github/docs/vale/styles/Coder/RepeatedWords.yml new file mode 100644 index 0000000000000..d2743b400d7a8 --- /dev/null +++ b/.github/docs/vale/styles/Coder/RepeatedWords.yml @@ -0,0 +1,7 @@ +extends: repetition +message: "'%s' is repeated" +level: error +scope: paragraph +ignorecase: true +tokens: + - '[a-z]+' \ No newline at end of file diff --git a/.github/docs/vale/styles/Coder/SentenceLength.yml b/.github/docs/vale/styles/Coder/SentenceLength.yml new file mode 100644 index 0000000000000..7654d5848fefd --- /dev/null +++ b/.github/docs/vale/styles/Coder/SentenceLength.yml @@ -0,0 +1,7 @@ +extends: metric +message: "Try to keep sentences under 30 words." +level: suggestion +scope: sentence +formula: | + size(tokens) +condition: "> 30" \ No newline at end of file diff --git a/.github/docs/vale/styles/Coder/Terms.yml b/.github/docs/vale/styles/Coder/Terms.yml new file mode 100644 index 0000000000000..4abab4008ba18 --- /dev/null +++ b/.github/docs/vale/styles/Coder/Terms.yml @@ -0,0 +1,13 @@ +extends: substitution +message: "Use '%s' instead of '%s'" +level: error +ignorecase: false +swap: + "(? $TEMP_FILE + EMOJI="✅" else - HEADER="# 📚 Documentation Preview ⚠️" - STATUS_EMOJI="⚠️" + echo "# 📚 Documentation Preview ⚠️" > $TEMP_FILE + EMOJI="⚠️" fi - # Build the full comment - COMMENT="$HEADER - -## 🖥️ [View Documentation Preview]($PREVIEW_URL) - -> ${STATUS_EMOJI} **Validation Result**: ${{ steps.validation-results.outputs.badge }} - -### Quick Links -- [Main Docs]($PREVIEW_URL) -- [Installation Guide]($PREVIEW_URL/install) -- [Quickstart]($PREVIEW_URL/tutorials/quickstart) - -### 📊 Validation Stats - -- **Changed Files**: ${{ steps.changed-files.outputs.all_changed_files_count }} files checked -- **Validation Success**: ${{ steps.validation-results.outputs.success_percentage }}% (${{ steps.validation-results.outputs.passing_count }}/${{ steps.validation-results.outputs.validation_count }} checks passed) -- **Processing Time**: ${{ steps.validation-duration.outputs.duration }} - -⏱️ Validation completed in ${{ steps.validation-duration.outputs.duration }} | [View Workflow Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" - - # Save the comment to output + # Add content line by line + echo "" >> $TEMP_FILE + echo "## 🖥️ [View Documentation Preview]($PREVIEW_URL)" >> $TEMP_FILE + echo "" >> $TEMP_FILE + echo "> $EMOJI **Validation Result**: $BADGE" >> $TEMP_FILE + echo "" >> $TEMP_FILE + echo "### Quick Links" >> $TEMP_FILE + echo "- [Main Docs]($PREVIEW_URL)" >> $TEMP_FILE + echo "- [Installation Guide]($PREVIEW_URL/install)" >> $TEMP_FILE + echo "- [Quickstart]($PREVIEW_URL/tutorials/quickstart)" >> $TEMP_FILE + echo "" >> $TEMP_FILE + echo "### 📊 Validation Stats" >> $TEMP_FILE + echo "" >> $TEMP_FILE + echo "- **Changed Files**: $FILES files checked" >> $TEMP_FILE + echo "- **Validation Success**: $SUCCESS% ($PASSING/$TOTAL checks passed)" >> $TEMP_FILE + echo "- **Processing Time**: $DURATION" >> $TEMP_FILE + echo "" >> $TEMP_FILE + echo "⏱️ Validation completed in $DURATION | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE + + # Export content to GitHub output echo "comment<> $GITHUB_OUTPUT - echo "$COMMENT" >> $GITHUB_OUTPUT + cat $TEMP_FILE >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT + # Cleanup + rm $TEMP_FILE + echo "::endgroup::" # Update the PR comment with results From ae27582f35f4f12bfcff115ff7175c08f45c9178 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 16:57:48 -0400 Subject: [PATCH 4/9] style: improve YAML formatting in docs workflow - Added YAML document start marker - Removed trailing whitespace - Added newline at end of file --- .github/workflows/docs-unified.yaml | 151 ++++++++++++++-------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index b1d41a8e6ad76..866f9ef8facae 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -1,3 +1,4 @@ +--- name: Docs Unified Workflow on: workflow_call: @@ -75,7 +76,7 @@ jobs: permissions: contents: read pull-requests: write # needed for commenting on PRs - + steps: - name: Harden Runner uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 @@ -87,21 +88,21 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 # Fetch all history for proper file comparison - + # Record start time for validation - name: Record start time id: start-time shell: bash run: | echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT - + # Apply configuration presets - name: Apply configuration preset id: config shell: bash run: | echo "::group::Applying configuration settings" - + # Default values (will be overridden by presets or specific inputs) LINT_MARKDOWN="false" CHECK_FORMAT="false" @@ -112,7 +113,7 @@ jobs: POST_COMMENT="false" CREATE_ISSUES="false" FAIL_ON_ERROR="false" - + # Apply presets if specified if [ -n "${{ inputs.preset }}" ]; then case "${{ inputs.preset }}" in @@ -173,62 +174,62 @@ jobs: ;; esac fi - + # Apply explicit overrides if provided if [ "${{ inputs.lint-markdown }}" == "true" ]; then LINT_MARKDOWN="true" elif [ "${{ inputs.lint-markdown }}" == "false" ]; then LINT_MARKDOWN="false" fi - + if [ "${{ inputs.check-format }}" == "true" ]; then CHECK_FORMAT="true" elif [ "${{ inputs.check-format }}" == "false" ]; then CHECK_FORMAT="false" fi - + if [ "${{ inputs.check-links }}" == "true" ]; then CHECK_LINKS="true" elif [ "${{ inputs.check-links }}" == "false" ]; then CHECK_LINKS="false" fi - + if [ "${{ inputs.check-cross-references }}" == "true" ]; then CHECK_XREFS="true" elif [ "${{ inputs.check-cross-references }}" == "false" ]; then CHECK_XREFS="false" fi - + if [ "${{ inputs.lint-vale }}" == "true" ]; then LINT_VALE="true" elif [ "${{ inputs.lint-vale }}" == "false" ]; then LINT_VALE="false" fi - + if [ "${{ inputs.generate-preview }}" == "true" ]; then GEN_PREVIEW="true" elif [ "${{ inputs.generate-preview }}" == "false" ]; then GEN_PREVIEW="false" fi - + if [ "${{ inputs.post-comment }}" == "true" ]; then POST_COMMENT="true" elif [ "${{ inputs.post-comment }}" == "false" ]; then POST_COMMENT="false" fi - + if [ "${{ inputs.create-issues }}" == "true" ]; then CREATE_ISSUES="true" elif [ "${{ inputs.create-issues }}" == "false" ]; then CREATE_ISSUES="false" fi - + if [ "${{ inputs.fail-on-error }}" == "true" ]; then FAIL_ON_ERROR="true" elif [ "${{ inputs.fail-on-error }}" == "false" ]; then FAIL_ON_ERROR="false" fi - + # Store configuration as outputs echo "lint_markdown=$LINT_MARKDOWN" >> $GITHUB_OUTPUT echo "check_format=$CHECK_FORMAT" >> $GITHUB_OUTPUT @@ -239,7 +240,7 @@ jobs: echo "post_comment=$POST_COMMENT" >> $GITHUB_OUTPUT echo "create_issues=$CREATE_ISSUES" >> $GITHUB_OUTPUT echo "fail_on_error=$FAIL_ON_ERROR" >> $GITHUB_OUTPUT - + echo "::endgroup::" # Extract context information for PR/branch @@ -248,7 +249,7 @@ jobs: shell: bash run: | echo "::group::Extracting context information" - + # Extract PR number from inputs or context if [ -n "${{ inputs.pr-number }}" ]; then PR_NUMBER="${{ inputs.pr-number }}" @@ -260,28 +261,28 @@ jobs: echo "::notice::No PR number available. Features requiring PR number will be disabled." PR_NUMBER="" fi - + # Extract branch information (used for preview URLs) if [ "${{ github.event_name }}" == "pull_request" ]; then BRANCH_NAME="${{ github.head_ref }}" else BRANCH_NAME="${{ github.ref_name }}" fi - + # Sanitize branch name for URLs SANITIZED_BRANCH="${BRANCH_NAME//\//-}" - + # Generate preview URL PREVIEW_URL="https://coder.com/docs/@$SANITIZED_BRANCH" - + # Store variables for later steps echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT + echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT echo "sanitized_branch=$SANITIZED_BRANCH" >> $GITHUB_OUTPUT echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT - + echo "::endgroup::" - + # Post initial comment with preview links - name: Post initial preview comment if: inputs.post-comment == 'true' && inputs.generate-preview == 'true' && (inputs.pr-number != '' || github.event.pull_request) @@ -300,7 +301,7 @@ jobs: - [Main Docs](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - + ⏳ Validating documentation... # Get changed files @@ -311,7 +312,7 @@ jobs: files: | docs/**/*.md **/*.md - + # Run MegaLinter (documentation flavor) - name: MegaLinter Documentation id: megalinter @@ -350,29 +351,29 @@ jobs: shell: bash run: | echo "::group::Checking cross-references" - + DOCS_DIR="docs" FOUND_ISSUES=0 BROKEN_REFS=0 - + # Check for broken references to deleted files if [ -n "${{ steps.changed-files.outputs.deleted_files }}" ]; then echo "Checking for references to deleted files..." - + # For each deleted file, check if any remaining files reference it for deleted_file in ${{ steps.changed-files.outputs.deleted_files }}; do # Skip non-markdown files if [[ "$deleted_file" != *.md ]]; then continue fi - + # Extract filename for matching deleted_name=$(basename "$deleted_file") - + # Use grep to find references to this file echo "Checking references to: $deleted_name" REFS_TO_DELETED=$(grep -l -r --include="*.md" "\[$deleted_name\]" "$DOCS_DIR" 2>/dev/null || echo "") - + if [ -n "$REFS_TO_DELETED" ]; then echo "::warning::Found references to deleted file '$deleted_name' in these files:" echo "$REFS_TO_DELETED" | sed 's/^/ - /' @@ -381,26 +382,26 @@ jobs: fi done fi - + # Check for broken cross-references in changed files if [ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]; then echo "Checking for broken internal links in changed files..." - + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do # Skip non-markdown files if [[ "$file" != *.md ]]; then continue fi - + # Extract all internal links with the [text](link) pattern (exclude http/https links) LINKS=$(grep -o -E '\[.+?\]\(\s*[^(http|https|mailto|#)][^)]+\s*\)' "$file" 2>/dev/null || echo "") - + if [ -n "$LINKS" ]; then # For each link, check if the target exists echo "$LINKS" | while read -r link_match; do # Extract just the URL part from [text](url) link_url=$(echo "$link_match" | sed -E 's/\[.+?\]\(\s*([^)]+)\s*\)/\1/') - + # Handle relative links correctly if [[ "$link_url" == /* ]]; then # Absolute path from repo root @@ -410,15 +411,15 @@ jobs: file_dir=$(dirname "$file") link_path="$file_dir/$link_url" fi - + # Add .md extension if missing and it's likely a markdown link if [[ ! "$link_path" == *.* ]]; then link_path="${link_path}.md" fi - + # Clean up the path (remove double slashes, etc.) link_path=$(echo "$link_path" | sed 's@//@/@g' | sed 's@\./@/@g') - + # Check if the link target exists if [ ! -f "$link_path" ]; then echo "::warning::Broken link in $file: $link_match -> $link_path (file not found)" @@ -429,26 +430,26 @@ jobs: fi done fi - + # Check for broken image references if [ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]; then echo "Checking for broken image references..." - + for file in ${{ steps.changed-files.outputs.all_changed_files }}; do # Skip non-markdown files if [[ "$file" != *.md ]]; then continue fi - + # Extract all image references with the ![text](link) pattern (exclude http/https links) IMAGES=$(grep -o -E '!\[.+?\]\(\s*[^(http|https)][^)]+\s*\)' "$file" 2>/dev/null || echo "") - + if [ -n "$IMAGES" ]; then # For each image, check if it exists echo "$IMAGES" | while read -r img_match; do # Extract just the URL part from ![text](url) img_url=$(echo "$img_match" | sed -E 's/!\[.+?\]\(\s*([^)]+)\s*\)/\1/') - + # Handle relative paths correctly if [[ "$img_url" == /* ]]; then # Absolute path from repo root @@ -458,10 +459,10 @@ jobs: file_dir=$(dirname "$file") img_path="$file_dir/$img_url" fi - + # Clean up the path (remove double slashes, etc.) img_path=$(echo "$img_path" | sed 's@//@/@g' | sed 's@\./@/@g') - + # Check if the image exists if [ ! -f "$img_path" ]; then echo "::warning::Broken image in $file: $img_match -> $img_path (file not found)" @@ -472,7 +473,7 @@ jobs: fi done fi - + # Set status based on findings if [ $FOUND_ISSUES -eq 0 ]; then echo "status=success" >> $GITHUB_OUTPUT @@ -481,7 +482,7 @@ jobs: echo "status=warning" >> $GITHUB_OUTPUT echo "message=Found $BROKEN_REFS broken cross-references" >> $GITHUB_OUTPUT fi - + echo "::endgroup::" # Calculate validation duration @@ -493,7 +494,7 @@ jobs: START_TIME=${{ steps.start-time.outputs.timestamp }} END_TIME=$(date +%s) DURATION=$((END_TIME - START_TIME)) - + # Format duration as minutes and seconds MINS=$((DURATION / 60)) SECS=$((DURATION % 60)) @@ -502,9 +503,9 @@ jobs: else DURATION_TEXT="${SECS}s" fi - + echo "duration=$DURATION_TEXT" >> $GITHUB_OUTPUT - + # Prepare validation results - name: Prepare validation results id: validation-results @@ -512,21 +513,21 @@ jobs: shell: bash run: | echo "::group::Preparing validation results" - + # Initialize results VALIDATION_COUNT=0 PASSING_COUNT=0 SUCCESS_PERCENTAGE=0 OVERALL_SUCCESS="true" - + # Function to process a validation result process_validation() { local name="$1" local status="$2" local message="$3" - + VALIDATION_COUNT=$((VALIDATION_COUNT + 1)) - + if [ "$status" == "success" ]; then PASSING_COUNT=$((PASSING_COUNT + 1)) echo "✅ $name: $message" @@ -537,11 +538,11 @@ jobs: echo "❌ $name: $message" fi } - + # Check MegaLinter results if [ "${{ steps.config.outputs.lint_markdown }}" == "true" ] || [ "${{ steps.config.outputs.lint_vale }}" == "true" ] || [ "${{ steps.config.outputs.check_links }}" == "true" ]; then MEGALINTER_STATUS="${{ steps.megalinter.outcome }}" - + if [ "$MEGALINTER_STATUS" == "success" ]; then process_validation "MegaLinter" "success" "All linting checks passed" elif [ "$MEGALINTER_STATUS" == "skipped" ]; then @@ -550,12 +551,12 @@ jobs: process_validation "MegaLinter" "error" "Some linting checks failed" fi fi - + # Check cross-references results if [ "${{ steps.config.outputs.check_xrefs }}" == "true" ]; then XREFS_STATUS="${{ steps.check-cross-refs.outputs.status }}" XREFS_MESSAGE="${{ steps.check-cross-refs.outputs.message }}" - + if [ "$XREFS_STATUS" == "success" ]; then process_validation "Cross-references" "success" "$XREFS_MESSAGE" elif [ "$XREFS_STATUS" == "skipped" ]; then @@ -564,14 +565,14 @@ jobs: process_validation "Cross-references" "warning" "$XREFS_MESSAGE" fi fi - + # Calculate success percentage if [ $VALIDATION_COUNT -gt 0 ]; then SUCCESS_PERCENTAGE=$((PASSING_COUNT * 100 / VALIDATION_COUNT)) else SUCCESS_PERCENTAGE=100 # No validations ran fi - + # Create badge text if [ "$OVERALL_SUCCESS" == "true" ]; then BADGE="✅ All validation checks passed" @@ -580,16 +581,16 @@ jobs: else BADGE="❌ Several validation issues were found" fi - + # Set outputs echo "validation_count=$VALIDATION_COUNT" >> $GITHUB_OUTPUT echo "passing_count=$PASSING_COUNT" >> $GITHUB_OUTPUT echo "success_percentage=$SUCCESS_PERCENTAGE" >> $GITHUB_OUTPUT echo "overall_success=$OVERALL_SUCCESS" >> $GITHUB_OUTPUT echo "badge=$BADGE" >> $GITHUB_OUTPUT - + echo "::endgroup::" - + # Prepare comment for PR using a template file approach - name: Prepare PR comment id: prepare-comment @@ -597,7 +598,7 @@ jobs: shell: bash run: | echo "::group::Preparing PR comment" - + # Variables for template SANITIZED_BRANCH="${{ steps.context-info.outputs.sanitized_branch }}" PREVIEW_URL="https://coder.com/docs/@$SANITIZED_BRANCH" @@ -608,10 +609,10 @@ jobs: TOTAL="${{ steps.validation-results.outputs.validation_count }}" DURATION="${{ steps.validation-duration.outputs.duration }}" WORKFLOW_RUN="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - + # Create a temporary file TEMP_FILE=$(mktemp) - + # Create header based on success if [ "${{ steps.validation-results.outputs.overall_success }}" == "true" ]; then echo "# 📚 Documentation Preview ✅" > $TEMP_FILE @@ -620,7 +621,7 @@ jobs: echo "# 📚 Documentation Preview ⚠️" > $TEMP_FILE EMOJI="⚠️" fi - + # Add content line by line echo "" >> $TEMP_FILE echo "## 🖥️ [View Documentation Preview]($PREVIEW_URL)" >> $TEMP_FILE @@ -639,17 +640,17 @@ jobs: echo "- **Processing Time**: $DURATION" >> $TEMP_FILE echo "" >> $TEMP_FILE echo "⏱️ Validation completed in $DURATION | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE - + # Export content to GitHub output echo "comment<> $GITHUB_OUTPUT cat $TEMP_FILE >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - + # Cleanup rm $TEMP_FILE - + echo "::endgroup::" - + # Update the PR comment with results - name: Update PR comment with results if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' @@ -659,11 +660,11 @@ jobs: number: ${{ inputs.pr-number || github.event.pull_request.number }} message: ${{ steps.prepare-comment.outputs.comment }} recreate: true - + # Fail the workflow if specified and there are errors - name: Check for validation failure if: steps.config.outputs.fail-on-error == 'true' && steps.validation-results.outputs.overall_success == 'false' shell: bash run: | echo "::error::Documentation validation failed with ${{ steps.validation-results.outputs.badge }}" - exit 1 \ No newline at end of file + exit 1 From b2ffe8572070c299ce0aba0fa4bce5155c0ca456 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:05:15 -0400 Subject: [PATCH 5/9] fix: add Node.js setup to docs-unified workflow - Added Node.js setup step in the workflow to resolve errors with pnpm installation - Uses existing ./.github/actions/setup-node action to maintain consistency with other workflows --- .github/workflows/docs-unified.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index 866f9ef8facae..f0e74cc850056 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -89,6 +89,10 @@ jobs: with: fetch-depth: 0 # Fetch all history for proper file comparison + # Setup Node.js environment + - name: Setup Node + uses: ./.github/actions/setup-node + # Record start time for validation - name: Record start time id: start-time From 6b714ec6da3febfe810c198c2bd13d73d9d903bf Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:06:39 -0400 Subject: [PATCH 6/9] fix: secure GitHub Actions workflow to pass actionlint - Follow security best practice for GitHub Actions - Pass potentially untrusted context values via environment variables - Fixes actionlint warning about using github.head_ref directly in scripts --- .github/workflows/docs-unified.yaml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index f0e74cc850056..0019307ff7f39 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -250,16 +250,22 @@ jobs: # Extract context information for PR/branch - name: Extract context information id: context-info + env: + INPUT_PR_NUMBER: ${{ inputs.pr-number }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} shell: bash run: | echo "::group::Extracting context information" # Extract PR number from inputs or context - if [ -n "${{ inputs.pr-number }}" ]; then - PR_NUMBER="${{ inputs.pr-number }}" + if [ -n "$INPUT_PR_NUMBER" ]; then + PR_NUMBER="$INPUT_PR_NUMBER" echo "::notice::Using PR number from action input: #${PR_NUMBER}" - elif [ "${{ github.event_name }}" == "pull_request" ]; then - PR_NUMBER="${{ github.event.pull_request.number }}" + elif [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then + PR_NUMBER="$GITHUB_PR_NUMBER" echo "::notice::Using PR number from event context: #${PR_NUMBER}" else echo "::notice::No PR number available. Features requiring PR number will be disabled." @@ -267,10 +273,10 @@ jobs: fi # Extract branch information (used for preview URLs) - if [ "${{ github.event_name }}" == "pull_request" ]; then - BRANCH_NAME="${{ github.head_ref }}" + if [ "$GITHUB_EVENT_NAME" == "pull_request" ]; then + BRANCH_NAME="$GITHUB_HEAD_REF" else - BRANCH_NAME="${{ github.ref_name }}" + BRANCH_NAME="$GITHUB_REF_NAME" fi # Sanitize branch name for URLs From 6eb9c32acfa964a5c0b0935939f8f944859fdd06 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:16:53 -0400 Subject: [PATCH 7/9] feat: enhance PR comments with timestamps and progress updates - Add timestamps to initial, in-progress, and completion comments - Add a comment update after file detection to show progress - Improve user experience with better status messaging - Allow tracking of workflow progress through multiple stages --- .github/workflows/docs-unified.yaml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index 0019307ff7f39..751e2d3e560f7 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -296,6 +296,8 @@ jobs: # Post initial comment with preview links - name: Post initial preview comment if: inputs.post-comment == 'true' && inputs.generate-preview == 'true' && (inputs.pr-number != '' || github.event.pull_request) + env: + TIMESTAMP: ${{ steps.start-time.outputs.timestamp }} uses: marocchino/sticky-pull-request-comment@v2.9.2 with: header: docs-preview-comment @@ -312,7 +314,8 @@ jobs: - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - ⏳ Validating documentation... + ⏳ Validating documentation... (Started at $(date -u -d @$TIMESTAMP '+%Y-%m-%d %H:%M:%S UTC')) + ⏱️ Status update: Waiting for validation results - last update: $(date -u '+%Y-%m-%d %H:%M:%S UTC') # Get changed files - name: Get changed files @@ -323,6 +326,27 @@ jobs: docs/**/*.md **/*.md + # Update PR comment to show we're starting validation + - name: Update PR comment with in-progress status + if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' + uses: marocchino/sticky-pull-request-comment@v2.9.2 + with: + header: docs-preview-comment + number: ${{ inputs.pr-number || github.event.pull_request.number }} + message: | + # 📚 Documentation Preview ⏳ + + ## 🖥️ [View Documentation Preview](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) + + > ℹ️ **Validation in progress**: Preview links are available now! This comment will update with validation results when complete. + + ### Quick Links + - [Main Docs](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) + - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) + - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) + + ⏳ Found ${{ steps.changed-files.outputs.all_changed_files_count }} markdown files to validate. Starting validation at $(date -u '+%Y-%m-%d %H:%M:%S UTC')... + # Run MegaLinter (documentation flavor) - name: MegaLinter Documentation id: megalinter @@ -649,7 +673,7 @@ jobs: echo "- **Validation Success**: $SUCCESS% ($PASSING/$TOTAL checks passed)" >> $TEMP_FILE echo "- **Processing Time**: $DURATION" >> $TEMP_FILE echo "" >> $TEMP_FILE - echo "⏱️ Validation completed in $DURATION | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE + echo "⏱️ Validation completed in $DURATION | Completed at $(date -u '+%Y-%m-%d %H:%M:%S UTC') | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE # Export content to GitHub output echo "comment<> $GITHUB_OUTPUT From ae660524cf043ac986ec5b49b2018d966d289bdc Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:21:17 -0400 Subject: [PATCH 8/9] fix: use platform-compatible date formatting in workflow - Fix date formatting to be compatible with GitHub Actions runners (Linux) - Use TZ=UTC instead of -u flag for better compatibility - Remove unnecessary timestamp environment variable - Ensure all date commands use the same format --- .github/workflows/docs-unified.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index 751e2d3e560f7..41468128633f5 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -296,8 +296,6 @@ jobs: # Post initial comment with preview links - name: Post initial preview comment if: inputs.post-comment == 'true' && inputs.generate-preview == 'true' && (inputs.pr-number != '' || github.event.pull_request) - env: - TIMESTAMP: ${{ steps.start-time.outputs.timestamp }} uses: marocchino/sticky-pull-request-comment@v2.9.2 with: header: docs-preview-comment @@ -314,8 +312,8 @@ jobs: - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - ⏳ Validating documentation... (Started at $(date -u -d @$TIMESTAMP '+%Y-%m-%d %H:%M:%S UTC')) - ⏱️ Status update: Waiting for validation results - last update: $(date -u '+%Y-%m-%d %H:%M:%S UTC') + ⏳ Validating documentation... (Started at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC')) + ⏱️ Status update: Waiting for validation results - last update: $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC') # Get changed files - name: Get changed files @@ -345,7 +343,7 @@ jobs: - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - ⏳ Found ${{ steps.changed-files.outputs.all_changed_files_count }} markdown files to validate. Starting validation at $(date -u '+%Y-%m-%d %H:%M:%S UTC')... + ⏳ Found ${{ steps.changed-files.outputs.all_changed_files_count }} markdown files to validate. Starting validation at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC')... # Run MegaLinter (documentation flavor) - name: MegaLinter Documentation @@ -673,7 +671,7 @@ jobs: echo "- **Validation Success**: $SUCCESS% ($PASSING/$TOTAL checks passed)" >> $TEMP_FILE echo "- **Processing Time**: $DURATION" >> $TEMP_FILE echo "" >> $TEMP_FILE - echo "⏱️ Validation completed in $DURATION | Completed at $(date -u '+%Y-%m-%d %H:%M:%S UTC') | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE + echo "⏱️ Validation completed in $DURATION | Completed at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC') | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE # Export content to GitHub output echo "comment<> $GITHUB_OUTPUT From 36e6ef015db985aee13eb0dc242902c98dac2fc7 Mon Sep 17 00:00:00 2001 From: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:27:38 -0400 Subject: [PATCH 9/9] feat: simplify PR comment system in docs workflow - Use peter-evans/create-or-update-comment action for PR comments - Replace marocchino/sticky-pull-request-comment with established pattern - Remove complex date formatting and temporary file management - Add find-comment action to track PR comments consistently - Follow existing workflow patterns from PR deploy workflow --- .github/workflows/docs-unified.yaml | 100 +++++++++++++++------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/.github/workflows/docs-unified.yaml b/.github/workflows/docs-unified.yaml index 41468128633f5..a3297cf9f2f38 100644 --- a/.github/workflows/docs-unified.yaml +++ b/.github/workflows/docs-unified.yaml @@ -296,11 +296,10 @@ jobs: # Post initial comment with preview links - name: Post initial preview comment if: inputs.post-comment == 'true' && inputs.generate-preview == 'true' && (inputs.pr-number != '' || github.event.pull_request) - uses: marocchino/sticky-pull-request-comment@v2.9.2 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 with: - header: docs-preview-comment - number: ${{ inputs.pr-number || github.event.pull_request.number }} - message: | + issue-number: ${{ inputs.pr-number || github.event.pull_request.number }} + body: | # 📚 Documentation Preview ⏳ ## 🖥️ [View Documentation Preview](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) @@ -312,8 +311,8 @@ jobs: - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - ⏳ Validating documentation... (Started at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC')) - ⏱️ Status update: Waiting for validation results - last update: $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC') + ⏳ Waiting for validation results... + reactions: eyes # Get changed files - name: Get changed files @@ -327,11 +326,10 @@ jobs: # Update PR comment to show we're starting validation - name: Update PR comment with in-progress status if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' - uses: marocchino/sticky-pull-request-comment@v2.9.2 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 with: - header: docs-preview-comment - number: ${{ inputs.pr-number || github.event.pull_request.number }} - message: | + issue-number: ${{ inputs.pr-number || github.event.pull_request.number }} + body: | # 📚 Documentation Preview ⏳ ## 🖥️ [View Documentation Preview](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}) @@ -343,7 +341,8 @@ jobs: - [Installation Guide](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/install) - [Quickstart](https://coder.com/docs/@${{ steps.context-info.outputs.sanitized_branch }}/tutorials/quickstart) - ⏳ Found ${{ steps.changed-files.outputs.all_changed_files_count }} markdown files to validate. Starting validation at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC')... + ⏳ Found ${{ steps.changed-files.outputs.all_changed_files_count }} markdown files. Validating documentation... + reactions: eyes # Run MegaLinter (documentation flavor) - name: MegaLinter Documentation @@ -623,14 +622,14 @@ jobs: echo "::endgroup::" - # Prepare comment for PR using a template file approach + # Prepare comment for PR - name: Prepare PR comment id: prepare-comment if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' shell: bash run: | echo "::group::Preparing PR comment" - + # Variables for template SANITIZED_BRANCH="${{ steps.context-info.outputs.sanitized_branch }}" PREVIEW_URL="https://coder.com/docs/@$SANITIZED_BRANCH" @@ -641,57 +640,64 @@ jobs: TOTAL="${{ steps.validation-results.outputs.validation_count }}" DURATION="${{ steps.validation-duration.outputs.duration }}" WORKFLOW_RUN="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - - # Create a temporary file - TEMP_FILE=$(mktemp) - + # Create header based on success if [ "${{ steps.validation-results.outputs.overall_success }}" == "true" ]; then - echo "# 📚 Documentation Preview ✅" > $TEMP_FILE + HEADER="# 📚 Documentation Preview ✅" EMOJI="✅" else - echo "# 📚 Documentation Preview ⚠️" > $TEMP_FILE + HEADER="# 📚 Documentation Preview ⚠️" EMOJI="⚠️" fi + + # Create comment content + COMMENT="$HEADER + +## 🖥️ [View Documentation Preview]($PREVIEW_URL) + +> $EMOJI **Validation Result**: $BADGE + +### Quick Links +- [Main Docs]($PREVIEW_URL) +- [Installation Guide]($PREVIEW_URL/install) +- [Quickstart]($PREVIEW_URL/tutorials/quickstart) - # Add content line by line - echo "" >> $TEMP_FILE - echo "## 🖥️ [View Documentation Preview]($PREVIEW_URL)" >> $TEMP_FILE - echo "" >> $TEMP_FILE - echo "> $EMOJI **Validation Result**: $BADGE" >> $TEMP_FILE - echo "" >> $TEMP_FILE - echo "### Quick Links" >> $TEMP_FILE - echo "- [Main Docs]($PREVIEW_URL)" >> $TEMP_FILE - echo "- [Installation Guide]($PREVIEW_URL/install)" >> $TEMP_FILE - echo "- [Quickstart]($PREVIEW_URL/tutorials/quickstart)" >> $TEMP_FILE - echo "" >> $TEMP_FILE - echo "### 📊 Validation Stats" >> $TEMP_FILE - echo "" >> $TEMP_FILE - echo "- **Changed Files**: $FILES files checked" >> $TEMP_FILE - echo "- **Validation Success**: $SUCCESS% ($PASSING/$TOTAL checks passed)" >> $TEMP_FILE - echo "- **Processing Time**: $DURATION" >> $TEMP_FILE - echo "" >> $TEMP_FILE - echo "⏱️ Validation completed in $DURATION | Completed at $(TZ=UTC date '+%Y-%m-%d %H:%M:%S UTC') | [View Workflow Run]($WORKFLOW_RUN)" >> $TEMP_FILE +### 📊 Validation Stats +- **Changed Files**: $FILES files checked +- **Validation Success**: $SUCCESS% ($PASSING/$TOTAL checks passed) +- **Processing Time**: $DURATION + +⏱️ Validation completed in $DURATION | [View Workflow Run]($WORKFLOW_RUN)" + # Export content to GitHub output echo "comment<> $GITHUB_OUTPUT - cat $TEMP_FILE >> $GITHUB_OUTPUT + echo "$COMMENT" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - - # Cleanup - rm $TEMP_FILE - + echo "::endgroup::" + # Find existing comment + - name: Find existing comment + if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 + id: find-comment + with: + issue-number: ${{ inputs.pr-number || github.event.pull_request.number }} + comment-author: "github-actions[bot]" + body-includes: "Documentation Preview" + direction: last + # Update the PR comment with results - name: Update PR comment with results if: inputs.post-comment == 'true' && (inputs.pr-number != '' || github.event.pull_request) && steps.changed-files.outputs.all_changed_files != '' - uses: marocchino/sticky-pull-request-comment@v2.9.2 + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 with: - header: docs-preview-comment - number: ${{ inputs.pr-number || github.event.pull_request.number }} - message: ${{ steps.prepare-comment.outputs.comment }} - recreate: true + comment-id: ${{ steps.find-comment.outputs.comment-id }} + issue-number: ${{ inputs.pr-number || github.event.pull_request.number }} + body: ${{ steps.prepare-comment.outputs.comment }} + edit-mode: replace + reactions: ${{ steps.validation-results.outputs.overall_success == 'true' && 'rocket' || 'eyes' }} # Fail the workflow if specified and there are errors - name: Check for validation failure