docker.yaml 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. ---
  2. name: Build Docker images
  3. on:
  4. pull_request:
  5. branches:
  6. - main
  7. push:
  8. branches:
  9. - main
  10. tags:
  11. - v*.*.*
  12. workflow_dispatch:
  13. inputs:
  14. version:
  15. description: 'FrankenPHP version'
  16. required: false
  17. type: string
  18. schedule:
  19. - cron: '0 4 * * *'
  20. env:
  21. IMAGE_NAME: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && 'dunglas/frankenphp' || 'dunglas/frankenphp-dev' }}
  22. LATEST: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/')) && '0' || '1' }}
  23. jobs:
  24. prepare:
  25. runs-on: ubuntu-latest
  26. outputs:
  27. # Push if it's a scheduled job, a tag, or if we're committing to the main branch
  28. push: ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.version) || startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request')) && true || false }}
  29. variants: ${{ steps.matrix.outputs.variants }}
  30. platforms: ${{ steps.matrix.outputs.platforms }}
  31. metadata: ${{ steps.matrix.outputs.metadata }}
  32. php_version: ${{ steps.check.outputs.php_version }}
  33. php82_version: ${{ steps.check.outputs.php82_version }}
  34. php83_version: ${{ steps.check.outputs.php83_version }}
  35. skip: ${{ steps.check.outputs.skip }}
  36. ref: ${{ steps.check.outputs.ref || (github.event_name == 'workflow_dispatch' && inputs.version) || '' }}
  37. steps:
  38. -
  39. name: Check PHP versions
  40. id: check
  41. env:
  42. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  43. run: |
  44. PHP_82_LATEST=$(skopeo inspect docker://docker.io/library/php:8.2 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
  45. PHP_83_LATEST=$(skopeo inspect docker://docker.io/library/php:8.3 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
  46. {
  47. echo php_version="${PHP_83_LATEST},${PHP_82_LATEST}"
  48. echo php82_version="${PHP_82_LATEST//./-}"
  49. echo php83_version="${PHP_83_LATEST//./-}"
  50. } >> "${GITHUB_OUTPUT}"
  51. # Check if the Docker images must be rebuilt
  52. if [[ "${GITHUB_EVENT_NAME}" != "schedule" ]]; then
  53. echo skip=false >> "${GITHUB_OUTPUT}"
  54. exit 0
  55. fi
  56. FRANKENPHP_82_LATEST=$(skopeo inspect docker://docker.io/dunglas/frankenphp:latest-php8.2 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
  57. FRANKENPHP_83_LATEST=$(skopeo inspect docker://docker.io/dunglas/frankenphp:latest-php8.3 --override-os linux --override-arch amd64 | jq -r '.Env[] | select(test("^PHP_VERSION=")) | sub("^PHP_VERSION="; "")')
  58. if [[ "${FRANKENPHP_82_LATEST}" == "${PHP_82_LATEST}" ]] && [[ "${FRANKENPHP_83_LATEST}" == "${PHP_83_LATEST}" ]]; then
  59. echo skip=true >> "${GITHUB_OUTPUT}"
  60. exit 0
  61. fi
  62. {
  63. echo ref="$(gh release view --repo dunglas/frankenphp --json tagName --jq '.tagName')"
  64. echo skip=false
  65. } >> "${GITHUB_OUTPUT}"
  66. -
  67. uses: actions/checkout@v4
  68. if: ${{ !fromJson(steps.check.outputs.skip) }}
  69. with:
  70. ref: ${{ steps.check.outputs.ref }}
  71. -
  72. name: Set up Docker Buildx
  73. uses: docker/setup-buildx-action@v3
  74. with:
  75. version: latest
  76. -
  77. name: Create variants matrix
  78. if: ${{ !fromJson(steps.check.outputs.skip) }}
  79. id: matrix
  80. run: |
  81. METADATA="$(docker buildx bake --print | jq -c)"
  82. {
  83. echo metadata="${METADATA}"
  84. echo variants="$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< "${METADATA}")"
  85. echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "${METADATA}")"
  86. } >> "${GITHUB_OUTPUT}"
  87. env:
  88. SHA: ${{ github.sha }}
  89. VERSION: ${{ (github.ref_type == 'tag' && github.ref_name) || steps.check.outputs.ref || github.sha }}
  90. PHP_VERSION: ${{ steps.check.outputs.php_version }}
  91. build:
  92. runs-on: ubuntu-latest
  93. needs:
  94. - prepare
  95. if: ${{ !fromJson(needs.prepare.outputs.skip) }}
  96. strategy:
  97. fail-fast: false
  98. matrix:
  99. variant: ${{ fromJson(needs.prepare.outputs.variants) }}
  100. platform: ${{ fromJson(needs.prepare.outputs.platforms) }}
  101. include:
  102. -
  103. race: ""
  104. qemu: true
  105. -
  106. platform: linux/amd64
  107. qemu: false
  108. race: "-race" # The Go race detector is only supported on amd64
  109. -
  110. platform: linux/386
  111. qemu: false
  112. exclude:
  113. # arm/v6 is only available for Alpine: https://github.com/docker-library/golang/issues/502
  114. -
  115. variant: php-${{ needs.prepare.outputs.php82_version }}-bookworm
  116. platform: linux/arm/v6
  117. -
  118. variant: php-${{ needs.prepare.outputs.php83_version }}-bookworm
  119. platform: linux/arm/v6
  120. steps:
  121. -
  122. uses: actions/checkout@v4
  123. with:
  124. ref: ${{ needs.prepare.outputs.ref }}
  125. -
  126. name: Set up QEMU
  127. if: matrix.qemu
  128. uses: docker/setup-qemu-action@v3
  129. with:
  130. platforms: ${{ matrix.platform }}
  131. -
  132. name: Set up Docker Buildx
  133. uses: docker/setup-buildx-action@v3
  134. with:
  135. platforms: ${{ matrix.platform }}
  136. version: latest
  137. -
  138. name: Login to DockerHub
  139. if: fromJson(needs.prepare.outputs.push)
  140. uses: docker/login-action@v3
  141. with:
  142. username: ${{ secrets.REGISTRY_USERNAME }}
  143. password: ${{ secrets.REGISTRY_PASSWORD }}
  144. -
  145. name: Build
  146. id: build
  147. uses: docker/bake-action@v4
  148. with:
  149. pull: true
  150. load: ${{ !fromJson(needs.prepare.outputs.push) }}
  151. targets: |
  152. builder-${{ matrix.variant }}
  153. runner-${{ matrix.variant }}
  154. # Remove tags to prevent "can't push tagged ref [...] by digest" error
  155. set: |
  156. *.tags=
  157. *.platform=${{ matrix.platform }}
  158. *.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-${{ matrix.platform }}
  159. *.cache-from=type=gha,scope=refs/heads/main-${{ matrix.platform }}
  160. *.cache-to=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-${{ matrix.platform }},ignore-error=true
  161. ${{ fromJson(needs.prepare.outputs.push) && '*.output=type=image,name=dunglas/frankenphp,push-by-digest=true,name-canonical=true,push=true' || '' }}
  162. env:
  163. SHA: ${{ github.sha }}
  164. VERSION: ${{ github.ref_type == 'tag' && github.ref_name || needs.prepare.outputs.ref || github.sha }}
  165. PHP_VERSION: ${{ needs.prepare.outputs.php_version }}
  166. -
  167. # Workaround for https://github.com/actions/runner/pull/2477#issuecomment-1501003600
  168. name: Export metadata
  169. if: fromJson(needs.prepare.outputs.push)
  170. run: |
  171. mkdir -p /tmp/metadata/builder /tmp/metadata/runner
  172. builderDigest=$(jq -r '."builder-${{ matrix.variant }}"."containerimage.digest"' <<< "${METADATA}")
  173. touch "/tmp/metadata/builder/${builderDigest#sha256:}"
  174. runnerDigest=$(jq -r '."runner-${{ matrix.variant }}"."containerimage.digest"' <<< "${METADATA}")
  175. touch "/tmp/metadata/runner/${runnerDigest#sha256:}"
  176. env:
  177. METADATA: ${{ steps.build.outputs.metadata }}
  178. -
  179. name: Upload builder metadata
  180. if: fromJson(needs.prepare.outputs.push)
  181. uses: actions/upload-artifact@v3
  182. with:
  183. name: metadata-builder-${{ matrix.variant }}
  184. path: /tmp/metadata/builder/*
  185. if-no-files-found: error
  186. retention-days: 1
  187. -
  188. name: Upload runner metadata
  189. if: fromJson(needs.prepare.outputs.push)
  190. uses: actions/upload-artifact@v3
  191. with:
  192. name: metadata-runner-${{ matrix.variant }}
  193. path: /tmp/metadata/runner/*
  194. if-no-files-found: error
  195. retention-days: 1
  196. -
  197. name: Run tests
  198. if: ${{ !matrix.qemu && !fromJson(needs.prepare.outputs.push) }}
  199. run: |
  200. docker run --platform=${{ matrix.platform }} --rm \
  201. "$(jq -r '."builder-${{ matrix.variant }}"."containerimage.config.digest"' <<< "${METADATA}")" \
  202. sh -c 'go test ${{ matrix.race }} -v ./... && cd caddy && go test ${{ matrix.race }} -v ./...'
  203. env:
  204. METADATA: ${{ steps.build.outputs.metadata }}
  205. # Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
  206. push:
  207. runs-on: ubuntu-latest
  208. needs:
  209. - prepare
  210. - build
  211. if: fromJson(needs.prepare.outputs.push)
  212. strategy:
  213. fail-fast: false
  214. matrix:
  215. variant: ${{ fromJson(needs.prepare.outputs.variants) }}
  216. target: ['builder', 'runner']
  217. steps:
  218. -
  219. name: Download metadata
  220. uses: actions/download-artifact@v3
  221. with:
  222. name: metadata-${{ matrix.target }}-${{ matrix.variant }}
  223. path: /tmp/metadata
  224. -
  225. name: Set up Docker Buildx
  226. uses: docker/setup-buildx-action@v3
  227. with:
  228. # Temporary fix for https://github.com/docker/buildx/issues/2229
  229. version: "https://github.com/jedevc/buildx.git#imagetools-resolver-copy-dupe"
  230. -
  231. name: Login to DockerHub
  232. uses: docker/login-action@v3
  233. with:
  234. username: ${{ secrets.REGISTRY_USERNAME }}
  235. password: ${{ secrets.REGISTRY_PASSWORD }}
  236. -
  237. name: Create manifest list and push
  238. working-directory: /tmp/metadata
  239. run: |
  240. set -x
  241. # shellcheck disable=SC2046,SC2086
  242. docker buildx imagetools create $(jq -cr '.target."${{ matrix.target }}-${{ matrix.variant }}".tags | map("-t " + .) | join(" ")' <<< ${METADATA}) \
  243. $(printf 'dunglas/frankenphp@sha256:%s ' *)
  244. env:
  245. METADATA: ${{ needs.prepare.outputs.metadata }}
  246. -
  247. name: Inspect image
  248. run: |
  249. # shellcheck disable=SC2046,SC2086
  250. docker buildx imagetools inspect $(jq -cr '.target."${{ matrix.target }}-${{ matrix.variant }}".tags | first' <<< ${METADATA})
  251. env:
  252. METADATA: ${{ needs.prepare.outputs.metadata }}