diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7f777823098e..e2d59a733966 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,31 +1,22 @@ - ### What -### Checklist -* [ ] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) -* [ ] I've included a screenshot or gif (if applicable) -* [ ] I have tested the web demo (if applicable): - * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/{{pr.number}}?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) - * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/{{pr.number}}?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) -* [ ] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG -* [ ] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! -* [ ] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide + diff --git a/.github/workflows/auto_docs_check.yml b/.github/workflows/auto_docs_check.yml index c6e530e5dc75..f81f4b3a992f 100644 --- a/.github/workflows/auto_docs_check.yml +++ b/.github/workflows/auto_docs_check.yml @@ -26,7 +26,7 @@ jobs: check-cherry-pick: name: Check if merge commit can be cherry-picked runs-on: ubuntu-latest - if: ${{ contains(github.event.pull_request.labels.*.name, 'deploy docs') }} + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && contains(github.event.pull_request.labels.*.name, 'deploy docs') steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/checkboxes.yml b/.github/workflows/checkboxes.yml index fabccea3c79c..f4b14c01b40e 100644 --- a/.github/workflows/checkboxes.yml +++ b/.github/workflows/checkboxes.yml @@ -20,7 +20,7 @@ defaults: permissions: contents: "read" - pull-requests: "write" + pull-requests: "read" jobs: pr-checkboxes: diff --git a/.github/workflows/first_time_contrib.yml b/.github/workflows/first_time_contrib.yml new file mode 100644 index 000000000000..5a46efd25dc3 --- /dev/null +++ b/.github/workflows/first_time_contrib.yml @@ -0,0 +1,22 @@ +name: First time contributors + +on: + pull_request_target: + +permissions: + contents: "write" + id-token: "write" + pull-requests: "write" + +jobs: + comment: + name: Comment on PRs + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1.3.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + pr-message: | + Hi! Thanks for opening this pull request. + + Because this is your first time contributing to this repository, make sure you've read our [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md). diff --git a/.github/workflows/on_pull_request.yml b/.github/workflows/on_pull_request.yml index 7ece8b240b1a..21cc5d17795c 100644 --- a/.github/workflows/on_pull_request.yml +++ b/.github/workflows/on_pull_request.yml @@ -25,11 +25,15 @@ jobs: CHANNEL: pr secrets: inherit - cpp-paths-filter: + paths-filter: if: github.event.pull_request.head.repo.owner.login == 'rerun-io' runs-on: ubuntu-latest outputs: cpp_changes: ${{ steps.filter.outputs.cpp_changes }} + docs_changes: ${{ steps.filter.outputs.docs_changes }} + python_changes: ${{ steps.filter.outputs.python_changes }} + rust_changes: ${{ steps.filter.outputs.rust_changes }} + web_changes: ${{ steps.filter.outputs.web_changes }} steps: - uses: actions/checkout@v4 with: @@ -48,16 +52,6 @@ jobs: - '**/CMakeLists.txt' - '**/*cmake' - docs-paths-filter: - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - runs-on: ubuntu-latest - outputs: - docs_changes: ${{ steps.filter.outputs.docs_changes }} - steps: - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | docs_changes: # - .github/**/*.yml - this is tempting, but leads to constant rebuilds - .github/actions/vercel/**/* @@ -68,19 +62,6 @@ jobs: - 'examples/**/*.md' - 'examples/manifest.toml' - python-paths-filter: - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - runs-on: ubuntu-latest - outputs: - python_changes: ${{ steps.filter.outputs.python_changes }} - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | python_changes: # - .github/**/*.yml - this is tempting, but leads to constant rebuilds - pixi.lock # maybe out build commands have changed @@ -90,19 +71,6 @@ jobs: - '**/requirements.txt' - '**/pyproject.toml' - rust-paths-filter: - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - runs-on: ubuntu-latest - outputs: - rust_changes: ${{ steps.filter.outputs.rust_changes }} - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | rust_changes: # - .github/**/*.yml - this is tempting, but leads to constant rebuilds - Cargo.lock @@ -112,19 +80,6 @@ jobs: - "**/*.rs" - "**/*.toml" - web-paths-filter: - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - runs-on: ubuntu-latest - outputs: - web_changes: ${{ steps.filter.outputs.web_changes }} - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} - - uses: dorny/paths-filter@v3 - id: filter - with: - filters: | web_changes: # - .github/**/*.yml - this is tempting, but leads to constant rebuilds - Cargo.lock @@ -141,8 +96,8 @@ jobs: rust-checks: name: "Rust Checks" - needs: rust-paths-filter - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.rust-paths-filter.outputs.rust_changes == 'true' + needs: [paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.paths-filter.outputs.rust_changes == 'true' uses: ./.github/workflows/reusable_checks_rust.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -151,8 +106,8 @@ jobs: python-checks: name: "Python Checks" - needs: python-paths-filter - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.python-paths-filter.outputs.python_changes == 'true' + needs: [paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.paths-filter.outputs.python_changes == 'true' uses: ./.github/workflows/reusable_checks_python.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -160,8 +115,8 @@ jobs: cpp-tests: name: "C++ tests" - needs: cpp-paths-filter - if: needs.cpp-paths-filter.outputs.cpp_changes == 'true' + needs: [paths-filter] + if: needs.paths-filter.outputs.cpp_changes == 'true' uses: ./.github/workflows/reusable_checks_cpp.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -170,8 +125,8 @@ jobs: min-cli-build: name: "Minimum CLI Build" - needs: [python-paths-filter, rust-paths-filter] - if: needs.python-paths-filter.outputs.python_changes == 'true' || needs.rust-paths-filter.outputs.rust_changes == 'true' + needs: [paths-filter] + if: needs.paths-filter.outputs.python_changes == 'true' || needs.paths-filter.outputs.rust_changes == 'true' uses: ./.github/workflows/reusable_build_and_upload_rerun_cli.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -182,8 +137,8 @@ jobs: # choice, but reusable_test_wheels.yml is broken for that target (https://github.com/rerun-io/rerun/issues/5525) min-wheel-build: name: "Minimum Wheel Build" - needs: [min-cli-build, python-paths-filter, rust-paths-filter] - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && (needs.python-paths-filter.outputs.python_changes == 'true' || needs.rust-paths-filter.outputs.rust_changes == 'true') + needs: [min-cli-build, paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && (needs.paths-filter.outputs.python_changes == 'true' || needs.paths-filter.outputs.rust_changes == 'true') uses: ./.github/workflows/reusable_build_and_upload_wheels.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -194,8 +149,8 @@ jobs: min-wheel-test: name: "Minimum Wheel Test" - needs: [min-wheel-build, python-paths-filter, rust-paths-filter] - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && (needs.python-paths-filter.outputs.python_changes == 'true' || needs.rust-paths-filter.outputs.rust_changes == 'true') + needs: [min-wheel-build, paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && (needs.paths-filter.outputs.python_changes == 'true' || needs.paths-filter.outputs.rust_changes == 'true') uses: ./.github/workflows/reusable_test_wheels.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -206,8 +161,8 @@ jobs: build-js: name: "Build rerun_js" - needs: [web-paths-filter] - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.web-paths-filter.outputs.web_changes == 'true' + needs: [paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.paths-filter.outputs.web_changes == 'true' uses: ./.github/workflows/reusable_build_js.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -215,8 +170,8 @@ jobs: build-web: name: "Build web viewer" - needs: [web-paths-filter] - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.web-paths-filter.outputs.web_changes == 'true' + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.paths-filter.outputs.web_changes == 'true' + needs: [paths-filter] uses: ./.github/workflows/reusable_build_web.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} @@ -225,28 +180,9 @@ jobs: upload-web: name: "Upload Web" - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' needs: [build-web] - uses: ./.github/workflows/reusable_upload_web.yml - with: - CONCURRENCY: pr-${{ github.event.pull_request.number }} - PR_NUMBER: ${{ github.event.pull_request.number }} - secrets: inherit - - save-pr-summary: - name: "Save PR Summary" if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - needs: [upload-web] - uses: ./.github/workflows/reusable_pr_summary.yml - with: - CONCURRENCY: pr-${{ github.event.pull_request.number }} - PR_NUMBER: ${{ github.event.pull_request.number }} - secrets: inherit - - update-pr-body: - name: "Update PR Body" - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' - uses: ./.github/workflows/reusable_update_pr_body.yml + uses: ./.github/workflows/reusable_upload_web.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} PR_NUMBER: ${{ github.event.pull_request.number }} @@ -254,8 +190,8 @@ jobs: deploy-landing-preview: name: "Deploy Landing Preview" - if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.docs-paths-filter.outputs.docs_changes == 'true' - needs: docs-paths-filter + needs: [paths-filter] + if: github.event.pull_request.head.repo.owner.login == 'rerun-io' && needs.paths-filter.outputs.docs_changes == 'true' uses: ./.github/workflows/reusable_deploy_landing_preview.yml with: CONCURRENCY: pr-${{ github.event.pull_request.number }} diff --git a/.github/workflows/on_pull_request_target_contrib.yml b/.github/workflows/on_pull_request_target_contrib.yml deleted file mode 100644 index b4010f65b3a4..000000000000 --- a/.github/workflows/on_pull_request_target_contrib.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Jobs that only run for external contributors. -# These have to be carefully sanitized, we don't want to leak secrets. -name: Pull-Request-Target (Contrib) - -on: - # This will run workflows triggered by a pull request from the _base_ branch. - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target - pull_request_target: - types: - - opened - - synchronize - -defaults: - run: - shell: bash - -permissions: - contents: "read" - pull-requests: "write" # Updates PR body - -jobs: - update-pr-body: - name: Update PR body - if: github.event.pull_request.head.repo.owner.login != 'rerun-io' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - uses: prefix-dev/setup-pixi@v0.8.1 - with: - pixi-version: v0.34.0 - - - name: Update PR description - run: | - pixi run ./scripts/ci/update_pr_body.py \ - --github-token '${{ secrets.GITHUB_TOKEN }}' \ - --github-repository '${{ github.repository }}' \ - --pr-number '${{ github.event.pull_request.number }}' diff --git a/.github/workflows/reusable_build_web.yml b/.github/workflows/reusable_build_web.yml index 8c8acc383b46..8eecabae7f3b 100644 --- a/.github/workflows/reusable_build_web.yml +++ b/.github/workflows/reusable_build_web.yml @@ -42,8 +42,9 @@ defaults: shell: bash permissions: - contents: "read" + contents: "write" id-token: "write" + pull-requests: "write" jobs: rs-build-web-viewer: @@ -54,6 +55,28 @@ jobs: with: ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} + - name: Get sha + id: get-sha + run: | + full_commit="${{ (github.event_name == 'pull_request' && github.event.pull_request.head.sha) || github.sha }}" + echo "sha=$(echo $full_commit | cut -c1-7)" >> "$GITHUB_OUTPUT" + + - name: Status comment + if: github.event_name == 'pull_request' + # https://github.com/mshick/add-pr-comment + uses: mshick/add-pr-comment@v2.8.2 + with: + message-id: "web-viewer-build-status" + repo-token: ${{ secrets.GITHUB_TOKEN }} + message: | + Web viewer is being built. + + | Result | Commit | Link | + | ------ | ------- | ----- | + | ⏳ | ${{ steps.get-sha.outputs.sha }} | https://rerun.io/viewer/pr/${{ github.event.pull_request.number }} | + + Note: This comment is updated whenever you push a commit. + - name: Set up Rust uses: ./.github/actions/setup-rust with: @@ -91,3 +114,19 @@ jobs: with: name: web_viewer path: web_viewer + + - name: Status comment + if: failure() && github.event_name == 'pull_request' + # https://github.com/mshick/add-pr-comment + uses: mshick/add-pr-comment@v2.8.2 + with: + message-id: "web-viewer-build-status" + repo-token: ${{ secrets.GITHUB_TOKEN }} + message: | + Web viewer failed to build. + + | Result | Commit | Link | + | ------ | ------- | ----- | + | ❌ | ${{ steps.get-sha.outputs.sha }} | https://rerun.io/viewer/pr/${{ github.event.pull_request.number }} | + + Note: This comment is updated whenever you push a commit. diff --git a/.github/workflows/reusable_pr_summary.yml b/.github/workflows/reusable_pr_summary.yml deleted file mode 100644 index 8bc800d61f8b..000000000000 --- a/.github/workflows/reusable_pr_summary.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Reusable PR Summary - -on: - workflow_call: - inputs: - CONCURRENCY: - required: true - type: string - PR_NUMBER: - required: true - type: string - -concurrency: - group: ${{ inputs.CONCURRENCY }}-pr-summary - cancel-in-progress: true - -defaults: - run: - shell: bash - -permissions: - contents: "read" - id-token: "write" - pull-requests: "write" - -jobs: - pr-summary: - name: Create HTML summary for PR - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || '' }} - - - uses: prefix-dev/setup-pixi@v0.8.1 - with: - pixi-version: v0.34.0 - - - id: "auth" - uses: google-github-actions/auth@v2 - with: - workload_identity_provider: ${{ secrets.GOOGLE_WORKLOAD_IDENTITY_PROVIDER }} - service_account: ${{ secrets.GOOGLE_SERVICE_ACCOUNT }} - - - name: "Set up Cloud SDK" - uses: "google-github-actions/setup-gcloud@v2" - with: - version: ">= 363.0.0" - - - name: Render HTML template - run: | - pixi run python scripts/ci/generate_pr_summary.py \ - --github-token ${{secrets.GITHUB_TOKEN}} \ - --github-repository ${GITHUB_REPOSITORY} \ - --pr-number ${{ inputs.PR_NUMBER }} \ - --upload - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ inputs.PR_NUMBER }} diff --git a/.github/workflows/reusable_update_pr_body.yml b/.github/workflows/reusable_update_pr_body.yml deleted file mode 100644 index 99564254b110..000000000000 --- a/.github/workflows/reusable_update_pr_body.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Update links in PR Body - -on: - workflow_call: - inputs: - CONCURRENCY: - required: true - type: string - PR_NUMBER: - required: true - type: string - -concurrency: - group: ${{ inputs.CONCURRENCY }}-pr-update-body - cancel-in-progress: true - -defaults: - run: - shell: bash - -permissions: - contents: "read" - id-token: "write" - pull-requests: "write" - -jobs: - update-pr-body: - name: Update PR body - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - uses: prefix-dev/setup-pixi@v0.8.1 - with: - pixi-version: v0.34.0 - - - name: Update PR description - run: | - pixi run ./scripts/ci/update_pr_body.py \ - --github-token '${{ secrets.GITHUB_TOKEN }}' \ - --github-repository '${{ github.repository }}' \ - --pr-number '${{ inputs.PR_NUMBER }}' diff --git a/.github/workflows/reusable_upload_web.yml b/.github/workflows/reusable_upload_web.yml index 91df5ae54053..f7011908a850 100644 --- a/.github/workflows/reusable_upload_web.yml +++ b/.github/workflows/reusable_upload_web.yml @@ -36,8 +36,9 @@ defaults: shell: bash permissions: - contents: "read" + contents: "write" id-token: "write" + pull-requests: "write" jobs: upload-web: @@ -137,3 +138,37 @@ jobs: process_gcloudignore: false headers: |- cache-control: no-cache, max-age=0 + + - name: Status comment + if: success() && github.event_name == 'pull_request' + # https://github.com/mshick/add-pr-comment + uses: mshick/add-pr-comment@v2.8.2 + with: + message-id: "web-viewer-build-status" + repo-token: ${{ secrets.GITHUB_TOKEN }} + message: | + Web viewer built successfully. If applicable, you should also test it: + + - [ ] I have tested the web viewer + + | Result | Commit | Link | + | ------ | ------- | ----- | + | ✅ | ${{ steps.get-sha.outputs.sha }} | https://rerun.io/viewer/pr/${{ github.event.pull_request.number }} | + + Note: This comment is updated whenever you push a commit. + + - name: Status comment + if: failure() && github.event_name == 'pull_request' + # https://github.com/mshick/add-pr-comment + uses: mshick/add-pr-comment@v2.8.2 + with: + message-id: "web-viewer-build-status" + repo-token: ${{ secrets.GITHUB_TOKEN }} + message: | + Web viewer failed to build. + + | Result | Commit | Link | + | ------ | ------- | ----- | + | ❌ | ${{ steps.get-sha.outputs.sha }} | https://rerun.io/viewer/pr/${{ github.event.pull_request.number }} | + + Note: This comment is updated whenever you push a commit. diff --git a/pixi.toml b/pixi.toml index 9cd03413cdb8..cebdd5a20fe6 100644 --- a/pixi.toml +++ b/pixi.toml @@ -456,7 +456,7 @@ fd-find = ">=10.1.0" # Used by `cpp-fmt` to find C++ files flatbuffers = ">=23" gitignore-parser = ">=0.1.9" gitpython = ">=3.1.40" -jinja2 = ">=3.1.3,<3.2" # For `update_pr_bodies.py`, `generate_pr_summary.py`, `build_screenshot_compare.py` and other utilities that build websites. +jinja2 = ">=3.1.3,<3.2" # For `build_screenshot_compare.py` and other utilities that build websites. mypy = "1.8.0" nasm = ">=2.16" # Required by https://github.com/memorysafety/rav1d for native video support ninja = "1.11.1" @@ -480,7 +480,7 @@ cryptography = "==38.0.4" # For `upload_image.py` google-cloud-storage = "==2.9.0" # For `upload_image.py` hatch = "==1.12.0" # For `rerun_notebook` Pillow = "==10.0.0" # For `thumbnails.py` -pygithub = "==1.59.0" # Among others for `generate_pr_summary.py`, `sync_release_assets.py`, `update_pr_body.py`. +pygithub = "==1.59.0" # Among others for `sync_release_assets.py`. requests = ">=2.31,<3" # For `thumbnails.py` & `upload_image.py` types-Deprecated = "==1.2.9.2" # Type hint stubs types-requests = ">=2.31,<3" # Type hint stubs diff --git a/scripts/ci/check_pr_checkboxes.py b/scripts/ci/check_pr_checkboxes.py index 0055f5b90b7e..e1df74bed2d1 100755 --- a/scripts/ci/check_pr_checkboxes.py +++ b/scripts/ci/check_pr_checkboxes.py @@ -7,6 +7,16 @@ from github import Github +def get_unchecked_checkboxes(s: str) -> list[str]: + s = s.lower() + lines: list[str] = [] + for line in s.splitlines(): + if "* [ ]" in line or "- [ ]" in line: + lines.append(line) + + return lines + + def main() -> None: parser = argparse.ArgumentParser(description="Generate a PR summary page") parser.add_argument("--github-token", required=True, help="GitHub token") @@ -17,17 +27,16 @@ def main() -> None: gh = Github(args.github_token) repo = gh.get_repo(args.github_repository) pr = repo.get_pull(args.pr_number) - if not pr.body: - # body is empty - print("Don't delete the PR description") - exit(1) latest_commit = pr.get_commits().reversed[0] print(f"Latest commit: {latest_commit.sha}") - body_lower = pr.body.lower() - if "* [ ]" in body_lower or "- [ ]" in body_lower: - print("PR contains unchecked checkboxes") + checkboxes = get_unchecked_checkboxes(pr.body) + + if len(checkboxes) != 0: + print("Unchecked checkboxes found:") + for checkbox in checkboxes: + print(f" {checkbox}") exit(1) diff --git a/scripts/ci/generate_pr_summary.py b/scripts/ci/generate_pr_summary.py deleted file mode 100644 index a90bf7668e0a..000000000000 --- a/scripts/ci/generate_pr_summary.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -Script to generate a PR summary page. - -This script combines the GitHub and google cloud storage APIs -to find and link to the builds associated with a given PR. - -This is expected to be run by the `reusable_pr_summary.yml` GitHub workflow. -""" - -from __future__ import annotations - -import argparse -import io -import os -from typing import Any - -from github import Github -from google.cloud import storage -from jinja2 import Template - - -def generate_pr_summary(github_token: str, github_repository: str, pr_number: int, upload: bool) -> None: - # Initialize the GitHub and GCS clients - gh = Github(github_token) - gcs_client = storage.Client() - - # Get the list of commits associated with the PR - repo = gh.get_repo(github_repository) - pull = repo.get_pull(pr_number) - all_commits = [commit.sha for commit in pull.get_commits()] - all_commits.reverse() - - # Prepare the found_builds list - found_builds = [] - viewer_bucket = gcs_client.bucket("rerun-web-viewer") - builds_bucket = gcs_client.bucket("rerun-builds") - - for commit in all_commits: - commit_short = commit[:7] - print(f"Checking commit: {commit_short}...") - - found: dict[str, Any] = {} - - # Check if there is a hosted app for the current commit - app_blob = viewer_bucket.blob(f"commit/{commit_short}/index.html") - if app_blob.exists(): - print(f"Found web assets commit: {commit_short}") - found["hosted_app"] = f"https://rerun.io/viewer/commit/{commit_short}" - - # Check if there are rerun_c libraries - rerun_libraries_blobs = [ - builds_bucket.blob(f"commit/{commit_short}/rerun_c/windows-x64/rerun_c.lib"), - builds_bucket.blob(f"commit/{commit_short}/rerun_c/linux-arm64/librerun_c.a"), - builds_bucket.blob(f"commit/{commit_short}/rerun_c/linux-x64/librerun_c.a"), - builds_bucket.blob(f"commit/{commit_short}/rerun_c/macos-arm64/librerun_c.a"), - builds_bucket.blob(f"commit/{commit_short}/rerun_c/macos-x64/librerun_c.a"), - ] - rerun_libraries = [f"https://build.rerun.io/{blob.name}" for blob in rerun_libraries_blobs if blob.exists()] - if rerun_libraries: - print(f"Found rerun_c libraries for commit: {commit_short}") - found["rerun_c_libraries"] = rerun_libraries - - # Check if there are benchmark results - bench_blob = builds_bucket.blob(f"commit/{commit_short}/bench_results.txt") - if bench_blob.exists(): - print(f"Found benchmark results: {commit_short}") - found["bench_results"] = f"https://build.rerun.io/{bench_blob.name}" - - # Check if there are notebook results - notebook_blobs = list(builds_bucket.list_blobs(prefix=f"commit/{commit_short}/notebooks")) - notebooks = [f"https://build.rerun.io/{blob.name}" for blob in notebook_blobs if blob.name.endswith(".html")] - if notebooks: - print(f"Found notebooks for commit: {commit_short}") - found["notebooks"] = notebooks - - # Get the wheel files for the commit - wheel_blobs = list(builds_bucket.list_blobs(prefix=f"commit/{commit_short}/wheels")) - wheels = [f"https://build.rerun.io/{blob.name}" for blob in wheel_blobs if blob.name.endswith(".whl")] - if wheels: - print(f"Found wheels for commit: {commit_short}") - found["wheels"] = wheels - - if found: - found["commit"] = commit_short - found_builds.append(found) - - template_path = os.path.join(os.path.dirname(os.path.relpath(__file__)), "templates/pr_results_summary.html") - - # Render the Jinja template with the found_builds variable - with open(template_path, encoding="utf8") as f: - template = Template(f.read()) - - buffer = io.BytesIO(template.render(found_builds=found_builds, pr_number=pr_number).encode("utf-8")) - buffer.seek(0) - - if upload: - upload_blob = builds_bucket.blob(f"pull_request/{pr_number}/index.html") - print(f"Uploading results to {upload_blob.name}") - upload_blob.upload_from_file(buffer, content_type="text/html") - - -def main() -> None: - parser = argparse.ArgumentParser(description="Generate a PR summary page") - parser.add_argument("--github-token", required=True, help="GitHub token") - parser.add_argument("--github-repository", required=True, help="GitHub repository") - parser.add_argument("--pr-number", required=True, type=int, help="PR number") - parser.add_argument("--upload", action="store_true", help="Upload the summary page to GCS") - args = parser.parse_args() - - generate_pr_summary(args.github_token, args.github_repository, args.pr_number, args.upload) - - -if __name__ == "__main__": - main() diff --git a/scripts/ci/templates/pr_results_summary.html b/scripts/ci/templates/pr_results_summary.html deleted file mode 100644 index 9ed8afa8ae4d..000000000000 --- a/scripts/ci/templates/pr_results_summary.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - Build Summary - - - - -

- Build Summary for - #{{pr_number}} -

- - {% for build in found_builds %} -
-

- Commit: - {{ build.commit }} -

- {% if build.hosted_app %} -
-

Hosted App:

- {{ build.hosted_app }} -
- {% endif %} {% if build.rerun_c_libraries %} -
-

Rerun C build:

- -
- {% endif %} {% if build.bench_results %} -

- Benchmark Results -

- {% endif %} {% if build.notebooks %} -
-

Notebooks:

- -
- {% endif %} {% if build.wheels %} -
-

Wheels:

- -
- {% endif %} -
- {% endfor %} - - diff --git a/scripts/ci/update_pr_body.py b/scripts/ci/update_pr_body.py deleted file mode 100755 index 295f106cda4c..000000000000 --- a/scripts/ci/update_pr_body.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python3 - -""" -Script to update the PR description template. - -This is expected to be run by the `reusable_update_pr_body.yml` GitHub workflow. -""" - -from __future__ import annotations - -import argparse -import logging -import urllib.parse - -from github import Github -from jinja2 import DebugUndefined, select_autoescape -from jinja2.sandbox import SandboxedEnvironment - -# Need to protect code-blocks in the PR template. -# See https://github.com/rerun-io/rerun/issues/3972 -# -# Could not get this to work with jinja-markdown extension so replicating the functionality from: -# https://github.com/digital-land/design-system/commit/14e678ccb3e1e62da18072ccf035f3d0e7467f3c - -CODE_BLOCK_PLACEHOLDER = "{CODE BLOCK PLACEHOLDER}" - - -def index_code_block(lines: list[str]) -> tuple[bool, int, int]: - # Truncate the lines so we can find things like "```rust". - line_starts = [line[0:3] for line in lines] - if "```" in lines: - opening_line = line_starts.index("```") - closing_line = line_starts.index("```", opening_line + 1) - return True, opening_line, closing_line - return False, 0, 0 - - -# extracts code blocks from lines of text -# also replaces code_blocks with a placeholder string -def extract_code_blocks(lines: list[str]) -> list[list[str]]: - code_blocks = [] - has_code_blocks = True - while has_code_blocks: - # Truncate the lines so we can find things like "```rust". - line_starts = [line[0:3] for line in lines] - if "```" in line_starts: - block, op, cl = index_code_block(lines) - if block: - code_blocks.append(lines[op : cl + 1]) - del lines[op + 1 : cl + 1] - lines[op] = CODE_BLOCK_PLACEHOLDER - else: - has_code_blocks = False - else: - has_code_blocks = False - return code_blocks - - -def insert_lines(lines: list[str], lines_to_insert: list[str], start_from: int) -> None: - insert_pt = start_from - for line in lines_to_insert: - lines.insert(insert_pt, line) - insert_pt = insert_pt + 1 - - -def insert_code_blocks(lines: list[str], code_blocks: list[list[str]]) -> list[str]: - has_placeholders = True - while has_placeholders: - if CODE_BLOCK_PLACEHOLDER in lines: - idx = lines.index(CODE_BLOCK_PLACEHOLDER) - try: - block = code_blocks.pop(0) - insert_lines(lines, block, idx + 1) - del lines[idx] - except IndexError: - lines[idx] = "{Error: Couldn't re-insert code-block}" - else: - has_placeholders = False - return lines - - -def encode_uri_component(value: str) -> str: - return urllib.parse.quote(value, safe="") - - -def main() -> None: - logging.getLogger().setLevel(-1) - parser = argparse.ArgumentParser(description="Generate a PR summary page") - parser.add_argument("--github-token", required=True, help="GitHub token") - parser.add_argument("--github-repository", required=True, help="GitHub repository") - parser.add_argument("--pr-number", required=True, type=int, help="PR number") - args = parser.parse_args() - - gh = Github(args.github_token) - repo = gh.get_repo(args.github_repository) - pr = repo.get_pull(args.pr_number) - if not pr.body: - # body is empty - return - - latest_commit = pr.get_commits().reversed[0] - - print(f"Latest commit: {latest_commit.sha}") - - env = SandboxedEnvironment( - autoescape=select_autoescape(), - undefined=DebugUndefined, - ) - env.filters["encode_uri_component"] = encode_uri_component - - new_body = pr.body - - lines = new_body.splitlines() - codeblocks = extract_code_blocks(lines) - text = "\n".join(lines) - - new_body = env.from_string(text).render( - pr={ - "number": args.pr_number, - "branch": pr.head.ref, - "commit": latest_commit.sha, - }, - ) - - lines = new_body.split("\n") - new_body = "\n".join(insert_code_blocks(lines, codeblocks)) - - if new_body != pr.body: - print("updated pr body") - pr.edit(body=new_body) - - -if __name__ == "__main__": - main()