diff --git a/.github/workflows/docs-pr-preview-cleanup.yml b/.github/workflows/docs-pr-preview-cleanup.yml new file mode 100644 index 000000000000..cd174ca7447d --- /dev/null +++ b/.github/workflows/docs-pr-preview-cleanup.yml @@ -0,0 +1,69 @@ +# Clean up documentation preview versions from Read the Docs +# --------------------------------------------------------- +# This workflow triggers when a PR is closed (merged or abandoned). +# It checks if a docs preview was created by parsing PR comments posted +# by the bot, then deactivates the corresponding version on Read the Docs. + +name: Docs Preview Cleanup + +on: + pull_request: + types: [closed] + +permissions: + contents: read + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Generate a token + id: generate-token + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + + - name: Get project slug from preview comment + id: rtd + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + # Extract project identifier from the bot's comment. + # The JSON output for a valid commit will have "assistant-librarian[bot]" + # for key `user.login` and contain "rocm.docs.amd.com/projects/" for key `body` + # and we need to parse "rocm.docs.amd.com/projects/project_slug/xyz..." to "project_slug" + + comment=$(gh api repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \ + --jq '[.[] | select(.user.login == "assistant-librarian[bot]" and (.body | contains("rocm.docs.amd.com/projects/")))] | last | .body') + + if [ -z "$comment" ] || [ "$comment" = "null" ]; then + echo "No preview comment found, skipping cleanup." + exit 0 + fi + + # Extract the string immediately following "projects/" (up to the next "/") from $comment + project_alias=$(echo "$comment" | grep --only-matching --perl-regexp 'projects/\K[^/]+') + echo "project_slug=advanced-micro-devices-${project_alias}" >> $GITHUB_OUTPUT + + - name: Deactivate RTD preview version + if: steps.rtd.outputs.project_slug + env: + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + run: | + branch_name="${{ github.head_ref }}" + # Generate slug like 'user-me-my_test_branch' from 'user/me/my_test_branch' + version_slug="${branch_name//\//-}" + + response=$(curl \ + --connect-timeout 180 \ + --silent \ + --output /dev/null \ + --write-out "%{http_code}" \ + --request PATCH \ + --header "Authorization: Token $RTD_TOKEN" \ + --header "Content-Type: application/json" \ + --data '{"active": false}' \ + "https://app.readthedocs.com/api/v3/projects/${{ steps.rtd.outputs.project_slug }}/versions/${version_slug}/") + echo "RTD PATCH responded with HTTP $response" diff --git a/.github/workflows/docs-pr-preview.yml b/.github/workflows/docs-pr-preview.yml new file mode 100644 index 000000000000..3b813a6e60b7 --- /dev/null +++ b/.github/workflows/docs-pr-preview.yml @@ -0,0 +1,160 @@ +# Create a documentation preview in the pull request +# -------------------------------------------------- +# This GitHub Actions workflow triggers by commenting '/docs-preview' on a PR. +# It creates a new Read the Docs version for the PR branch and triggers a build. +# +# Key Steps: +# 1. Use the script (.github/scripts/pr_detect_changed_subtrees.py) to detect changed project +# 2. Get the PR branch name and derive the RTD version slug +# 3. Activate the branch as a new version on Read the Docs (key difference from update-docs.yml) +# 4. Trigger a build for that version +# 5. Comment the preview URL back on the PR +# +# Note: if multiple projects are changed in one PR, only the first detected project is previewed. + +name: Docs Preview + +on: + issue_comment: + types: [created, edited] + +permissions: + contents: read + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + # Only run when '/docs-preview' is commented on a PR by a member or owner + if: >- + github.event.issue.pull_request != null && + contains(github.event.comment.body, '/docs-preview') && + (github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'OWNER') + steps: + - name: Generate a token + id: generate-token + uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + + - name: Checkout config files only + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + sparse-checkout: .github + sparse-checkout-cone-mode: true + token: ${{ steps.generate-token.outputs.token }} + + - name: Set up Python + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: '3.10' + + - name: Install python dependencies + run: | + python -m pip install --upgrade pip + pip install pydantic requests + + - name: Set up Git user + run: | + git config user.name "assistant-librarian[bot]" + git config user.email "assistant-librarian[bot]@users.noreply.github.com" + + - name: Detect changed subtrees from PR + id: detect + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + python .github/scripts/pr_detect_changed_subtrees.py \ + --repo "${{ github.repository }}" \ + --pr "${{ github.event.issue.number }}" \ + --config ".github/repos-config.json" \ + --require-auto-push + + - name: Extract first project + id: extract + run: | + # pr_detect_changed_subtrees.py returns a string of project names + # separated by "\n", i.e. "projects/rocprim\nprojects/hipblaslt\nshared/tensile" + # head -n1: extracts the first project, i.e. "projects/rocprim" + # cut -d'/' -f2: cuts the "projects/" prefix, leaving "rocprim" for project_name + project=$(echo "${{ steps.detect.outputs.subtrees }}" | head -n1) + echo "project=$project" >> $GITHUB_OUTPUT + echo "project_name=$(echo "$project" | cut -d'/' -f2)" >> $GITHUB_OUTPUT + + - name: Get PR branch + if: steps.extract.outputs.project + id: pr + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}) + echo "branch=$(echo $PR_DATA | jq -r .head.ref)" >> $GITHUB_OUTPUT + + - name: Derive RTD slugs and verify project exists + if: steps.extract.outputs.project + id: rtd + env: + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + run: | + branch_name="${{ steps.pr.outputs.branch }}" + # Generate slug like 'user-me-my_test_branch' from 'user/me/my_test_branch' + version_slug="${branch_name//\//-}" + project_slug="advanced-micro-devices-${{ steps.extract.outputs.project_name }}" + + echo "version_slug=$version_slug" >> $GITHUB_OUTPUT + echo "project_slug=$project_slug" >> $GITHUB_OUTPUT + + # The API call returns upto 300 projects information from RTD + # jq extracts the project slug from the list + project_list=$(curl \ + --connect-timeout 180 + --header "Authorization: Token $RTD_TOKEN" \ + "https://app.readthedocs.com/api/v3/projects/?expand=advanced-micro-devices&limit=300" \ + | jq -r '.results[].slug') + + if echo "$project_list" | grep -Fxq "$project_slug"; then + echo "exists=true" >> $GITHUB_OUTPUT + else + echo "${{ steps.extract.outputs.project_name }} does not exist on Read the Docs, skipping." + echo "exists=false" >> $GITHUB_OUTPUT + fi + + - name: Activate PR branch as a new RTD version + if: steps.rtd.outputs.exists == 'true' + env: + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + run: | + curl \ + --connect-timeout 180 \ + --fail \ + --request PATCH \ + --header "Authorization: Token $RTD_TOKEN" \ + --header "Content-Type: application/json" \ + --data '{"active": true, "hidden": true, "privacy_level": "private"}' \ + "https://app.readthedocs.com/api/v3/projects/${{ steps.rtd.outputs.project_slug }}/versions/${{ steps.rtd.outputs.version_slug }}/" + + - name: Trigger RTD build for PR branch + if: steps.rtd.outputs.exists == 'true' + env: + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + run: | + curl \ + --fail \ + --silent \ + --request POST \ + --header "Authorization: Token $RTD_TOKEN" \ + "https://app.readthedocs.com/api/v3/projects/${{ steps.rtd.outputs.project_slug }}/versions/${{ steps.rtd.outputs.version_slug }}/builds/" + + - name: Comment RTD preview URL on PR + if: steps.rtd.outputs.exists == 'true' + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + run: | + preview_url="https://rocm.docs.amd.com/projects/${{ steps.extract.outputs.project_name }}/en/${{ steps.rtd.outputs.version_slug }}/" + gh pr comment ${{ github.event.issue.number }} \ + --repo ${{ github.repository }} \ + --body "Docs preview for **${{ steps.extract.outputs.project_name }}** is building on Read the Docs: $preview_url \ + (Note: If a pull request includes multiple projects, only the preview for the first project will be built.)"