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