name: PR-check on: pull_request_target: branches: - 'main' - 'stable-*' paths-ignore: - 'ydb/docs/**' - '*' types: - 'opened' - 'synchronize' - 'reopened' - 'labeled' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: check-running-allowed: if: ${{vars.CHECKS_SWITCH != '' && fromJSON(vars.CHECKS_SWITCH).pr_check == true}} runs-on: ubuntu-latest outputs: result: ${{ steps.check-ownership-membership.outputs.result == 'true' && steps.check-is-mergeable.outputs.result == 'true' }} commit_sha: ${{ steps.check-is-mergeable.outputs.commit_sha }} steps: - name: Check if running tests is allowed id: check-ownership-membership uses: actions/github-script@v6 with: github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }} script: | const labels = context.payload.pull_request.labels; const okToTestLabel = labels.find( label => label.name == 'ok-to-test' ); console.log("okToTestLabel=%o", okToTestLabel !== undefined); if (okToTestLabel !== undefined) { return true; } // This is used primarily in forks. Repository owner // should be allowed to run anything. const userLogin = context.payload.pull_request.user.login; // How to interpret membership status code: // https://docs.github.com/rest/collaborators/collaborators#check-if-a-user-is-a-repository-collaborator const isRepoCollaborator = async function () { try { const response = await github.rest.repos.checkCollaborator({ owner: context.payload.repository.owner.login, repo: context.payload.repository.name, username: userLogin, }); return response.status == 204; } catch (error) { if (error.status && error.status == 404) { return false; } throw error; } } if (context.payload.repository.owner.login == userLogin) { console.log("You are the repository owner!"); return true; } if (await isRepoCollaborator()) { console.log("You are a collaborator!"); return true; } return false; - name: comment-if-waiting-on-ok if: steps.check-ownership-membership.outputs.result == 'false' && github.event.action == 'opened' uses: actions/github-script@v6 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: 'Hi! Thank you for contributing!\nThe tests on this PR will run after a maintainer adds an `ok-to-test` label to this PR manually. Thank you for your patience!' }); - name: cleanup-test-label uses: actions/github-script@v6 with: script: | let labelsToRemove = ['ok-to-test', 'rebase-and-check']; const prNumber = context.payload.pull_request.number; const prLabels = new Set(context.payload.pull_request.labels.map(l => l.name)); for await (const label of labelsToRemove.filter(l => prLabels.has(l))) { core.info(`remove label=${label} for pr=${prNumber}`); try { const result = await github.rest.issues.removeLabel({ ...context.repo, issue_number: prNumber, name: label }); } catch(error) { // ignore the 404 error that arises // when the label did not exist for the // organization member if (error.status && error.status != 404) { throw error; } } } - name: check is mergeable id: check-is-mergeable if: steps.check-ownership-membership.outputs.result == 'true' uses: actions/github-script@v6 with: result-encoding: string script: | let pr = context.payload.pull_request; const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); const header = `\n`; const fail_msg = header + ':red_circle: Unable to merge your PR into the `main` branch. ' + 'Please rebase or merge it with the `main` branch.' let i = 0; while (pr.mergeable == null || i >= 60) { console.log("get pull-request status"); let result = await github.rest.pulls.get({ ...context.repo, pull_number: pr.number }) pr = result.data; if (pr.mergeable == null) { await delay(5000); } i += 1; } console.log("pr.mergeable=%o", pr.mergeable); const { data: comments } = await github.rest.issues.listComments({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo }); const commentToUpdate = comments.find(comment => comment.body.startsWith(header)); if (pr.mergeable === false) { let commentParams = { ...context.repo, issue_number: context.issue.number, body: fail_msg }; if (commentToUpdate) { await github.rest.issues.updateComment({ ...commentParams, comment_id: commentToUpdate.id, }); } else { await github.rest.issues.createComment({...commentParams}); } core.setFailed("Merge conflict detected"); return false; } else if (commentToUpdate) { await github.rest.issues.deleteComment({ ...context.repo, issue_number: context.issue.number, comment_id: commentToUpdate.id, }); } core.info(`commit_sha=${pr.commit_sha}`); core.setOutput('commit_sha', pr.merge_commit_sha); return true; build_and_test: needs: - check-running-allowed if: needs.check-running-allowed.outputs.result == 'true' && needs.check-running-allowed.outputs.commit_sha != '' strategy: fail-fast: false matrix: build_preset: ["relwithdebinfo", "release-asan"] name: Build and test ${{ matrix.build_preset }} uses: ./.github/workflows/build_and_test_ya_provisioned.yml with: build_preset: ${{ matrix.build_preset }} build_target: "ydb/" test_size: "small,medium" test_type: "unittest,py3test,py2test,pytest" test_threads: 52 runner_label: auto-provisioned put_build_results_to_cache: true commit_sha: ${{ needs.check-running-allowed.outputs.commit_sha }} secrets: inherit