lib_sxng_container.sh 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: AGPL-3.0-or-later
  3. container.help() {
  4. cat <<EOF
  5. container.:
  6. build : build container image
  7. EOF
  8. }
  9. CONTAINER_IMAGE_ORGANIZATION=${GITHUB_REPOSITORY_OWNER:-"searxng"}
  10. CONTAINER_IMAGE_NAME="searxng"
  11. container.build() {
  12. local parch=${OVERRIDE_ARCH:-$(uname -m)}
  13. local container_engine
  14. local dockerfile
  15. local arch
  16. local variant
  17. local platform
  18. required_commands git
  19. # Check if podman or docker is installed
  20. if [ "$1" = "podman" ] || [ "$1" = "docker" ]; then
  21. if ! command -v "$1" &>/dev/null; then
  22. die 42 "$1 is not installed"
  23. fi
  24. container_engine="$1"
  25. else
  26. # If no explicit engine is passed, prioritize podman over docker
  27. if command -v podman &>/dev/null; then
  28. container_engine="podman"
  29. elif command -v docker &>/dev/null; then
  30. container_engine="docker"
  31. else
  32. die 42 "no compatible container engine is installed (podman or docker)"
  33. fi
  34. fi
  35. info_msg "Selected engine: $container_engine"
  36. # Setup arch specific
  37. case $parch in
  38. "X64" | "x86_64" | "amd64")
  39. dockerfile="Dockerfile"
  40. arch="amd64"
  41. variant=""
  42. platform="linux/$arch"
  43. ;;
  44. "ARM64" | "aarch64" | "arm64")
  45. dockerfile="Dockerfile"
  46. arch="arm64"
  47. variant=""
  48. platform="linux/$arch"
  49. ;;
  50. "ARMV7" | "armhf" | "armv7l" | "armv7")
  51. dockerfile="legacy/Dockerfile"
  52. arch="arm"
  53. variant="v7"
  54. platform="linux/$arch/$variant"
  55. ;;
  56. *)
  57. err_msg "Unsupported architecture; $parch"
  58. exit 1
  59. ;;
  60. esac
  61. info_msg "Selected platform: $platform"
  62. pyenv.install
  63. (
  64. set -e
  65. pyenv.activate
  66. # Check if it is a git repository
  67. if [ ! -d .git ]; then
  68. die 1 "This is not Git repository"
  69. fi
  70. if ! git remote get-url origin &>/dev/null; then
  71. die 1 "There is no remote origin"
  72. fi
  73. # This is a git repository
  74. git update-index -q --refresh
  75. python -m searx.version freeze
  76. eval "$(python -m searx.version)"
  77. info_msg "Set \$VERSION_STRING: $VERSION_STRING"
  78. info_msg "Set \$VERSION_TAG: $VERSION_TAG"
  79. info_msg "Set \$DOCKER_TAG: $DOCKER_TAG"
  80. info_msg "Set \$GIT_URL: $GIT_URL"
  81. info_msg "Set \$GIT_BRANCH: $GIT_BRANCH"
  82. if [ "$container_engine" = "podman" ]; then
  83. params_build_builder="build --format=docker --platform=$platform --target=builder --layers --identity-label=false"
  84. params_build="build --format=docker --platform=$platform --layers --squash-all --omit-history --identity-label=false"
  85. else
  86. params_build_builder="build --platform=$platform --target=builder"
  87. params_build="build --platform=$platform --squash"
  88. fi
  89. if [ "$GITHUB_ACTIONS" = "true" ]; then
  90. params_build_builder+=" --cache-from=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache --cache-to=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache"
  91. params_build+=" --cache-from=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache --cache-to=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache"
  92. # Tags
  93. params_build+=" --tag=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  94. else
  95. # Tags
  96. params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:latest"
  97. params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$DOCKER_TAG"
  98. fi
  99. # shellcheck disable=SC2086
  100. "$container_engine" $params_build_builder \
  101. --build-arg="TIMESTAMP_SETTINGS=$(git log -1 --format="%cd" --date=unix -- ./searx/settings.yml)" \
  102. --build-arg="TIMESTAMP_UWSGI=$(git log -1 --format="%cd" --date=unix -- ./container/uwsgi.ini)" \
  103. --tag="localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:builder" \
  104. --file="./container/$dockerfile" \
  105. .
  106. build_msg CONTAINER "Image \"builder\" built"
  107. # shellcheck disable=SC2086
  108. "$container_engine" $params_build \
  109. --build-arg="GIT_URL=$GIT_URL" \
  110. --build-arg="SEARXNG_GIT_VERSION=$VERSION_STRING" \
  111. --build-arg="LABEL_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  112. --build-arg="LABEL_VCS_REF=$(git rev-parse HEAD)" \
  113. --build-arg="LABEL_VCS_URL=$GIT_URL" \
  114. --file="./container/$dockerfile" \
  115. .
  116. build_msg CONTAINER "Image built"
  117. if [ "$GITHUB_ACTIONS" = "true" ]; then
  118. "$container_engine" push "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  119. # Output to GHA
  120. cat <<EOF >>"$GITHUB_OUTPUT"
  121. version_string=$VERSION_STRING
  122. version_tag=$VERSION_TAG
  123. docker_tag=$DOCKER_TAG
  124. git_url=$GIT_URL
  125. git_branch=$GIT_BRANCH
  126. EOF
  127. fi
  128. )
  129. dump_return $?
  130. }
  131. container.test() {
  132. local parch=${OVERRIDE_ARCH:-$(uname -m)}
  133. local arch
  134. local variant
  135. local platform
  136. if [ "$GITHUB_ACTIONS" != "true" ]; then
  137. die 1 "This command is intended to be run in GitHub Actions"
  138. fi
  139. required_commands podman
  140. # Setup arch specific
  141. case $parch in
  142. "X64" | "x86_64" | "amd64")
  143. arch="amd64"
  144. variant=""
  145. platform="linux/$arch"
  146. ;;
  147. "ARM64" | "aarch64" | "arm64")
  148. arch="arm64"
  149. variant=""
  150. platform="linux/$arch"
  151. ;;
  152. "ARMV7" | "armhf" | "armv7l" | "armv7")
  153. arch="arm"
  154. variant="v7"
  155. platform="linux/$arch/$variant"
  156. ;;
  157. *)
  158. err_msg "Unsupported architecture; $parch"
  159. exit 1
  160. ;;
  161. esac
  162. build_msg CONTAINER "Selected platform: $platform"
  163. (
  164. set -e
  165. podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  166. name="$CONTAINER_IMAGE_NAME-$(date +%N)"
  167. podman create --name="$name" --rm --timeout=60 --network="host" \
  168. "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" >/dev/null
  169. podman start "$name" >/dev/null
  170. podman logs -f "$name" &
  171. pid_logs=$!
  172. # Wait until container is ready
  173. sleep 5
  174. curl -vf --max-time 5 "http://localhost:8080/healthz"
  175. kill $pid_logs &>/dev/null || true
  176. podman stop "$name" >/dev/null
  177. )
  178. dump_return $?
  179. }
  180. container.push() {
  181. # Architectures on manifest
  182. local release_archs=("amd64" "arm64" "armv7")
  183. local archs=()
  184. local variants=()
  185. local platforms=()
  186. if [ "$GITHUB_ACTIONS" != "true" ]; then
  187. die 1 "This command is intended to be run in GitHub Actions"
  188. fi
  189. required_commands podman
  190. for arch in "${release_archs[@]}"; do
  191. case $arch in
  192. "X64" | "x86_64" | "amd64")
  193. archs+=("amd64")
  194. variants+=("")
  195. platforms+=("linux/${archs[-1]}")
  196. ;;
  197. "ARM64" | "aarch64" | "arm64")
  198. archs+=("arm64")
  199. variants+=("")
  200. platforms+=("linux/${archs[-1]}")
  201. ;;
  202. "ARMV7" | "armv7" | "armhf" | "arm")
  203. archs+=("arm")
  204. variants+=("v7")
  205. platforms+=("linux/${archs[-1]}/${variants[-1]}")
  206. ;;
  207. *)
  208. err_msg "Unsupported architecture; $arch"
  209. exit 1
  210. ;;
  211. esac
  212. done
  213. (
  214. set -e
  215. # Pull archs
  216. for i in "${!archs[@]}"; do
  217. podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}"
  218. done
  219. # Manifest tags
  220. release_tags=("latest")
  221. release_tags+=("$DOCKER_TAG")
  222. # Create manifests
  223. for tag in "${release_tags[@]}"; do
  224. if ! podman manifest exists "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"; then
  225. podman manifest create "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"
  226. fi
  227. # Add archs to manifest
  228. for i in "${!archs[@]}"; do
  229. podman manifest add \
  230. "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \
  231. "containers-storage:ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}"
  232. done
  233. done
  234. podman image list
  235. # Push manifests
  236. for tag in "${release_tags[@]}"; do
  237. build_msg CONTAINER "Pushing manifest with tag: $tag"
  238. podman manifest push \
  239. "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \
  240. "docker://docker.io/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"
  241. done
  242. )
  243. dump_return $?
  244. }
  245. # Alias
  246. podman.build() {
  247. container.build podman
  248. }
  249. # Alias
  250. docker.build() {
  251. container.build docker
  252. }
  253. # Alias
  254. docker.buildx() {
  255. container.build docker
  256. }