lib_sxng_container.sh 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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. # Tags
  92. params_build+=" --tag=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  93. else
  94. # Tags
  95. params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:latest"
  96. params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$DOCKER_TAG"
  97. fi
  98. # shellcheck disable=SC2086
  99. "$container_engine" $params_build_builder \
  100. --build-arg="TIMESTAMP_SETTINGS=$(git log -1 --format="%cd" --date=unix -- ./searx/settings.yml)" \
  101. --build-arg="TIMESTAMP_UWSGI=$(git log -1 --format="%cd" --date=unix -- ./container/uwsgi.ini)" \
  102. --tag="localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:builder" \
  103. --file="./container/$dockerfile" \
  104. .
  105. build_msg CONTAINER "Image \"builder\" built"
  106. # shellcheck disable=SC2086
  107. "$container_engine" $params_build \
  108. --build-arg="GIT_URL=$GIT_URL" \
  109. --build-arg="SEARXNG_GIT_VERSION=$VERSION_STRING" \
  110. --build-arg="LABEL_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  111. --build-arg="LABEL_VCS_REF=$(git rev-parse HEAD)" \
  112. --build-arg="LABEL_VCS_URL=$GIT_URL" \
  113. --file="./container/$dockerfile" \
  114. .
  115. build_msg CONTAINER "Image built"
  116. if [ "$GITHUB_ACTIONS" = "true" ]; then
  117. "$container_engine" push "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  118. # Output to GHA
  119. cat <<EOF >>"$GITHUB_OUTPUT"
  120. version_string=$VERSION_STRING
  121. version_tag=$VERSION_TAG
  122. docker_tag=$DOCKER_TAG
  123. git_url=$GIT_URL
  124. git_branch=$GIT_BRANCH
  125. EOF
  126. fi
  127. )
  128. dump_return $?
  129. }
  130. container.test() {
  131. local parch=${OVERRIDE_ARCH:-$(uname -m)}
  132. local arch
  133. local variant
  134. local platform
  135. if [ "$GITHUB_ACTIONS" != "true" ]; then
  136. die 1 "This command is intended to be run in GitHub Actions"
  137. fi
  138. required_commands podman
  139. # Setup arch specific
  140. case $parch in
  141. "X64" | "x86_64" | "amd64")
  142. arch="amd64"
  143. variant=""
  144. platform="linux/$arch"
  145. ;;
  146. "ARM64" | "aarch64" | "arm64")
  147. arch="arm64"
  148. variant=""
  149. platform="linux/$arch"
  150. ;;
  151. "ARMV7" | "armhf" | "armv7l" | "armv7")
  152. arch="arm"
  153. variant="v7"
  154. platform="linux/$arch/$variant"
  155. ;;
  156. *)
  157. err_msg "Unsupported architecture; $parch"
  158. exit 1
  159. ;;
  160. esac
  161. build_msg CONTAINER "Selected platform: $platform"
  162. (
  163. set -e
  164. podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant"
  165. name="$CONTAINER_IMAGE_NAME-$(date +%N)"
  166. podman create --name="$name" --rm --timeout=60 --network="host" \
  167. "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" >/dev/null
  168. podman start "$name" >/dev/null
  169. podman logs -f "$name" &
  170. pid_logs=$!
  171. # Wait until container is ready
  172. sleep 5
  173. curl -vf --max-time 5 "http://localhost:8080/healthz"
  174. kill $pid_logs &>/dev/null || true
  175. podman stop "$name" >/dev/null
  176. )
  177. dump_return $?
  178. }
  179. container.push() {
  180. # Architectures on manifest
  181. local release_archs=("amd64" "arm64" "armv7")
  182. local archs=()
  183. local variants=()
  184. local platforms=()
  185. if [ "$GITHUB_ACTIONS" != "true" ]; then
  186. die 1 "This command is intended to be run in GitHub Actions"
  187. fi
  188. required_commands podman
  189. for arch in "${release_archs[@]}"; do
  190. case $arch in
  191. "X64" | "x86_64" | "amd64")
  192. archs+=("amd64")
  193. variants+=("")
  194. platforms+=("linux/${archs[-1]}")
  195. ;;
  196. "ARM64" | "aarch64" | "arm64")
  197. archs+=("arm64")
  198. variants+=("")
  199. platforms+=("linux/${archs[-1]}")
  200. ;;
  201. "ARMV7" | "armv7" | "armhf" | "arm")
  202. archs+=("arm")
  203. variants+=("v7")
  204. platforms+=("linux/${archs[-1]}/${variants[-1]}")
  205. ;;
  206. *)
  207. err_msg "Unsupported architecture; $arch"
  208. exit 1
  209. ;;
  210. esac
  211. done
  212. (
  213. set -e
  214. # Pull archs
  215. for i in "${!archs[@]}"; do
  216. podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}"
  217. done
  218. # Manifest tags
  219. release_tags=("latest" "$DOCKER_TAG")
  220. # Create manifests
  221. for tag in "${release_tags[@]}"; do
  222. if ! podman manifest exists "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"; then
  223. podman manifest create "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"
  224. fi
  225. # Add archs to manifest
  226. for i in "${!archs[@]}"; do
  227. podman manifest add \
  228. "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \
  229. "containers-storage:ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}"
  230. done
  231. done
  232. podman image list
  233. # Remote registries
  234. release_registries=("ghcr.io" "docker.io")
  235. # Push manifests
  236. for registry in "${release_registries[@]}"; do
  237. for tag in "${release_tags[@]}"; do
  238. build_msg CONTAINER "Pushing manifest $tag to $registry"
  239. podman manifest push \
  240. "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \
  241. "docker://$registry/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"
  242. done
  243. done
  244. )
  245. dump_return $?
  246. }
  247. # Alias
  248. podman.build() {
  249. container.build podman
  250. }
  251. # Alias
  252. docker.build() {
  253. container.build docker
  254. }
  255. # Alias
  256. docker.buildx() {
  257. container.build docker
  258. }