ghcr_runtime.yml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. # Workflow that builds, tests and then pushes the runtime docker images to the ghcr.io repository
  2. name: Build, Test and Publish RT Image
  3. # Only run one workflow of the same group at a time.
  4. # There can be at most one running and one pending job in a concurrency group at any time.
  5. concurrency:
  6. group: ${{ github.workflow }}-${{ github.ref }}
  7. cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
  8. # Always run on "main"
  9. # Always run on tags
  10. # Always run on PRs
  11. # Can also be triggered manually
  12. on:
  13. push:
  14. branches:
  15. - main
  16. tags:
  17. - '*'
  18. pull_request:
  19. workflow_dispatch:
  20. inputs:
  21. reason:
  22. description: 'Reason for manual trigger'
  23. required: true
  24. default: ''
  25. jobs:
  26. # Builds the runtime Docker images
  27. ghcr_build_runtime:
  28. name: Build Image
  29. runs-on: ubuntu-latest
  30. permissions:
  31. contents: read
  32. packages: write
  33. strategy:
  34. matrix:
  35. base_image:
  36. - image: 'nikolaik/python-nodejs:python3.11-nodejs22'
  37. tag: nikolaik
  38. steps:
  39. - name: Checkout
  40. uses: actions/checkout@v4
  41. - name: Free Disk Space (Ubuntu)
  42. uses: jlumbroso/free-disk-space@main
  43. with:
  44. # this might remove tools that are actually needed,
  45. # if set to "true" but frees about 6 GB
  46. tool-cache: true
  47. # all of these default to true, but feel free to set to
  48. # "false" if necessary for your workflow
  49. android: true
  50. dotnet: true
  51. haskell: true
  52. large-packages: true
  53. docker-images: false
  54. swap-storage: true
  55. - name: Set up QEMU
  56. uses: docker/setup-qemu-action@v3
  57. - name: Login to GHCR
  58. uses: docker/login-action@v3
  59. with:
  60. registry: ghcr.io
  61. username: ${{ github.repository_owner }}
  62. password: ${{ secrets.GITHUB_TOKEN }}
  63. - name: Set up Docker Buildx
  64. id: buildx
  65. uses: docker/setup-buildx-action@v3
  66. - name: Set up Python
  67. uses: actions/setup-python@v5
  68. with:
  69. python-version: '3.11'
  70. - name: Cache Poetry dependencies
  71. uses: actions/cache@v4
  72. with:
  73. path: |
  74. ~/.cache/pypoetry
  75. ~/.virtualenvs
  76. key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
  77. restore-keys: |
  78. ${{ runner.os }}-poetry-
  79. - name: Install poetry via pipx
  80. run: pipx install poetry
  81. - name: Install Python dependencies using Poetry
  82. run: make install-python-dependencies
  83. - name: Create source distribution and Dockerfile
  84. run: poetry run python3 openhands/runtime/utils/runtime_build.py --base_image ${{ matrix.base_image.image }} --build_folder containers/runtime --force_rebuild
  85. - name: Build and push runtime image ${{ matrix.base_image.image }}
  86. if: github.event.pull_request.head.repo.fork != true
  87. run: |
  88. ./containers/build.sh runtime ${{ github.repository_owner }} --push ${{ matrix.base_image.tag }}
  89. # Forked repos can't push to GHCR, so we need to upload the image as an artifact
  90. - name: Build runtime image ${{ matrix.base_image.image }} for fork
  91. if: github.event.pull_request.head.repo.fork
  92. uses: docker/build-push-action@v6
  93. with:
  94. tags: ghcr.io/all-hands-ai/runtime:${{ github.sha }}-${{ matrix.base_image.tag }}
  95. outputs: type=docker,dest=/tmp/runtime-${{ matrix.base_image.tag }}.tar
  96. context: containers/runtime
  97. - name: Upload runtime image for fork
  98. if: github.event.pull_request.head.repo.fork
  99. uses: actions/upload-artifact@v4
  100. with:
  101. name: runtime-${{ matrix.base_image.tag }}
  102. path: /tmp/runtime-${{ matrix.base_image.tag }}.tar
  103. # Run unit tests with the EventStream runtime Docker images as root
  104. test_runtime_root:
  105. name: RT Unit Tests (Root)
  106. needs: [ghcr_build_runtime]
  107. runs-on: ubuntu-latest
  108. strategy:
  109. fail-fast: false
  110. matrix:
  111. base_image: ['nikolaik']
  112. steps:
  113. - uses: actions/checkout@v4
  114. - name: Free Disk Space (Ubuntu)
  115. uses: jlumbroso/free-disk-space@main
  116. with:
  117. # this might remove tools that are actually needed,
  118. # if set to "true" but frees about 6 GB
  119. tool-cache: true
  120. # all of these default to true, but feel free to set to
  121. # "false" if necessary for your workflow
  122. android: true
  123. dotnet: true
  124. haskell: true
  125. large-packages: true
  126. docker-images: false
  127. swap-storage: true
  128. - name: Set up Docker Buildx
  129. id: buildx
  130. uses: docker/setup-buildx-action@v3
  131. # Forked repos can't push to GHCR, so we need to download the image as an artifact
  132. - name: Download runtime image for fork
  133. if: github.event.pull_request.head.repo.fork
  134. uses: actions/download-artifact@v4
  135. with:
  136. name: runtime-${{ matrix.base_image }}
  137. path: /tmp
  138. - name: Load runtime image for fork
  139. if: github.event.pull_request.head.repo.fork
  140. run: |
  141. docker load --input /tmp/runtime-${{ matrix.base_image }}.tar
  142. - name: Cache Poetry dependencies
  143. uses: actions/cache@v4
  144. with:
  145. path: |
  146. ~/.cache/pypoetry
  147. ~/.virtualenvs
  148. key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
  149. restore-keys: |
  150. ${{ runner.os }}-poetry-
  151. - name: Set up Python
  152. uses: actions/setup-python@v5
  153. with:
  154. python-version: '3.11'
  155. - name: Install poetry via pipx
  156. run: pipx install poetry
  157. - name: Install Python dependencies using Poetry
  158. run: make install-python-dependencies
  159. - name: Run runtime tests
  160. run: |
  161. # We install pytest-xdist in order to run tests across CPUs
  162. poetry run pip install pytest-xdist
  163. # Install to be able to retry on failures for flaky tests
  164. poetry run pip install pytest-rerunfailures
  165. image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
  166. image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
  167. SKIP_CONTAINER_LOGS=true \
  168. TEST_RUNTIME=eventstream \
  169. SANDBOX_USER_ID=$(id -u) \
  170. SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
  171. TEST_IN_CI=true \
  172. RUN_AS_OPENHANDS=false \
  173. poetry run pytest -n 3 -raR --reruns 1 --reruns-delay 3 --cov=agenthub --cov=openhands --cov-report=xml -s ./tests/runtime
  174. - name: Upload coverage to Codecov
  175. uses: codecov/codecov-action@v4
  176. env:
  177. CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  178. # Run unit tests with the EventStream runtime Docker images as openhands user
  179. test_runtime_oh:
  180. name: RT Unit Tests (openhands)
  181. runs-on: ubuntu-latest
  182. needs: [ghcr_build_runtime]
  183. strategy:
  184. matrix:
  185. base_image: ['nikolaik']
  186. steps:
  187. - uses: actions/checkout@v4
  188. - name: Free Disk Space (Ubuntu)
  189. uses: jlumbroso/free-disk-space@main
  190. with:
  191. # this might remove tools that are actually needed,
  192. # if set to "true" but frees about 6 GB
  193. tool-cache: true
  194. # all of these default to true, but feel free to set to
  195. # "false" if necessary for your workflow
  196. android: true
  197. dotnet: true
  198. haskell: true
  199. large-packages: true
  200. docker-images: false
  201. swap-storage: true
  202. - name: Set up Docker Buildx
  203. id: buildx
  204. uses: docker/setup-buildx-action@v3
  205. # Forked repos can't push to GHCR, so we need to download the image as an artifact
  206. - name: Download runtime image for fork
  207. if: github.event.pull_request.head.repo.fork
  208. uses: actions/download-artifact@v4
  209. with:
  210. name: runtime-${{ matrix.base_image }}
  211. path: /tmp
  212. - name: Load runtime image for fork
  213. if: github.event.pull_request.head.repo.fork
  214. run: |
  215. docker load --input /tmp/runtime-${{ matrix.base_image }}.tar
  216. - name: Cache Poetry dependencies
  217. uses: actions/cache@v4
  218. with:
  219. path: |
  220. ~/.cache/pypoetry
  221. ~/.virtualenvs
  222. key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
  223. restore-keys: |
  224. ${{ runner.os }}-poetry-
  225. - name: Set up Python
  226. uses: actions/setup-python@v5
  227. with:
  228. python-version: '3.11'
  229. - name: Install poetry via pipx
  230. run: pipx install poetry
  231. - name: Install Python dependencies using Poetry
  232. run: make install-python-dependencies
  233. - name: Run runtime tests
  234. run: |
  235. # We install pytest-xdist in order to run tests across CPUs
  236. poetry run pip install pytest-xdist
  237. # Install to be able to retry on failures for flaky tests
  238. poetry run pip install pytest-rerunfailures
  239. image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
  240. image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
  241. SKIP_CONTAINER_LOGS=true \
  242. TEST_RUNTIME=eventstream \
  243. SANDBOX_USER_ID=$(id -u) \
  244. SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
  245. TEST_IN_CI=true \
  246. RUN_AS_OPENHANDS=true \
  247. poetry run pytest -n 3 -raR --reruns 1 --reruns-delay 3 --cov=agenthub --cov=openhands --cov-report=xml -s ./tests/runtime
  248. - name: Upload coverage to Codecov
  249. uses: codecov/codecov-action@v4
  250. env:
  251. CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  252. # Run integration tests with the eventstream runtime Docker image
  253. runtime_integration_tests_on_linux:
  254. name: RT Integration Tests (Linux)
  255. runs-on: ubuntu-latest
  256. needs: [ghcr_build_runtime]
  257. strategy:
  258. fail-fast: false
  259. matrix:
  260. base_image: ['nikolaik']
  261. steps:
  262. - uses: actions/checkout@v4
  263. - name: Free Disk Space (Ubuntu)
  264. uses: jlumbroso/free-disk-space@main
  265. with:
  266. # this might remove tools that are actually needed,
  267. # if set to "true" but frees about 6 GB
  268. tool-cache: true
  269. # all of these default to true, but feel free to set to
  270. # "false" if necessary for your workflow
  271. android: true
  272. dotnet: true
  273. haskell: true
  274. large-packages: true
  275. docker-images: false
  276. swap-storage: true
  277. - name: Set up Docker Buildx
  278. id: buildx
  279. uses: docker/setup-buildx-action@v3
  280. # Forked repos can't push to GHCR, so we need to download the image as an artifact
  281. - name: Download runtime image for fork
  282. if: github.event.pull_request.head.repo.fork
  283. uses: actions/download-artifact@v4
  284. with:
  285. name: runtime-${{ matrix.base_image }}
  286. path: /tmp
  287. - name: Load runtime image for fork
  288. if: github.event.pull_request.head.repo.fork
  289. run: |
  290. docker load --input /tmp/runtime-${{ matrix.base_image }}.tar
  291. - name: Cache Poetry dependencies
  292. uses: actions/cache@v4
  293. with:
  294. path: |
  295. ~/.cache/pypoetry
  296. ~/.virtualenvs
  297. key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
  298. restore-keys: |
  299. ${{ runner.os }}-poetry-
  300. - name: Set up Python
  301. uses: actions/setup-python@v5
  302. with:
  303. python-version: '3.11'
  304. - name: Install poetry via pipx
  305. run: pipx install poetry
  306. - name: Install Python dependencies using Poetry
  307. run: make install-python-dependencies
  308. - name: Run integration tests
  309. run: |
  310. image_name=ghcr.io/${{ github.repository_owner }}/runtime:${{ github.sha }}-${{ matrix.base_image }}
  311. image_name=$(echo $image_name | tr '[:upper:]' '[:lower:]')
  312. TEST_RUNTIME=eventstream \
  313. SANDBOX_USER_ID=$(id -u) \
  314. SANDBOX_RUNTIME_CONTAINER_IMAGE=$image_name \
  315. TEST_IN_CI=true \
  316. TEST_ONLY=true \
  317. ./tests/integration/regenerate.sh
  318. - name: Upload coverage to Codecov
  319. uses: codecov/codecov-action@v4
  320. env:
  321. CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
  322. # The two following jobs (named identically) are to check whether all the runtime tests have passed as the
  323. # "All Runtime Tests Passed" is a required job for PRs to merge
  324. # Due to this bug: https://github.com/actions/runner/issues/2566, we want to create a job that runs when the
  325. # prerequisites have been cancelled or failed so merging is disallowed, otherwise Github considers "skipped" as "success"
  326. runtime_tests_check_success:
  327. name: All Runtime Tests Passed
  328. if: ${{ !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
  329. runs-on: ubuntu-latest
  330. needs: [test_runtime_root, test_runtime_oh, runtime_integration_tests_on_linux]
  331. steps:
  332. - name: All tests passed
  333. run: echo "All runtime tests have passed successfully!"
  334. runtime_tests_check_fail:
  335. name: All Runtime Tests Passed
  336. if: ${{ cancelled() || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
  337. runs-on: ubuntu-latest
  338. needs: [test_runtime_root, test_runtime_oh, runtime_integration_tests_on_linux]
  339. steps:
  340. - name: Some tests failed
  341. run: |
  342. echo "Some runtime tests failed or were cancelled"
  343. exit 1