yuqi1129 commented on code in PR #13049: URL: https://github.com/apache/gravitino/pull/13049#discussion_r4337455231
########## .github/workflows/fork-ci.yml: ########## @@ -0,0 +1,235 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Run the complete CI entry point from branch pushes in the contributor's repository. +name: Fork CI + +on: + push: + branches: ['**'] + +concurrency: + group: fork-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + eligibility: + name: Resolve fork CI eligibility + # Downstream mirrors can set this repository variable to keep the + # inherited workflow completely dormant. + if: vars.DISABLE_FORK_CI != 'true' + runs-on: ubuntu-slim Review Comment: `ubuntu-slim` is picked up in personal forks as well: the eligibility job ran on it in every fork run I did, for example https://github.com/qqqttt123/gravitino/actions/runs/39581484861/job/119160356453 for this head. The job only calls the API and runs `git merge-tree`, so it stays well under the 15-minute limit. ########## .github/workflows/fork-ci.yml: ########## @@ -0,0 +1,235 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Run the complete CI entry point from branch pushes in the contributor's repository. +name: Fork CI + +on: + push: + branches: ['**'] + +concurrency: + group: fork-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + eligibility: + name: Resolve fork CI eligibility + # Downstream mirrors can set this repository variable to keep the + # inherited workflow completely dormant. + if: vars.DISABLE_FORK_CI != 'true' + runs-on: ubuntu-slim + outputs: + applicable: ${{ steps.resolve.outputs.applicable }} + valid: ${{ steps.resolve.outputs.valid }} + steps: + - name: Resolve repository and pull request + id: resolve + env: + GH_TOKEN: ${{ github.token }} + HEAD_BRANCH: ${{ github.ref_name }} + HEAD_OWNER: ${{ github.repository_owner }} + HEAD_REPOSITORY: ${{ github.repository }} + HEAD_SHA: ${{ github.sha }} + run: | + set -euo pipefail + + echo "applicable=false" >> "${GITHUB_OUTPUT}" + echo "valid=false" >> "${GITHUB_OUTPUT}" + + if [[ "${HEAD_REPOSITORY}" == "apache/gravitino" && \ + ("${GITHUB_REF}" == "refs/heads/main" || "${GITHUB_REF}" == refs/heads/branch-*) ]]; then + echo "applicable=true" >> "${GITHUB_OUTPUT}" + echo "base_ref=${GITHUB_REF_NAME}" >> "${GITHUB_OUTPUT}" + echo "valid=true" >> "${GITHUB_OUTPUT}" + exit 0 + fi + + repository_response="$(curl --fail-with-body --silent --show-error \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "${GITHUB_API_URL}/repos/${HEAD_REPOSITORY}")" + source_repository="$(jq -r '.source.full_name // empty' <<< "${repository_response}")" + + if [[ "${HEAD_REPOSITORY}" != "apache/gravitino" && \ + "${source_repository}" != "apache/gravitino" ]]; then + echo "This repository is not in the apache/gravitino fork network; Fork CI is dormant." + exit 0 + fi + + echo "applicable=true" >> "${GITHUB_OUTPUT}" + matches="[]" + match_count=0 + for attempt in 1 2 3; do + response="$(curl --fail-with-body --silent --show-error \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer ${GH_TOKEN}" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + --get \ + --data-urlencode "state=open" \ + --data-urlencode "head=${HEAD_OWNER}:${HEAD_BRANCH}" \ + --data-urlencode "per_page=10" \ + "https://api.github.com/repos/apache/gravitino/pulls")" + matches="$(jq -r \ + --arg head_sha "${HEAD_SHA}" \ + --arg head_repository "${HEAD_REPOSITORY}" \ + '[.[] | select(.head.sha == $head_sha and .head.repo.full_name == $head_repository)]' \ + <<< "${response}")" + match_count="$(jq -r 'length' <<< "${matches}")" + if [[ "${match_count}" == "1" ]]; then + break + fi + if [[ "${attempt}" != "3" ]]; then + sleep 3 + fi + done + + if [[ "${match_count}" != "1" ]]; then + echo "::error::Expected one open apache/gravitino pull request for ${HEAD_REPOSITORY}:${HEAD_BRANCH} at ${HEAD_SHA}, found ${match_count}." + exit 0 + fi + + echo "base_ref=$(jq -r '.[0].base.ref' <<< "${matches}")" >> "${GITHUB_OUTPUT}" + echo "valid=true" >> "${GITHUB_OUTPUT}" + + - name: Reject an unmatched Apache fork branch Review Comment: No longer fails. A push without an open PR now tests against the inferred base (the default branch or one of the latest release branches, whichever the branch is fewest commits ahead of), and the result is mirrored once the PR is opened. Syncing a fork's `main` is such a push and passes (https://github.com/yuqi1129/gravitino/actions/runs/39581500994 ran the full suites on current `main`). Branches in `apache/gravitino` without a PR, such as `cherry-pick-*`, stay dormant. ########## .github/workflows/build.yml: ########## @@ -216,36 +214,30 @@ jobs: ./gradlew "${gradle_args[@]}" - name: Fetch base branch for coverage diff - if: github.event_name == 'pull_request' - run: git fetch origin ${{ github.base_ref }} --depth=1 + if: steps.build-checkout.outputs.synced == 'true' + run: git fetch origin ${{ steps.build-checkout.outputs.base-ref }} --depth=1 - name: Generate Coverage Report id: coverage - if: github.event_name == 'pull_request' + if: steps.build-checkout.outputs.synced == 'true' run: | python3 dev/ci/jacoco_report.py \ - --base-ref "${{ github.base_ref }}" \ - --head-sha "${{ github.event.pull_request.head.sha }}" \ - --repo-url "${{ github.event.pull_request.head.repo.html_url }}" \ + --base-ref "${{ steps.build-checkout.outputs.base-ref }}" \ + --head-sha "${{ github.sha }}" \ + --repo-url "${{ github.server_url }}/${{ github.repository }}" \ --min-overall 40 \ --min-changed 60 \ --output coverage-report.md - - name: Save PR number - if: github.event_name == 'pull_request' && steps.coverage.outputs.has_reports == 'true' - run: echo "${{ github.event.pull_request.number }}" > pr-number.txt - - name: Upload Coverage Report - if: github.event_name == 'pull_request' && steps.coverage.outputs.has_reports == 'true' + if: steps.build-checkout.outputs.synced == 'true' && steps.coverage.outputs.has_reports == 'true' uses: actions/upload-artifact@v7 with: name: coverage-report - path: | - coverage-report.md - pr-number.txt + path: coverage-report.md Review Comment: Kept on purpose: `branch-*` PRs still run `build.yml` on `pull_request` in `apache/gravitino`, and `workflow_run` uses the file on `main`, so `coverage-comment.yml` keeps serving them. For PRs to `main`, coverage is in the `build / coverage` job log and the `coverage-report` artifact in the fork; this is documented in `CONTRIBUTING.md` (Known limitations of Fork CI). ########## .github/workflows/fork-ci-status-update.yml: ########## @@ -0,0 +1,111 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. +# +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Update Fork CI Status + +on: + schedule: + - cron: '*/15 * * * *' + +permissions: + actions: read + checks: write + pull-requests: read + +jobs: + update: + name: Update fork CI checks + if: github.repository == 'apache/gravitino' + runs-on: ubuntu-slim + steps: + - name: Synchronize fork CI checks + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prs = await github.paginate( + 'GET /repos/{owner}/{repo}/pulls', + { owner: context.repo.owner, repo: context.repo.repo, state: 'open', per_page: 100 }); + + for (const pr of prs) { + if (!pr.head.repo) continue; + const owner = pr.head.repo.owner.login; + const repo = pr.head.repo.name; + let runs; + try { + runs = await github.paginate( Review Comment: Addressed. The sweep reads fork runs with `head_sha`, wraps each PR in its own `try/catch`, and keeps the previous sweep's state, so settled PRs cost no calls. Since `e062791` it runs every 5 minutes, but only running checks are read on every sweep; other PRs at most every 15 minutes. In the sandbox a sweep took 4 to 8 calls for 3 PRs, and each sweep logs the `GITHUB_TOKEN` rate limit so we can watch it after merging. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
