From e4ec679f3867e025ec96ea9bdf73ad9bf359061b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:21:05 +0000 Subject: [PATCH 01/12] Initial plan From ec5bd7ab5c13de390e1e5cf647ae8d1af8fa17c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:25:37 +0000 Subject: [PATCH 02/12] Add PR preview cleanup script with comprehensive error handling and documentation Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/README-cleanup-pr-previews.md | 106 ++++++++++++++++ ci/cleanup-pr-previews | 206 +++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) create mode 100644 ci/README-cleanup-pr-previews.md create mode 100755 ci/cleanup-pr-previews diff --git a/ci/README-cleanup-pr-previews.md b/ci/README-cleanup-pr-previews.md new file mode 100644 index 000000000..3b294b4af --- /dev/null +++ b/ci/README-cleanup-pr-previews.md @@ -0,0 +1,106 @@ +# PR Preview Cleanup Script + +## Overview + +This script (`cleanup-pr-previews`) helps maintain the `gh-pages` branch by cleaning up documentation preview folders for PRs that have been closed or merged. + +## Problem + +The current `doc_preview` action has some limitations that can result in stale preview folders: + +1. Cleanup steps only run when the target branch is `main` (so PRs targeting feature branches don't get cleaned up) +2. Canceled/interrupted documentation jobs don't run cleanup steps +3. Various other edge cases where the cleanup logic isn't executed + +This results in a mismatch between the number of `pr-XXXXX` folders in `docs/pr-preview/` and the actual number of open PRs. + +## Solution + +The `cleanup-pr-previews` script: + +1. Fetches all `pr-XXXXX` folders from the `docs/pr-preview/` directory in the `gh-pages` branch +2. For each folder, extracts the PR number and checks its status via GitHub API +3. Identifies folders corresponding to closed/merged PRs or deleted PRs +4. Removes the stale folders and commits the changes back to `gh-pages` + +## Usage + +### Prerequisites + +- `GH_TOKEN` environment variable with appropriate permissions +- GitHub CLI (`gh`) installed and authenticated +- `jq` installed for JSON parsing +- `git` available + +### Basic Usage + +```bash +# Preview what would be cleaned up (recommended first run) +./ci/cleanup-pr-previews NVIDIA/cuda-python true + +# Actually perform the cleanup +./ci/cleanup-pr-previews NVIDIA/cuda-python false + +# Use defaults (NVIDIA/cuda-python, actual cleanup) +./ci/cleanup-pr-previews +``` + +### Parameters + +1. **repository** (optional): GitHub repository in `owner/repo` format. Default: `NVIDIA/cuda-python` +2. **dry-run** (optional): Set to `true` to preview changes without making them. Default: `false` + +### Examples + +```bash +# Preview cleanup for the main repository +./ci/cleanup-pr-previews NVIDIA/cuda-python true + +# Clean up a different repository +./ci/cleanup-pr-previews myorg/my-repo false + +# Show help +./ci/cleanup-pr-previews --help +``` + +## Sample Output + +``` +[INFO] Checking prerequisites... +[INFO] All prerequisites satisfied +[INFO] Fetching PR preview folders from gh-pages branch... +[INFO] Found 44 PR preview folders +[CHECK] Checking PR #415... +[REMOVE] PR #415 is closed +[CHECK] Checking PR #1021... +[KEEP] PR #1021 is still open +... + +[SUMMARY] +Total PR preview folders: 44 +Open PRs: 17 +Folders to remove: 27 + +[FOLDERS TO REMOVE] + - pr-415 (PR #415) + - pr-435 (PR #435) + ... + +[CLEANUP] Proceeding to remove 27 folders... +[INFO] Cloning gh-pages branch to temporary directory... +[REMOVE] Removing docs/pr-preview/pr-415 +... +[INFO] Committing changes... +[INFO] Pushing to gh-pages branch... +[SUCCESS] Cleanup completed! Removed 27 PR preview folders +``` + +## Security Considerations + +- The script requires write access to the repository to modify the `gh-pages` branch +- Always run with `dry-run=true` first to verify the expected behavior +- The script clones the repository to a temporary directory which is automatically cleaned up + +## Future Enhancements + +This script could be integrated into a scheduled GitHub Actions workflow to run periodically (e.g., weekly) to automatically maintain the `gh-pages` branch. \ No newline at end of file diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews new file mode 100755 index 000000000..75801866c --- /dev/null +++ b/ci/cleanup-pr-previews @@ -0,0 +1,206 @@ +#!/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 + +# Configuration +REPOSITORY="${1:-NVIDIA/cuda-python}" +DRY_RUN="${2:-false}" + +# 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 +Usage: $0 [repository] [dry-run] + repository: GitHub repository (default: NVIDIA/cuda-python) + dry-run: Set to 'true' to preview what would be deleted without actually deleting (default: false) + +Examples: + $0 # Clean up NVIDIA/cuda-python with actual deletions + $0 NVIDIA/cuda-python true # Preview what would be cleaned up + $0 myorg/my-repo false # Clean up a different repository + +Requirements: + - GH_TOKEN environment variable must be set + - 'gh' (GitHub CLI) must be installed and authenticated + - 'jq' must be installed for JSON parsing + - 'git' must be available +EOF + exit 1 +} + +# Check for help flag +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage +fi + +# 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 + +if ! command -v git >/dev/null 2>&1; then + echo -e "${RED}[ERROR]${NC} git 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=$(echo "$folder" | sed 's/^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=$(echo "$folder" | sed 's/^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 (use dry-run=false to actually remove)" +else + echo -e "${RED}[CLEANUP]${NC} Proceeding to remove ${#FOLDERS_TO_REMOVE[@]} folders..." + + # Clone gh-pages branch to a temporary directory + TEMP_DIR=$(mktemp -d) + trap 'rm -rf "$TEMP_DIR"' EXIT + + echo -e "${YELLOW}[INFO]${NC} Cloning gh-pages branch to temporary directory..." + git clone --depth 1 --branch gh-pages "https://github.com/${REPOSITORY}.git" "$TEMP_DIR" >/dev/null 2>&1 + + cd "$TEMP_DIR" + + # Configure git for the cleanup commit + git config user.name "cuda-python-bot" + git config user.email "cuda-python-bot@users.noreply.github.com" + + # Remove each folder + REMOVED_COUNT=0 + for folder in "${FOLDERS_TO_REMOVE[@]}"; do + pr_num=$(echo "$folder" | sed 's/^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" + + 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" + else + echo -e "${YELLOW}[INFO]${NC} No folders were actually removed (they may have been cleaned up already)" + fi +fi \ No newline at end of file From 0797a07ddb0c90876d51b8240491dcd71afbb47c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:29:27 +0000 Subject: [PATCH 03/12] Apply shellcheck improvements to cleanup-pr-previews script Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/cleanup-pr-previews | 6 ++-- ci/test-cleanup-pr-previews | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100755 ci/test-cleanup-pr-previews diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 75801866c..fff588ba9 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -101,7 +101,7 @@ while IFS= read -r folder; do TOTAL_FOLDERS=$((TOTAL_FOLDERS + 1)) # Extract PR number from folder name (pr-XXXXX -> XXXXX) - PR_NUMBER=$(echo "$folder" | sed 's/^pr-//') + PR_NUMBER="${folder#pr-}" echo -e "${YELLOW}[CHECK]${NC} Checking PR #${PR_NUMBER}..." @@ -145,7 +145,7 @@ fi echo "" echo -e "${YELLOW}[FOLDERS TO REMOVE]${NC}" for folder in "${FOLDERS_TO_REMOVE[@]}"; do - pr_num=$(echo "$folder" | sed 's/^pr-//') + pr_num="${folder#pr-}" echo " - $folder (PR #${pr_num})" done @@ -172,7 +172,7 @@ else # Remove each folder REMOVED_COUNT=0 for folder in "${FOLDERS_TO_REMOVE[@]}"; do - pr_num=$(echo "$folder" | sed 's/^pr-//') + pr_num="${folder#pr-}" folder_path="docs/pr-preview/$folder" if [[ -d "$folder_path" ]]; then diff --git a/ci/test-cleanup-pr-previews b/ci/test-cleanup-pr-previews new file mode 100755 index 000000000..ba2201831 --- /dev/null +++ b/ci/test-cleanup-pr-previews @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Simple test script for cleanup-pr-previews +# This tests the basic argument parsing and help functionality + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CLEANUP_SCRIPT="${SCRIPT_DIR}/cleanup-pr-previews" + +echo "Testing PR preview cleanup script..." + +# Test 1: Help functionality +echo "Test 1: Help functionality" +help_output=$($CLEANUP_SCRIPT --help 2>&1 || true) +if echo "$help_output" | grep -q "Usage:"; then + echo "✓ Help functionality works" +else + echo "✗ Help functionality failed" + exit 1 +fi + +# Test 2: Script is executable +echo "Test 2: Script executability" +if [[ -x "$CLEANUP_SCRIPT" ]]; then + echo "✓ Script is executable" +else + echo "✗ Script is not executable" + exit 1 +fi + +# Test 3: Script handles missing GH_TOKEN +echo "Test 3: Missing GH_TOKEN handling" +error_output=$($CLEANUP_SCRIPT 2>&1 || true) +if echo "$error_output" | grep -q "GH_TOKEN environment variable is required"; then + echo "✓ Missing GH_TOKEN handled correctly" +else + echo "✗ Missing GH_TOKEN not handled correctly" + exit 1 +fi + +# Test 4: Check shebang and basic shell syntax +echo "Test 4: Shell syntax validation" +if bash -n "$CLEANUP_SCRIPT"; then + echo "✓ Shell syntax is valid" +else + echo "✗ Shell syntax errors detected" + exit 1 +fi + +echo "" +echo "All tests passed! ✓" +echo "" +echo "To actually test the script functionality, run:" +echo " export GH_TOKEN=your_token" +echo " $CLEANUP_SCRIPT NVIDIA/cuda-python true" \ No newline at end of file From 326272746a7b2ce88c624771b9a9d9edf3b6cdc3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:50:32 +0000 Subject: [PATCH 04/12] Changes before error encountered Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/README-cleanup-pr-previews.md | 106 ------------------------------- ci/cleanup-pr-previews | 26 ++++++-- ci/test-cleanup-pr-previews | 56 ---------------- 3 files changed, 21 insertions(+), 167 deletions(-) delete mode 100644 ci/README-cleanup-pr-previews.md delete mode 100755 ci/test-cleanup-pr-previews diff --git a/ci/README-cleanup-pr-previews.md b/ci/README-cleanup-pr-previews.md deleted file mode 100644 index 3b294b4af..000000000 --- a/ci/README-cleanup-pr-previews.md +++ /dev/null @@ -1,106 +0,0 @@ -# PR Preview Cleanup Script - -## Overview - -This script (`cleanup-pr-previews`) helps maintain the `gh-pages` branch by cleaning up documentation preview folders for PRs that have been closed or merged. - -## Problem - -The current `doc_preview` action has some limitations that can result in stale preview folders: - -1. Cleanup steps only run when the target branch is `main` (so PRs targeting feature branches don't get cleaned up) -2. Canceled/interrupted documentation jobs don't run cleanup steps -3. Various other edge cases where the cleanup logic isn't executed - -This results in a mismatch between the number of `pr-XXXXX` folders in `docs/pr-preview/` and the actual number of open PRs. - -## Solution - -The `cleanup-pr-previews` script: - -1. Fetches all `pr-XXXXX` folders from the `docs/pr-preview/` directory in the `gh-pages` branch -2. For each folder, extracts the PR number and checks its status via GitHub API -3. Identifies folders corresponding to closed/merged PRs or deleted PRs -4. Removes the stale folders and commits the changes back to `gh-pages` - -## Usage - -### Prerequisites - -- `GH_TOKEN` environment variable with appropriate permissions -- GitHub CLI (`gh`) installed and authenticated -- `jq` installed for JSON parsing -- `git` available - -### Basic Usage - -```bash -# Preview what would be cleaned up (recommended first run) -./ci/cleanup-pr-previews NVIDIA/cuda-python true - -# Actually perform the cleanup -./ci/cleanup-pr-previews NVIDIA/cuda-python false - -# Use defaults (NVIDIA/cuda-python, actual cleanup) -./ci/cleanup-pr-previews -``` - -### Parameters - -1. **repository** (optional): GitHub repository in `owner/repo` format. Default: `NVIDIA/cuda-python` -2. **dry-run** (optional): Set to `true` to preview changes without making them. Default: `false` - -### Examples - -```bash -# Preview cleanup for the main repository -./ci/cleanup-pr-previews NVIDIA/cuda-python true - -# Clean up a different repository -./ci/cleanup-pr-previews myorg/my-repo false - -# Show help -./ci/cleanup-pr-previews --help -``` - -## Sample Output - -``` -[INFO] Checking prerequisites... -[INFO] All prerequisites satisfied -[INFO] Fetching PR preview folders from gh-pages branch... -[INFO] Found 44 PR preview folders -[CHECK] Checking PR #415... -[REMOVE] PR #415 is closed -[CHECK] Checking PR #1021... -[KEEP] PR #1021 is still open -... - -[SUMMARY] -Total PR preview folders: 44 -Open PRs: 17 -Folders to remove: 27 - -[FOLDERS TO REMOVE] - - pr-415 (PR #415) - - pr-435 (PR #435) - ... - -[CLEANUP] Proceeding to remove 27 folders... -[INFO] Cloning gh-pages branch to temporary directory... -[REMOVE] Removing docs/pr-preview/pr-415 -... -[INFO] Committing changes... -[INFO] Pushing to gh-pages branch... -[SUCCESS] Cleanup completed! Removed 27 PR preview folders -``` - -## Security Considerations - -- The script requires write access to the repository to modify the `gh-pages` branch -- Always run with `dry-run=true` first to verify the expected behavior -- The script clones the repository to a temporary directory which is automatically cleaned up - -## Future Enhancements - -This script could be integrated into a scheduled GitHub Actions workflow to run periodically (e.g., weekly) to automatically maintain the `gh-pages` branch. \ No newline at end of file diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index fff588ba9..04260d916 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -24,20 +24,36 @@ NC='\033[0m' # No Color # Usage information usage() { cat << EOF -Usage: $0 [repository] [dry-run] +PR Preview Cleanup Script - Clean up stale PR preview documentation folders + +PROBLEM: +The doc_preview action has limitations that result in stale preview folders: +- Cleanup steps only run when target branch is 'main' (PRs targeting feature branches aren't cleaned up) +- Canceled/interrupted documentation jobs don't run cleanup steps +- Other edge cases where cleanup logic isn't executed + +SOLUTION: +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 [repository] [dry-run] repository: GitHub repository (default: NVIDIA/cuda-python) dry-run: Set to 'true' to preview what would be deleted without actually deleting (default: false) -Examples: +EXAMPLES: $0 # Clean up NVIDIA/cuda-python with actual deletions - $0 NVIDIA/cuda-python true # Preview what would be cleaned up + $0 NVIDIA/cuda-python true # Preview what would be cleaned up (RECOMMENDED FIRST) $0 myorg/my-repo false # Clean up a different repository -Requirements: - - GH_TOKEN environment variable must be set +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 - 'git' must be available + +SAFETY: +Always run with dry-run=true first to verify expected behavior before actual cleanup. +The script will show a summary of what would be removed and requires confirmation. EOF exit 1 } diff --git a/ci/test-cleanup-pr-previews b/ci/test-cleanup-pr-previews deleted file mode 100755 index ba2201831..000000000 --- a/ci/test-cleanup-pr-previews +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -# Simple test script for cleanup-pr-previews -# This tests the basic argument parsing and help functionality - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -CLEANUP_SCRIPT="${SCRIPT_DIR}/cleanup-pr-previews" - -echo "Testing PR preview cleanup script..." - -# Test 1: Help functionality -echo "Test 1: Help functionality" -help_output=$($CLEANUP_SCRIPT --help 2>&1 || true) -if echo "$help_output" | grep -q "Usage:"; then - echo "✓ Help functionality works" -else - echo "✗ Help functionality failed" - exit 1 -fi - -# Test 2: Script is executable -echo "Test 2: Script executability" -if [[ -x "$CLEANUP_SCRIPT" ]]; then - echo "✓ Script is executable" -else - echo "✗ Script is not executable" - exit 1 -fi - -# Test 3: Script handles missing GH_TOKEN -echo "Test 3: Missing GH_TOKEN handling" -error_output=$($CLEANUP_SCRIPT 2>&1 || true) -if echo "$error_output" | grep -q "GH_TOKEN environment variable is required"; then - echo "✓ Missing GH_TOKEN handled correctly" -else - echo "✗ Missing GH_TOKEN not handled correctly" - exit 1 -fi - -# Test 4: Check shebang and basic shell syntax -echo "Test 4: Shell syntax validation" -if bash -n "$CLEANUP_SCRIPT"; then - echo "✓ Shell syntax is valid" -else - echo "✗ Shell syntax errors detected" - exit 1 -fi - -echo "" -echo "All tests passed! ✓" -echo "" -echo "To actually test the script functionality, run:" -echo " export GH_TOKEN=your_token" -echo " $CLEANUP_SCRIPT NVIDIA/cuda-python true" \ No newline at end of file From 2d77199883fd78e38b501220e5c90791b79397ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 04:23:15 +0000 Subject: [PATCH 05/12] Refactor cleanup-pr-previews script based on feedback Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/cleanup-pr-previews | 85 +++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 04260d916..e70a38f84 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -11,10 +11,6 @@ set -euo pipefail -# Configuration -REPOSITORY="${1:-NVIDIA/cuda-python}" -DRY_RUN="${2:-false}" - # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' @@ -36,14 +32,18 @@ SOLUTION: 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 [repository] [dry-run] - repository: GitHub repository (default: NVIDIA/cuda-python) - dry-run: Set to 'true' to preview what would be deleted without actually deleting (default: false) +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 # Clean up NVIDIA/cuda-python with actual deletions - $0 NVIDIA/cuda-python true # Preview what would be cleaned up (RECOMMENDED FIRST) - $0 myorg/my-repo false # Clean up a different repository + $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 @@ -52,16 +52,41 @@ REQUIREMENTS: - 'git' must be available SAFETY: -Always run with dry-run=true first to verify expected behavior before actual cleanup. -The script will show a summary of what would be removed and requires confirmation. +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 } -# Check for help flag -if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then - usage -fi +# 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..." @@ -71,21 +96,11 @@ if [[ -z "${GH_TOKEN:-}" ]]; then 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 -if ! command -v git >/dev/null 2>&1; then - echo -e "${RED}[ERROR]${NC} git 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 @@ -168,7 +183,7 @@ 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 (use dry-run=false to actually remove)" + 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..." @@ -181,10 +196,6 @@ else cd "$TEMP_DIR" - # Configure git for the cleanup commit - git config user.name "cuda-python-bot" - git config user.email "cuda-python-bot@users.noreply.github.com" - # Remove each folder REMOVED_COUNT=0 for folder in "${FOLDERS_TO_REMOVE[@]}"; do @@ -212,10 +223,14 @@ $(if [[ ${#FOLDERS_TO_REMOVE[@]} -gt 20 ]]; then echo "... and $((${#FOLDERS_TO_ echo -e "${YELLOW}[INFO]${NC} Committing changes..." git commit -m "$commit_message" - 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" + 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 From 724aed6470f9b2a6b4da8c52b7608f76d1c35345 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Fri, 26 Sep 2025 00:36:38 -0400 Subject: [PATCH 06/12] manual fix --- ci/cleanup-pr-previews | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index e70a38f84..3e7497e76 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -22,13 +22,6 @@ usage() { cat << EOF PR Preview Cleanup Script - Clean up stale PR preview documentation folders -PROBLEM: -The doc_preview action has limitations that result in stale preview folders: -- Cleanup steps only run when target branch is 'main' (PRs targeting feature branches aren't cleaned up) -- Canceled/interrupted documentation jobs don't run cleanup steps -- Other edge cases where cleanup logic isn't executed - -SOLUTION: 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. @@ -49,7 +42,6 @@ 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 - - 'git' must be available SAFETY: Always run with --dry-run first to verify expected behavior before actual cleanup. From 503a00230e70b54c453adda9ae81ec317dc0b9e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 04:45:02 +0000 Subject: [PATCH 07/12] Add back jq check and replace cloning with git worktree Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/cleanup-pr-previews | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 3e7497e76..03df129af 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -88,6 +88,11 @@ if [[ -z "${GH_TOKEN:-}" ]]; then 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 @@ -179,12 +184,25 @@ if [[ "$DRY_RUN" == "true" ]]; then else echo -e "${RED}[CLEANUP]${NC} Proceeding to remove ${#FOLDERS_TO_REMOVE[@]} folders..." - # Clone gh-pages branch to a temporary directory + # Create a git worktree for gh-pages branch TEMP_DIR=$(mktemp -d) - trap 'rm -rf "$TEMP_DIR"' EXIT - echo -e "${YELLOW}[INFO]${NC} Cloning gh-pages branch to temporary directory..." - git clone --depth 1 --branch gh-pages "https://github.com/${REPOSITORY}.git" "$TEMP_DIR" >/dev/null 2>&1 + # 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 + + 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" From c44f63a3880acd6c7b74bc941339af54edda05d0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 04:59:24 +0000 Subject: [PATCH 08/12] [pre-commit.ci] auto code formatting --- ci/cleanup-pr-previews | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 03df129af..5627d2020 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -45,7 +45,7 @@ REQUIREMENTS: 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 +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. @@ -125,19 +125,19 @@ 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" @@ -183,10 +183,10 @@ 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 @@ -196,22 +196,22 @@ else rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true } trap cleanup_worktree EXIT - + 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" @@ -221,7 +221,7 @@ 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 @@ -229,10 +229,10 @@ else 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 @@ -244,4 +244,4 @@ $(if [[ ${#FOLDERS_TO_REMOVE[@]} -gt 20 ]]; then echo "... and $((${#FOLDERS_TO_ else echo -e "${YELLOW}[INFO]${NC} No folders were actually removed (they may have been cleaned up already)" fi -fi \ No newline at end of file +fi From 241923e9e4a70419bd0b6d47699321364efbb8c0 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Fri, 26 Sep 2025 10:22:26 -0400 Subject: [PATCH 09/12] fix preview folder fetching --- ci/cleanup-pr-previews | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 5627d2020..47495a09f 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -104,10 +104,10 @@ echo -e "${GREEN}[INFO]${NC} All prerequisites satisfied" 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 \ +PR_FOLDERS=$(gh api repos/"${REPOSITORY}"/contents/docs/pr-preview?ref=gh-pages \ --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) + 2>/dev/null || true) if [[ -z "$PR_FOLDERS" ]]; then echo -e "${YELLOW}[INFO]${NC} No PR preview folders found in gh-pages branch" From 14aaca3b76bd34e0dadae2833c0d3bc6d339cb4c Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Fri, 26 Sep 2025 10:47:38 -0400 Subject: [PATCH 10/12] ensure temp dir is under the same root & gh-pages is up-to-date --- ci/cleanup-pr-previews | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 47495a09f..0b08faf16 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -185,7 +185,9 @@ 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) + TEMP_DIR="./gh-pages-cleanup" + rm -rf ${TEMP_DIR} + mkdir ${TEMP_DIR} # Cleanup function to properly remove worktree and temp directory cleanup_worktree() { @@ -197,6 +199,9 @@ else } trap cleanup_worktree EXIT + # Ensure the local gh-pages branch is up-to-date + git fetch origin gh-pages:gh-pages + 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 From 2a7b4ccedee29e5a2341024ebee4c71b988a4e57 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 14:53:53 +0000 Subject: [PATCH 11/12] Fix worktree cleanup to preserve unpushed changes Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/cleanup-pr-previews | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 0b08faf16..1c3b36d9d 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -192,10 +192,16 @@ else # 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 + # Only cleanup if changes have been pushed or if no changes were made + if [[ "${CHANGES_PUSHED:-false}" == "true" ]] || [[ "${REMOVED_COUNT:-0}" -eq 0 ]]; then + 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 + else + echo -e "${YELLOW}[INFO]${NC} Worktree preserved at $TEMP_DIR for manual verification" >&2 + echo -e "${YELLOW}[INFO]${NC} Remove manually with: git worktree remove $TEMP_DIR && rm -rf $TEMP_DIR" >&2 fi - rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true } trap cleanup_worktree EXIT @@ -241,12 +247,16 @@ $(if [[ ${#FOLDERS_TO_REMOVE[@]} -gt 20 ]]; then echo "... and $((${#FOLDERS_TO_ if [[ "$PUSH_CHANGES" == "true" ]]; then echo -e "${YELLOW}[INFO]${NC} Pushing to gh-pages branch..." git push origin gh-pages + CHANGES_PUSHED="true" echo -e "${GREEN}[SUCCESS]${NC} Cleanup completed! Removed ${REMOVED_COUNT} PR preview folders and pushed changes" else + CHANGES_PUSHED="false" 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." + echo -e "${YELLOW}[WARNING]${NC} Worktree will be preserved for manual verification." fi else + CHANGES_PUSHED="true" # No changes made, safe to cleanup echo -e "${YELLOW}[INFO]${NC} No folders were actually removed (they may have been cleaned up already)" fi fi From 49bdbce6bca8eef290e10a98fc7eab2a72f26e60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:05:04 +0000 Subject: [PATCH 12/12] Implement safer worktree cleanup with proper git registration handling Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- ci/cleanup-pr-previews | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/ci/cleanup-pr-previews b/ci/cleanup-pr-previews index 1c3b36d9d..b392808e3 100755 --- a/ci/cleanup-pr-previews +++ b/ci/cleanup-pr-previews @@ -186,8 +186,15 @@ else # Create a git worktree for gh-pages branch TEMP_DIR="./gh-pages-cleanup" - rm -rf ${TEMP_DIR} - mkdir ${TEMP_DIR} + + # Safely remove any existing worktree and directory + if [[ -d "$TEMP_DIR" ]]; then + echo -e "${YELLOW}[INFO]${NC} Cleaning up existing worktree at $TEMP_DIR..." + # Try to remove existing worktree first (if it's registered) + git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true + # Now remove the directory + rm -rf "$TEMP_DIR" + fi # Cleanup function to properly remove worktree and temp directory cleanup_worktree() { @@ -211,8 +218,22 @@ else 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 + + # Check if the issue might be a leftover worktree registration + if git worktree list | grep -q "$TEMP_DIR" 2>/dev/null; then + echo -e "${YELLOW}[INFO]${NC} Found existing worktree registration, attempting to clean up..." >&2 + git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true + rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true + + # Try again + if ! git worktree add "$TEMP_DIR" gh-pages >/dev/null 2>&1; then + echo -e "${RED}[ERROR]${NC} Still unable to create worktree after cleanup" >&2 + exit 1 + fi + else + echo "Make sure the gh-pages branch exists and is accessible" >&2 + exit 1 + fi fi cd "$TEMP_DIR"