Skip to content
Draft
Changes from 8 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
247 changes: 247 additions & 0 deletions ci/cleanup-pr-previews
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
#!/usr/bin/env bash

# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

# A utility script to clean up PR preview documentation folders for closed/merged PRs.
# This script checks all pr-XXXXX folders in the gh-pages branch docs/pr-preview/ directory,
# verifies if the corresponding PR XXXXX is still open, and removes preview folders
# for PRs that have been closed or merged.

set -euo pipefail

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Usage information
usage() {
cat << EOF
PR Preview Cleanup Script - Clean up stale PR preview documentation folders

This script fetches all pr-XXXXX folders from docs/pr-preview/ in the gh-pages branch,
checks PR status via GitHub API, and removes folders for closed/merged/deleted PRs.

USAGE: $0 [OPTIONS]

OPTIONS:
-n, --dry-run Preview what would be deleted without actually deleting
--push Commit and push changes to gh-pages (default: false, requires manual push)
-h, --help Show this help message

EXAMPLES:
$0 -n # Preview what would be cleaned up (RECOMMENDED FIRST)
$0 # Clean up folders locally (no push)
$0 --push # Clean up folders and push to gh-pages branch
$0 --dry-run --push # Invalid combination (dry-run takes precedence)

REQUIREMENTS:
- GH_TOKEN environment variable must be set with appropriate permissions
- 'gh' (GitHub CLI) must be installed and authenticated
- 'jq' must be installed for JSON parsing

SAFETY:
Always run with --dry-run first to verify expected behavior before actual cleanup.
The script will show a summary of what would be removed. Use --push to automatically
commit and push changes, otherwise manual git operations are required.

This script is specifically designed for the NVIDIA/cuda-python repository structure.
EOF
exit 1
}

# Configuration - hardcoded for this specific repository
REPOSITORY="NVIDIA/cuda-python"
DRY_RUN="false"
PUSH_CHANGES="false"

# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-n|--dry-run)
DRY_RUN="true"
shift
;;
--push)
PUSH_CHANGES="true"
shift
;;
-h|--help)
usage
;;
*)
echo -e "${RED}[ERROR]${NC} Unknown option: $1" >&2
echo "Use --help for usage information" >&2
exit 1
;;
esac
done

# Validate required tools and environment
echo -e "${YELLOW}[INFO]${NC} Checking prerequisites..."

if [[ -z "${GH_TOKEN:-}" ]]; then
echo -e "${RED}[ERROR]${NC} GH_TOKEN environment variable is required" >&2
exit 1
fi

if ! command -v jq >/dev/null 2>&1; then
echo -e "${RED}[ERROR]${NC} jq is required but not installed" >&2
exit 1
fi

if ! command -v gh >/dev/null 2>&1; then
echo -e "${RED}[ERROR]${NC} GitHub CLI (gh) is required but not installed" >&2
exit 1
fi

echo -e "${GREEN}[INFO]${NC} All prerequisites satisfied"

# Fetch PR preview folders from gh-pages branch
echo -e "${YELLOW}[INFO]${NC} Fetching PR preview folders from gh-pages branch..."

# Get the list of pr-XXXXX folders from gh-pages branch
PR_FOLDERS=$(gh api repos/"${REPOSITORY}"/contents/docs/pr-preview \
--header "Accept: application/vnd.github+json" \
--jq '.[] | select(.type == "dir" and (.name | test("^pr-[0-9]+$"))) | .name' \
--field ref=gh-pages 2>/dev/null || true)

if [[ -z "$PR_FOLDERS" ]]; then
echo -e "${YELLOW}[INFO]${NC} No PR preview folders found in gh-pages branch"
exit 0
fi

echo -e "${GREEN}[INFO]${NC} Found $(echo "$PR_FOLDERS" | wc -l) PR preview folders"

# Check each PR folder
FOLDERS_TO_REMOVE=()
TOTAL_FOLDERS=0
OPEN_PRS=0

while IFS= read -r folder; do
if [[ -z "$folder" ]]; then
continue
fi

TOTAL_FOLDERS=$((TOTAL_FOLDERS + 1))

# Extract PR number from folder name (pr-XXXXX -> XXXXX)
PR_NUMBER="${folder#pr-}"

echo -e "${YELLOW}[CHECK]${NC} Checking PR #${PR_NUMBER}..."

# Check PR status using GitHub API
PR_STATUS=$(gh api repos/"${REPOSITORY}"/pulls/"${PR_NUMBER}" \
--header "Accept: application/vnd.github+json" \
--jq '.state' 2>/dev/null || echo "not_found")

case "$PR_STATUS" in
"open")
echo -e "${GREEN}[KEEP]${NC} PR #${PR_NUMBER} is still open"
OPEN_PRS=$((OPEN_PRS + 1))
;;
"closed")
echo -e "${RED}[REMOVE]${NC} PR #${PR_NUMBER} is closed"
FOLDERS_TO_REMOVE+=("$folder")
;;
"not_found")
echo -e "${RED}[REMOVE]${NC} PR #${PR_NUMBER} not found (may have been deleted)"
FOLDERS_TO_REMOVE+=("$folder")
;;
*)
echo -e "${YELLOW}[UNKNOWN]${NC} PR #${PR_NUMBER} has unexpected status: ${PR_STATUS}"
;;
esac
done <<< "$PR_FOLDERS"

# Summary
echo ""
echo -e "${YELLOW}[SUMMARY]${NC}"
echo "Total PR preview folders: ${TOTAL_FOLDERS}"
echo "Open PRs: ${OPEN_PRS}"
echo "Folders to remove: ${#FOLDERS_TO_REMOVE[@]}"

if [[ ${#FOLDERS_TO_REMOVE[@]} -eq 0 ]]; then
echo -e "${GREEN}[INFO]${NC} No cleanup needed - all preview folders correspond to open PRs"
exit 0
fi

# List folders to be removed
echo ""
echo -e "${YELLOW}[FOLDERS TO REMOVE]${NC}"
for folder in "${FOLDERS_TO_REMOVE[@]}"; do
pr_num="${folder#pr-}"
echo " - $folder (PR #${pr_num})"
done

# Perform cleanup or show what would be done
echo ""
if [[ "$DRY_RUN" == "true" ]]; then
echo -e "${YELLOW}[DRY RUN]${NC} Would remove ${#FOLDERS_TO_REMOVE[@]} folders (run without --dry-run to actually remove)"
else
echo -e "${RED}[CLEANUP]${NC} Proceeding to remove ${#FOLDERS_TO_REMOVE[@]} folders..."

# Create a git worktree for gh-pages branch
TEMP_DIR=$(mktemp -d)

# Cleanup function to properly remove worktree and temp directory
cleanup_worktree() {
cd - >/dev/null 2>&1 || true # Go back to original directory
if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true
fi
rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true
}
trap cleanup_worktree EXIT
Comment on lines 199 to 213
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cleanup should not be executed if the changes made to the local worktree is not yet pushed, otherwise all local changes are lost after the script ends.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 2a7b4cc. The cleanup function now checks if changes have been pushed before removing the worktree. When --push is not used, the worktree is preserved to prevent loss of unpushed changes, and the script provides instructions for manual cleanup. The cleanup only occurs automatically when changes are successfully pushed or when no changes were made.


echo -e "${YELLOW}[INFO]${NC} Creating git worktree for gh-pages branch..."
if ! git worktree add "$TEMP_DIR" gh-pages >/dev/null 2>&1; then
echo -e "${RED}[ERROR]${NC} Failed to create git worktree for gh-pages branch" >&2
echo "Make sure the gh-pages branch exists and is accessible" >&2
exit 1
fi

cd "$TEMP_DIR"

# Remove each folder
REMOVED_COUNT=0
for folder in "${FOLDERS_TO_REMOVE[@]}"; do
pr_num="${folder#pr-}"
folder_path="docs/pr-preview/$folder"

if [[ -d "$folder_path" ]]; then
echo -e "${YELLOW}[REMOVE]${NC} Removing $folder_path"
rm -rf "$folder_path"
git add "$folder_path"
REMOVED_COUNT=$((REMOVED_COUNT + 1))
else
echo -e "${YELLOW}[SKIP]${NC} Folder $folder_path not found locally"
fi
done

if [[ $REMOVED_COUNT -gt 0 ]]; then
# Commit and push changes
commit_message="Clean up PR preview folders for ${REMOVED_COUNT} closed/merged PRs

Removed preview folders for the following PRs:
$(printf '%s\n' "${FOLDERS_TO_REMOVE[@]}" | sed 's/^pr-/- PR #/' | head -20)
$(if [[ ${#FOLDERS_TO_REMOVE[@]} -gt 20 ]]; then echo "... and $((${#FOLDERS_TO_REMOVE[@]} - 20)) more"; fi)"

echo -e "${YELLOW}[INFO]${NC} Committing changes..."
git commit -m "$commit_message"

if [[ "$PUSH_CHANGES" == "true" ]]; then
echo -e "${YELLOW}[INFO]${NC} Pushing to gh-pages branch..."
git push origin gh-pages
echo -e "${GREEN}[SUCCESS]${NC} Cleanup completed! Removed ${REMOVED_COUNT} PR preview folders and pushed changes"
else
echo -e "${GREEN}[SUCCESS]${NC} Cleanup completed! Removed ${REMOVED_COUNT} PR preview folders"
echo -e "${YELLOW}[INFO]${NC} Changes have been committed locally but not pushed. Use 'git push origin gh-pages' to push manually."
fi
else
echo -e "${YELLOW}[INFO]${NC} No folders were actually removed (they may have been cleaned up already)"
fi
fi