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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions .github/workflows/check-redirects-on-rename.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
name: Check Redirects on File Rename

on:
pull_request:
branches: [master]

jobs:
check-redirects:
name: Check redirects for renamed files
runs-on: ubuntu-latest
continue-on-error: true # Fail the check but don't block merge
permissions:
contents: read
pull-requests: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Set up git for diff
run: |
git fetch origin ${{ github.event.pull_request.base.ref }}:${{ github.event.pull_request.base.ref }}
echo "GITHUB_BASE_REF=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV
echo "GITHUB_BASE_SHA=$(git rev-parse origin/${{ github.event.pull_request.base.ref }})" >> $GITHUB_ENV
echo "GITHUB_SHA=${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV

- name: Run redirect validation
id: validate
continue-on-error: true
run: |
set +e
OUTPUT=$(bun scripts/check-redirects-on-rename.ts 2>&1)
EXIT_CODE=$?
set -e

echo "$OUTPUT"

# Extract JSON output if present
HAS_JSON=false
if echo "$OUTPUT" | grep -Fq -- "---JSON_OUTPUT---"; then
JSON_OUTPUT=$(echo "$OUTPUT" | sed -n '/---JSON_OUTPUT---/,/---JSON_OUTPUT---/p' | sed '1d;$d')
echo "validation_result<<EOF" >> $GITHUB_OUTPUT
echo "$JSON_OUTPUT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
HAS_JSON=true
fi

echo "has_results=$HAS_JSON" >> $GITHUB_OUTPUT
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT

- name: Post comment if redirects are missing
if: steps.validate.outputs.exit_code == '1' && steps.validate.outputs.has_results == 'true'
uses: actions/github-script@v7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
// Use toJSON() to safely escape the JSON string for JavaScript interpolation
// toJSON() will JSON-encode the string, so we need to parse it once to get the original JSON string,
// then parse again to get the actual object
const validationResultJsonString = ${{ toJSON(steps.validate.outputs.validation_result) }};
let validationResult;
try {
// First parse: convert from JSON-encoded string to original JSON string
const jsonString = JSON.parse(validationResultJsonString);
// Second parse: convert from JSON string to object
validationResult = JSON.parse(jsonString);
} catch (e) {
console.error('Failed to parse validation result:', e);
return;
}

const missingRedirects = validationResult.missingRedirects || [];

if (missingRedirects.length === 0) {
return;
}

// Group by redirects array type
const devDocsRedirects = missingRedirects.filter(mr => mr.isDeveloperDocs);
const userDocsRedirects = missingRedirects.filter(mr => !mr.isDeveloperDocs);

let comment = '## ⚠️ Missing Redirects Detected\n\n';
comment += 'This PR renames or moves MDX files, but some redirects may be missing from `redirects.js`.\n\n';
comment += 'Please add the following redirects to ensure old URLs continue to work:\n\n';

if (userDocsRedirects.length > 0) {
comment += '### User Docs Redirects (userDocsRedirects array)\n\n';
comment += '```javascript\n';
userDocsRedirects.forEach(mr => {
comment += ` {\n`;
comment += ` source: '${mr.oldUrl}',\n`;
comment += ` destination: '${mr.newUrl}',\n`;
comment += ` },\n`;
});
comment += '```\n\n';
}

if (devDocsRedirects.length > 0) {
comment += '### Developer Docs Redirects (developerDocsRedirects array)\n\n';
comment += '```javascript\n';
devDocsRedirects.forEach(mr => {
comment += ` {\n`;
comment += ` source: '${mr.oldUrl}',\n`;
comment += ` destination: '${mr.newUrl}',\n`;
comment += ` },\n`;
});
comment += '```\n\n';
}

comment += '---\n';
comment += '_Note: This check will fail until redirects are added. Adding redirects ensures old links continue to work._\n';

// Check for existing comments from this action
const {data: comments} = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const existingComment = comments.find(comment =>
comment.user.type === 'Bot' &&
(comment.user.login === 'github-actions[bot]' || comment.user.login.includes('bot')) &&
comment.body.includes('Missing Redirects Detected')
);

if (existingComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existingComment.id,
body: comment,
});
console.log(`Updated existing comment ${existingComment.id}`);
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment,
});
console.log('Created new comment');
}

- name: Report failure if redirects are missing
if: steps.validate.outputs.exit_code == '1' && steps.validate.outputs.has_results == 'true'
run: |
echo "::warning::Missing redirects detected. Please add the redirects shown in the PR comment above."
echo "::warning::This check will show as failed, but will not block merging. However, adding redirects is recommended."
exit 1

- name: Success - no redirects needed
if: steps.validate.outputs.exit_code == '0'
run: |
echo "✅ No file renames detected, or all renames have corresponding redirects."
2 changes: 1 addition & 1 deletion redirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -1269,4 +1269,4 @@ const redirects = async () => {
});
};

module.exports = {redirects};
module.exports = {redirects, developerDocsRedirects, userDocsRedirects};
Loading
Loading