Skip to content
Closed
Next Next commit
Add simplified docs preview GitHub action
Creates a simplified docs preview action that comments on PRs with Vercel preview links.
The action shows changed files and highlights newly added documentation from manifest.json.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
  • Loading branch information
EdwardAngert and claude committed Apr 9, 2025
commit d0fe1f8893a3bf97519c4e8ddcdf20dcffc4cb22
164 changes: 164 additions & 0 deletions .github/actions/docs-preview/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
name: 'Docs Preview Action'
description: 'A composite action to provide Vercel preview links for documentation changes'
author: 'Coder'
inputs:
github-token:
description: 'GitHub token for API operations'
required: true
docs-dir:
description: 'Path to the docs directory'
required: false
default: 'docs'
vercel-domain:
description: 'Vercel deployment domain'
required: false
default: 'coder-docs-git'
changed-files:
description: 'JSON string of changed files (from tj-actions/changed-files)'
required: true
manifest-changed:
description: 'Boolean indicating if manifest.json has changed (from tj-actions/changed-files)'
required: true

outputs:
has_changes:
description: 'Boolean indicating if documentation files have changed'
value: ${{ steps.docs-analysis.outputs.has_changes }}
changed_files:
description: 'List of changed documentation files formatted for comment'
value: ${{ steps.docs-analysis.outputs.changed_files }}
url:
description: 'Vercel preview URL'
value: ${{ steps.vercel-preview.outputs.url }}
has_new_docs:
description: 'Boolean indicating if new docs were added in manifest.json'
value: ${{ steps.manifest-analysis.outputs.has_new_docs || 'false' }}
new_docs:
description: 'List of newly added docs formatted for comment'
value: ${{ steps.manifest-analysis.outputs.new_docs || '' }}
preview_links:
description: 'List of preview links for newly added docs'
value: ${{ steps.manifest-analysis.outputs.preview_links || '' }}

runs:
using: 'composite'
steps:
- name: Set security environment
shell: bash
run: |
# Secure the environment by clearing potentially harmful variables
unset HISTFILE
umask 077

# Validate that docs directory exists
if [ ! -d "${{ inputs.docs-dir }}" ]; then
echo "::error::Docs directory '${{ inputs.docs-dir }}' does not exist"
exit 1
fi

- name: Analyze docs changes
id: docs-analysis
shell: bash
run: |
# Parse changed files from input and write to temp file with strict permissions
echo '${{ inputs.changed-files }}' > changed_files.json

# Count total changed doc files
DOC_FILES_COUNT=$(jq -r '.[] | select(startswith("${{ inputs.docs-dir }}/"))' changed_files.json | wc -l)
echo "doc_files_count=$DOC_FILES_COUNT" >> $GITHUB_OUTPUT

# Format changed files for comment
FORMATTED_FILES=$(jq -r '.[] | select(startswith("${{ inputs.docs-dir }}/")) | "- `" + . + "`"' changed_files.json)
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
echo "$FORMATTED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

# Determine if docs have changed
if [ "$DOC_FILES_COUNT" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi

# Clean up sensitive file
rm -f changed_files.json

- name: Generate Vercel preview URL
id: vercel-preview
if: steps.docs-analysis.outputs.has_changes == 'true'
shell: bash
run: |
# Get PR number for Vercel preview URL using GitHub event file
PR_NUMBER=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH")

# Input validation - ensure PR number is a number
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid PR number: $PR_NUMBER"
exit 1
fi

# Generate and output Vercel preview URL with sanitized inputs
VERCEL_DOMAIN="${{ inputs.vercel-domain }}"
# Remove any dangerous characters from domain
VERCEL_DOMAIN=$(echo "$VERCEL_DOMAIN" | tr -cd 'a-zA-Z0-9-.')

VERCEL_PREVIEW_URL="https://${VERCEL_DOMAIN}-${PR_NUMBER}.vercel.app"
echo "url=$VERCEL_PREVIEW_URL" >> $GITHUB_OUTPUT

- name: Analyze manifest changes
id: manifest-analysis
if: inputs.manifest-changed == 'true'
shell: bash
run: |
# Get PR number for links
PR_NUMBER=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH")

# Get the base SHA for diff
BASE_SHA=$(git merge-base HEAD origin/main)

# Extract new docs from manifest.json diff with safe patterns
NEW_DOCS=$(git diff "$BASE_SHA"..HEAD -- "${{ inputs.docs-dir }}/manifest.json" | grep -E '^\+.*"path":' | sed -E 's/.*"path": *"(.*)".*/\1/g')

if [ -n "$NEW_DOCS" ]; then
echo "has_new_docs=true" >> $GITHUB_OUTPUT

# Format new docs for comment
FORMATTED_NEW_DOCS=$(echo "$NEW_DOCS" | sort | uniq | grep -v "^$" | sed 's/^/- `/g' | sed 's/$/`/g')
echo "new_docs<<EOF" >> $GITHUB_OUTPUT
echo "$FORMATTED_NEW_DOCS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

# Generate preview links for new docs
PREVIEW_LINKS=""
while IFS= read -r doc_path; do
# Skip empty lines
[ -z "$doc_path" ] && continue

# Clean the path and sanitize
clean_path=${doc_path#./}
clean_path=$(echo "$clean_path" | tr -cd 'a-zA-Z0-9_./-')

# Generate preview URL
url_path=$(echo "$clean_path" | sed 's/\.md$//')
VERCEL_DOMAIN="${{ inputs.vercel-domain }}"
VERCEL_DOMAIN=$(echo "$VERCEL_DOMAIN" | tr -cd 'a-zA-Z0-9-.')
preview_url="https://${VERCEL_DOMAIN}-${PR_NUMBER}.vercel.app/${url_path}"

# Extract doc title or use filename safely
if [ -f "$doc_path" ]; then
title=$(grep -m 1 "^# " "$doc_path" | sed 's/^# //')
title=$(echo "$title" | tr -cd 'a-zA-Z0-9 _.,-')
[ -z "$title" ] && title=$(basename "$doc_path" .md | tr -cd 'a-zA-Z0-9_.-')
else
title=$(basename "$doc_path" .md | tr -cd 'a-zA-Z0-9_.-')
fi

PREVIEW_LINKS="${PREVIEW_LINKS}- [$title]($preview_url)\n"
done <<< "$NEW_DOCS"

echo "preview_links<<EOF" >> $GITHUB_OUTPUT
echo -e "$PREVIEW_LINKS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "has_new_docs=false" >> $GITHUB_OUTPUT
fi
80 changes: 80 additions & 0 deletions .github/workflows/docs-preview.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Docs Preview
on:
pull_request:
types: [opened, synchronize, reopened]
paths:
- 'docs/**'

permissions:
contents: read
pull-requests: write

jobs:
preview:
name: Generate docs preview
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
with:
egress-policy: audit

- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@2a398787e7b39c6ca11ce0843e5956d0b4165c80 # v43.0.0
with:
files: |
docs/**

- name: Check if manifest changed
id: manifest-check
run: |
echo "changed=${{ contains(steps.changed-files.outputs.all_changed_files, 'docs/manifest.json') }}" >> $GITHUB_OUTPUT

- name: Generate docs preview
id: docs-preview
uses: ./.github/actions/docs-preview
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
changed-files: ${{ steps.changed-files.outputs.all_changed_files_json }}
manifest-changed: ${{ steps.manifest-check.outputs.changed }}

- name: Find existing comment
if: steps.docs-preview.outputs.has_changes == 'true'
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: '## 📚 Docs Preview'

- name: Create or update preview comment
if: steps.docs-preview.outputs.has_changes == 'true'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## 📚 Docs Preview

Your documentation changes are available for preview at:
**🔗 [Vercel Preview](${{ steps.docs-preview.outputs.url }})**

### Changed Documentation Files
${{ steps.docs-preview.outputs.changed_files }}

${{ steps.docs-preview.outputs.has_new_docs == 'true' && '### Newly Added Documentation' || '' }}
${{ steps.docs-preview.outputs.has_new_docs == 'true' && steps.docs-preview.outputs.new_docs || '' }}

${{ steps.docs-preview.outputs.has_new_docs == 'true' && '### Preview Links for New Docs' || '' }}
${{ steps.docs-preview.outputs.has_new_docs == 'true' && steps.docs-preview.outputs.preview_links || '' }}

---
<sub>🤖 This comment is automatically generated and updated when documentation changes.</sub>
edit-mode: replace
reactions: eyes
Loading