Skip to content

Commit 19e4b8f

Browse files
authored
Merge branch 'main' into memory-refactor
2 parents cf4dc9d + f223c17 commit 19e4b8f

File tree

15 files changed

+1438
-150
lines changed

15 files changed

+1438
-150
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: "Cleanup: PR Preview Documentation"
6+
7+
on:
8+
schedule:
9+
# Run every night at 11pm EST (4am UTC during EST, 3am UTC during EDT)
10+
# Using 4am UTC to be safe during EST (Nov-Mar)
11+
- cron: '0 4 * * *'
12+
workflow_dispatch:
13+
inputs:
14+
dry-run:
15+
description: 'Run in dry-run mode (preview only, no changes)'
16+
required: false
17+
default: false
18+
type: boolean
19+
20+
permissions:
21+
contents: write # Required to push changes to gh-pages branch
22+
23+
jobs:
24+
cleanup:
25+
name: Clean up stale PR preview folders
26+
runs-on: ubuntu-latest
27+
# Only run for NVIDIA org to prevent forks from running this
28+
if: github.repository_owner == 'NVIDIA'
29+
steps:
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
with:
33+
# Fetch all history and branches for worktree operations
34+
fetch-depth: 0
35+
36+
- name: Configure git identity
37+
run: |
38+
git config --global user.name "cuda-python-bot"
39+
git config --global user.email "[email protected]"
40+
41+
- name: Run PR preview cleanup script
42+
env:
43+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
44+
run: |
45+
# Determine if we should run in dry-run mode
46+
if [[ "${{ inputs.dry-run }}" == "true" ]]; then
47+
echo "Running in dry-run mode (preview only)"
48+
./ci/cleanup-pr-previews --dry-run
49+
else
50+
echo "Running cleanup with push to gh-pages"
51+
./ci/cleanup-pr-previews --push
52+
fi
53+
54+
- name: Summary
55+
if: always()
56+
run: |
57+
echo "### PR Preview Cleanup Summary" >> $GITHUB_STEP_SUMMARY
58+
echo "" >> $GITHUB_STEP_SUMMARY
59+
if [[ "${{ inputs.dry-run }}" == "true" ]]; then
60+
echo "✅ Dry-run completed successfully" >> $GITHUB_STEP_SUMMARY
61+
echo "No changes were made to the gh-pages branch" >> $GITHUB_STEP_SUMMARY
62+
else
63+
echo "✅ Cleanup completed and changes pushed to gh-pages" >> $GITHUB_STEP_SUMMARY
64+
fi
65+
echo "" >> $GITHUB_STEP_SUMMARY
66+
echo "Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_STEP_SUMMARY

ci/cleanup-pr-previews

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
7+
# A utility script to clean up PR preview documentation folders for closed/merged PRs.
8+
# This script checks all pr-XXXXX folders in the gh-pages branch docs/pr-preview/ directory,
9+
# verifies if the corresponding PR XXXXX is still open, and removes preview folders
10+
# for PRs that have been closed or merged.
11+
12+
set -euo pipefail
13+
14+
# Colors for output
15+
RED='\033[0;31m'
16+
GREEN='\033[0;32m'
17+
YELLOW='\033[1;33m'
18+
NC='\033[0m' # No Color
19+
20+
# Usage information
21+
usage() {
22+
cat << EOF
23+
PR Preview Cleanup Script - Clean up stale PR preview documentation folders
24+
25+
This script fetches all pr-XXXXX folders from docs/pr-preview/ in the gh-pages branch,
26+
checks PR status via GitHub API, and removes folders for closed/merged/deleted PRs.
27+
28+
USAGE: $0 [OPTIONS]
29+
30+
OPTIONS:
31+
-n, --dry-run Preview what would be deleted without actually deleting
32+
--push Commit and push changes to gh-pages (default: false, requires manual push)
33+
-h, --help Show this help message
34+
35+
EXAMPLES:
36+
$0 -n # Preview what would be cleaned up (RECOMMENDED FIRST)
37+
$0 # Clean up folders locally (no push)
38+
$0 --push # Clean up folders and push to gh-pages branch
39+
$0 --dry-run --push # Invalid combination (dry-run takes precedence)
40+
41+
REQUIREMENTS:
42+
- GH_TOKEN environment variable must be set with appropriate permissions
43+
- 'gh' (GitHub CLI) must be installed and authenticated
44+
- 'jq' must be installed for JSON parsing
45+
46+
SAFETY:
47+
Always run with --dry-run first to verify expected behavior before actual cleanup.
48+
The script will show a summary of what would be removed. Use --push to automatically
49+
commit and push changes, otherwise manual git operations are required.
50+
51+
This script is specifically designed for the NVIDIA/cuda-python repository structure.
52+
EOF
53+
exit 1
54+
}
55+
56+
# Configuration - hardcoded for this specific repository
57+
REPOSITORY="NVIDIA/cuda-python"
58+
DRY_RUN="false"
59+
PUSH_CHANGES="false"
60+
61+
# Parse command line arguments
62+
while [[ $# -gt 0 ]]; do
63+
case $1 in
64+
-n|--dry-run)
65+
DRY_RUN="true"
66+
shift
67+
;;
68+
--push)
69+
PUSH_CHANGES="true"
70+
shift
71+
;;
72+
-h|--help)
73+
usage
74+
;;
75+
*)
76+
echo -e "${RED}[ERROR]${NC} Unknown option: $1" >&2
77+
echo "Use --help for usage information" >&2
78+
exit 1
79+
;;
80+
esac
81+
done
82+
83+
# Validate required tools and environment
84+
echo -e "${YELLOW}[INFO]${NC} Checking prerequisites..."
85+
86+
if [[ -z "${GH_TOKEN:-}" ]]; then
87+
echo -e "${RED}[ERROR]${NC} GH_TOKEN environment variable is required" >&2
88+
exit 1
89+
fi
90+
91+
if ! command -v jq >/dev/null 2>&1; then
92+
echo -e "${RED}[ERROR]${NC} jq is required but not installed" >&2
93+
exit 1
94+
fi
95+
96+
if ! command -v gh >/dev/null 2>&1; then
97+
echo -e "${RED}[ERROR]${NC} GitHub CLI (gh) is required but not installed" >&2
98+
exit 1
99+
fi
100+
101+
echo -e "${GREEN}[INFO]${NC} All prerequisites satisfied"
102+
103+
# Fetch PR preview folders from gh-pages branch
104+
echo -e "${YELLOW}[INFO]${NC} Fetching PR preview folders from gh-pages branch..."
105+
106+
# Get the list of pr-XXXXX folders from gh-pages branch
107+
PR_FOLDERS=$(gh api repos/"${REPOSITORY}"/contents/docs/pr-preview?ref=gh-pages \
108+
--header "Accept: application/vnd.github+json" \
109+
--jq '.[] | select(.type == "dir" and (.name | test("^pr-[0-9]+$"))) | .name' \
110+
2>/dev/null || true)
111+
112+
if [[ -z "$PR_FOLDERS" ]]; then
113+
echo -e "${YELLOW}[INFO]${NC} No PR preview folders found in gh-pages branch"
114+
exit 0
115+
fi
116+
117+
echo -e "${GREEN}[INFO]${NC} Found $(echo "$PR_FOLDERS" | wc -l) PR preview folders"
118+
119+
# Check each PR folder
120+
FOLDERS_TO_REMOVE=()
121+
TOTAL_FOLDERS=0
122+
OPEN_PRS=0
123+
124+
while IFS= read -r folder; do
125+
if [[ -z "$folder" ]]; then
126+
continue
127+
fi
128+
129+
TOTAL_FOLDERS=$((TOTAL_FOLDERS + 1))
130+
131+
# Extract PR number from folder name (pr-XXXXX -> XXXXX)
132+
PR_NUMBER="${folder#pr-}"
133+
134+
echo -e "${YELLOW}[CHECK]${NC} Checking PR #${PR_NUMBER}..."
135+
136+
# Check PR status using GitHub API
137+
PR_STATUS=$(gh api repos/"${REPOSITORY}"/pulls/"${PR_NUMBER}" \
138+
--header "Accept: application/vnd.github+json" \
139+
--jq '.state' 2>/dev/null || echo "not_found")
140+
141+
case "$PR_STATUS" in
142+
"open")
143+
echo -e "${GREEN}[KEEP]${NC} PR #${PR_NUMBER} is still open"
144+
OPEN_PRS=$((OPEN_PRS + 1))
145+
;;
146+
"closed")
147+
echo -e "${RED}[REMOVE]${NC} PR #${PR_NUMBER} is closed"
148+
FOLDERS_TO_REMOVE+=("$folder")
149+
;;
150+
"not_found")
151+
echo -e "${RED}[REMOVE]${NC} PR #${PR_NUMBER} not found (may have been deleted)"
152+
FOLDERS_TO_REMOVE+=("$folder")
153+
;;
154+
*)
155+
echo -e "${YELLOW}[UNKNOWN]${NC} PR #${PR_NUMBER} has unexpected status: ${PR_STATUS}"
156+
;;
157+
esac
158+
done <<< "$PR_FOLDERS"
159+
160+
# Summary
161+
echo ""
162+
echo -e "${YELLOW}[SUMMARY]${NC}"
163+
echo "Total PR preview folders: ${TOTAL_FOLDERS}"
164+
echo "Open PRs: ${OPEN_PRS}"
165+
echo "Folders to remove: ${#FOLDERS_TO_REMOVE[@]}"
166+
167+
if [[ ${#FOLDERS_TO_REMOVE[@]} -eq 0 ]]; then
168+
echo -e "${GREEN}[INFO]${NC} No cleanup needed - all preview folders correspond to open PRs"
169+
exit 0
170+
fi
171+
172+
# List folders to be removed
173+
echo ""
174+
echo -e "${YELLOW}[FOLDERS TO REMOVE]${NC}"
175+
for folder in "${FOLDERS_TO_REMOVE[@]}"; do
176+
pr_num="${folder#pr-}"
177+
echo " - $folder (PR #${pr_num})"
178+
done
179+
180+
# Perform cleanup or show what would be done
181+
echo ""
182+
if [[ "$DRY_RUN" == "true" ]]; then
183+
echo -e "${YELLOW}[DRY RUN]${NC} Would remove ${#FOLDERS_TO_REMOVE[@]} folders (run without --dry-run to actually remove)"
184+
else
185+
echo -e "${RED}[CLEANUP]${NC} Proceeding to remove ${#FOLDERS_TO_REMOVE[@]} folders..."
186+
187+
# Create a git worktree for gh-pages branch
188+
TEMP_DIR="./gh-pages-cleanup"
189+
190+
# Safely remove any existing worktree and directory
191+
if [[ -d "$TEMP_DIR" ]]; then
192+
echo -e "${YELLOW}[INFO]${NC} Cleaning up existing worktree at $TEMP_DIR..."
193+
# Try to remove existing worktree first (if it's registered)
194+
git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true
195+
# Now remove the directory
196+
rm -rf "$TEMP_DIR"
197+
fi
198+
199+
# Cleanup function to properly remove worktree and temp directory
200+
cleanup_worktree() {
201+
cd - >/dev/null 2>&1 || true # Go back to original directory
202+
# Only cleanup if changes have been pushed or if no changes were made
203+
if [[ "${CHANGES_PUSHED:-false}" == "true" ]] || [[ "${REMOVED_COUNT:-0}" -eq 0 ]]; then
204+
if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
205+
git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true
206+
fi
207+
rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true
208+
else
209+
echo -e "${YELLOW}[INFO]${NC} Worktree preserved at $TEMP_DIR for manual verification" >&2
210+
echo -e "${YELLOW}[INFO]${NC} Remove manually with: git worktree remove $TEMP_DIR && rm -rf $TEMP_DIR" >&2
211+
fi
212+
}
213+
trap cleanup_worktree EXIT
214+
215+
# Ensure the local gh-pages branch is up-to-date
216+
git fetch origin gh-pages:gh-pages
217+
218+
echo -e "${YELLOW}[INFO]${NC} Creating git worktree for gh-pages branch..."
219+
if ! git worktree add "$TEMP_DIR" gh-pages >/dev/null 2>&1; then
220+
echo -e "${RED}[ERROR]${NC} Failed to create git worktree for gh-pages branch" >&2
221+
222+
# Check if the issue might be a leftover worktree registration
223+
if git worktree list | grep -q "$TEMP_DIR" 2>/dev/null; then
224+
echo -e "${YELLOW}[INFO]${NC} Found existing worktree registration, attempting to clean up..." >&2
225+
git worktree remove "$TEMP_DIR" --force >/dev/null 2>&1 || true
226+
rm -rf "$TEMP_DIR" >/dev/null 2>&1 || true
227+
228+
# Try again
229+
if ! git worktree add "$TEMP_DIR" gh-pages >/dev/null 2>&1; then
230+
echo -e "${RED}[ERROR]${NC} Still unable to create worktree after cleanup" >&2
231+
exit 1
232+
fi
233+
else
234+
echo "Make sure the gh-pages branch exists and is accessible" >&2
235+
exit 1
236+
fi
237+
fi
238+
239+
cd "$TEMP_DIR"
240+
241+
# Remove each folder
242+
REMOVED_COUNT=0
243+
for folder in "${FOLDERS_TO_REMOVE[@]}"; do
244+
pr_num="${folder#pr-}"
245+
folder_path="docs/pr-preview/$folder"
246+
247+
if [[ -d "$folder_path" ]]; then
248+
echo -e "${YELLOW}[REMOVE]${NC} Removing $folder_path"
249+
rm -rf "$folder_path"
250+
git add "$folder_path"
251+
REMOVED_COUNT=$((REMOVED_COUNT + 1))
252+
else
253+
echo -e "${YELLOW}[SKIP]${NC} Folder $folder_path not found locally"
254+
fi
255+
done
256+
257+
if [[ $REMOVED_COUNT -gt 0 ]]; then
258+
# Commit and push changes
259+
commit_message="Clean up PR preview folders for ${REMOVED_COUNT} closed/merged PRs
260+
261+
Removed preview folders for the following PRs:
262+
$(printf '%s\n' "${FOLDERS_TO_REMOVE[@]}" | sed 's/^pr-/- PR #/' | head -20)
263+
$(if [[ ${#FOLDERS_TO_REMOVE[@]} -gt 20 ]]; then echo "... and $((${#FOLDERS_TO_REMOVE[@]} - 20)) more"; fi)"
264+
265+
echo -e "${YELLOW}[INFO]${NC} Committing changes..."
266+
git commit -m "$commit_message"
267+
268+
if [[ "$PUSH_CHANGES" == "true" ]]; then
269+
echo -e "${YELLOW}[INFO]${NC} Pushing to gh-pages branch..."
270+
git push origin gh-pages
271+
CHANGES_PUSHED="true"
272+
echo -e "${GREEN}[SUCCESS]${NC} Cleanup completed! Removed ${REMOVED_COUNT} PR preview folders and pushed changes"
273+
else
274+
CHANGES_PUSHED="false"
275+
echo -e "${GREEN}[SUCCESS]${NC} Cleanup completed! Removed ${REMOVED_COUNT} PR preview folders"
276+
echo -e "${YELLOW}[INFO]${NC} Changes have been committed locally but not pushed. Use 'git push origin gh-pages' to push manually."
277+
echo -e "${YELLOW}[WARNING]${NC} Worktree will be preserved for manual verification."
278+
fi
279+
else
280+
CHANGES_PUSHED="true" # No changes made, safe to cleanup
281+
echo -e "${YELLOW}[INFO]${NC} No folders were actually removed (they may have been cleaned up already)"
282+
fi
283+
fi

cuda_core/cuda/core/experimental/_context.pyx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,10 @@ cdef class Context:
2929
return ctx
3030

3131
def __eq__(self, other):
32-
return int(self._handle) == int(other._handle)
32+
if not isinstance(other, Context):
33+
return NotImplemented
34+
cdef Context _other = <Context>other
35+
return int(self._handle) == int(_other._handle)
36+
37+
def __hash__(self) -> int:
38+
return hash(int(self._handle))

0 commit comments

Comments
 (0)