pr_check.yml 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. name: PR-check
  2. on:
  3. pull_request_target:
  4. branches:
  5. - 'main'
  6. - 'stable-*'
  7. - 'stream-nb-*'
  8. paths-ignore:
  9. - 'ydb/docs/**'
  10. - '*'
  11. types:
  12. - 'opened'
  13. - 'synchronize'
  14. - 'reopened'
  15. - 'labeled'
  16. concurrency:
  17. group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
  18. cancel-in-progress: true
  19. jobs:
  20. check-running-allowed:
  21. if: ${{vars.CHECKS_SWITCH != '' && fromJSON(vars.CHECKS_SWITCH).pr_check == true}}
  22. runs-on: ubuntu-latest
  23. outputs:
  24. result: ${{ steps.check-ownership-membership.outputs.result == 'true' && steps.check-is-mergeable.outputs.result == 'true' }}
  25. commit_sha: ${{ steps.check-is-mergeable.outputs.commit_sha }}
  26. steps:
  27. - name: Check if running tests is allowed
  28. id: check-ownership-membership
  29. uses: actions/github-script@v6
  30. with:
  31. github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
  32. script: |
  33. const labels = context.payload.pull_request.labels;
  34. const okToTestLabel = labels.find(
  35. label => label.name == 'ok-to-test'
  36. );
  37. console.log("okToTestLabel=%o", okToTestLabel !== undefined);
  38. if (okToTestLabel !== undefined) {
  39. return true;
  40. }
  41. // This is used primarily in forks. Repository owner
  42. // should be allowed to run anything.
  43. const userLogin = context.payload.pull_request.user.login;
  44. // How to interpret membership status code:
  45. // https://docs.github.com/rest/collaborators/collaborators#check-if-a-user-is-a-repository-collaborator
  46. const isRepoCollaborator = async function () {
  47. try {
  48. const response = await github.rest.repos.checkCollaborator({
  49. owner: context.payload.repository.owner.login,
  50. repo: context.payload.repository.name,
  51. username: userLogin,
  52. });
  53. return response.status == 204;
  54. } catch (error) {
  55. if (error.status && error.status == 404) {
  56. return false;
  57. }
  58. throw error;
  59. }
  60. }
  61. if (context.payload.repository.owner.login == userLogin) {
  62. console.log("You are the repository owner!");
  63. return true;
  64. }
  65. if (await isRepoCollaborator()) {
  66. console.log("You are a collaborator!");
  67. return true;
  68. }
  69. return false;
  70. - name: comment-if-waiting-on-ok
  71. if: steps.check-ownership-membership.outputs.result == 'false' &&
  72. github.event.action == 'opened'
  73. uses: actions/github-script@v6
  74. with:
  75. script: |
  76. let externalContributorLabel = 'external';
  77. github.rest.issues.createComment({
  78. issue_number: context.issue.number,
  79. owner: context.repo.owner,
  80. repo: context.repo.repo,
  81. 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!'
  82. });
  83. github.rest.issues.addLabels({
  84. ...context.repo,
  85. issue_number: context.issue.number,
  86. labels: [externalContributorLabel]
  87. });
  88. - name: cleanup-test-label
  89. uses: actions/github-script@v6
  90. with:
  91. script: |
  92. let labelsToRemove = ['ok-to-test', 'rebase-and-check'];
  93. const prNumber = context.payload.pull_request.number;
  94. const prLabels = new Set(context.payload.pull_request.labels.map(l => l.name));
  95. for await (const label of labelsToRemove.filter(l => prLabels.has(l))) {
  96. core.info(`remove label=${label} for pr=${prNumber}`);
  97. try {
  98. const result = await github.rest.issues.removeLabel({
  99. ...context.repo,
  100. issue_number: prNumber,
  101. name: label
  102. });
  103. } catch(error) {
  104. // ignore the 404 error that arises
  105. // when the label did not exist for the
  106. // organization member
  107. if (error.status && error.status != 404) {
  108. throw error;
  109. }
  110. }
  111. }
  112. - name: check is mergeable
  113. id: check-is-mergeable
  114. if: steps.check-ownership-membership.outputs.result == 'true'
  115. uses: actions/github-script@v6
  116. with:
  117. result-encoding: string
  118. script: |
  119. let pr = context.payload.pull_request;
  120. const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
  121. const header = `<!-- merge pr=${pr.number} -->\n`;
  122. const fail_msg = header + ':red_circle: Unable to merge your PR into the `main` branch. '
  123. + 'Please rebase or merge it with the `main` branch.'
  124. let i = 0;
  125. while (pr.mergeable == null || i >= 60) {
  126. console.log("get pull-request status");
  127. let result = await github.rest.pulls.get({
  128. ...context.repo,
  129. pull_number: pr.number
  130. })
  131. pr = result.data;
  132. if (pr.mergeable == null) {
  133. await delay(5000);
  134. }
  135. i += 1;
  136. }
  137. console.log("pr.mergeable=%o", pr.mergeable);
  138. const { data: comments } = await github.rest.issues.listComments({
  139. issue_number: context.issue.number,
  140. owner: context.repo.owner,
  141. repo: context.repo.repo
  142. });
  143. const commentToUpdate = comments.find(comment => comment.body.startsWith(header));
  144. if (pr.mergeable === false) {
  145. let commentParams = {
  146. ...context.repo,
  147. issue_number: context.issue.number,
  148. body: fail_msg
  149. };
  150. if (commentToUpdate) {
  151. await github.rest.issues.updateComment({
  152. ...commentParams,
  153. comment_id: commentToUpdate.id,
  154. });
  155. } else {
  156. await github.rest.issues.createComment({...commentParams});
  157. }
  158. core.setFailed("Merge conflict detected");
  159. return false;
  160. } else if (commentToUpdate) {
  161. await github.rest.issues.deleteComment({
  162. ...context.repo,
  163. issue_number: context.issue.number,
  164. comment_id: commentToUpdate.id,
  165. });
  166. }
  167. core.info(`commit_sha=${pr.commit_sha}`);
  168. core.setOutput('commit_sha', pr.merge_commit_sha);
  169. return true;
  170. build_and_test:
  171. needs:
  172. - check-running-allowed
  173. if: needs.check-running-allowed.outputs.result == 'true' && needs.check-running-allowed.outputs.commit_sha != ''
  174. strategy:
  175. fail-fast: false
  176. matrix:
  177. build_preset: ["relwithdebinfo", "release-asan", "release-clang14"]
  178. runs-on: [ self-hosted, auto-provisioned, "${{ format('build-preset-{0}', matrix.build_preset) }}" ]
  179. name: Build and test ${{ matrix.build_preset }}
  180. steps:
  181. - name: Checkout
  182. uses: actions/checkout@v3
  183. with:
  184. ref: ${{ needs.check-running-allowed.outputs.commit_sha }}
  185. fetch-depth: 2
  186. - name: Build and test
  187. uses: ./.github/actions/build_and_test_ya
  188. with:
  189. build_preset: ${{ matrix.build_preset }}
  190. build_target: "ydb/"
  191. increment: true
  192. run_tests: ${{ contains(fromJSON('["relwithdebinfo", "release-asan"]'), matrix.build_preset) }}
  193. test_size: "small,medium"
  194. test_type: "unittest,py3test,py2test,pytest"
  195. test_threads: 52
  196. put_build_results_to_cache: true
  197. run_tests_if_build_fails: false
  198. secs: ${{ format('{{"TESTMO_TOKEN":"{0}","AWS_KEY_ID":"{1}","AWS_KEY_VALUE":"{2}","REMOTE_CACHE_USERNAME":"{3}","REMOTE_CACHE_PASSWORD":"{4}"}}',
  199. secrets.TESTMO_TOKEN, secrets.AWS_KEY_ID, secrets.AWS_KEY_VALUE, secrets.REMOTE_CACHE_USERNAME, secrets.REMOTE_CACHE_PASSWORD ) }}
  200. vars: ${{ format('{{"AWS_BUCKET":"{0}","AWS_ENDPOINT":"{1}","REMOTE_CACHE_URL":"{2}","TESTMO_URL":"{3}","TESTMO_PROJECT_ID":"{4}"}}',
  201. vars.AWS_BUCKET, vars.AWS_ENDPOINT, vars.REMOTE_CACHE_URL_YA, vars.TESTMO_URL, vars.TESTMO_PROJECT_ID ) }}