diff --git a/.github/workflows/checker.yml b/.github/workflows/checker.yml index 4e900dd4c..2de3c9896 100644 --- a/.github/workflows/checker.yml +++ b/.github/workflows/checker.yml @@ -43,5 +43,4 @@ jobs: run: make V=1 install - name: Search checker - continue-on-error: true run: make search.checker diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 9e73eba69..1cf77827b 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -26,9 +26,7 @@ env: jobs: build: - # TODO: Uncomment - # if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' - if: false + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' name: Build (${{ matrix.arch }}) runs-on: ${{ matrix.os }} strategy: @@ -49,6 +47,13 @@ jobs: # Organization GHCR packages: write + outputs: + version_string: ${{ steps.build.outputs.version_string }} + version_tag: ${{ steps.build.outputs.version_tag }} + docker_tag: ${{ steps.build.outputs.docker_tag }} + git_url: ${{ steps.build.outputs.git_url }} + git_branch: ${{ steps.build.outputs.git_branch }} + steps: - name: Setup Python uses: actions/setup-python@v5 @@ -88,9 +93,10 @@ jobs: password: "${{ secrets.GITHUB_TOKEN }}" - name: Build + id: build env: OVERRIDE_ARCH: "${{ matrix.arch }}" - run: make ci.container.build + run: make podman.build test: name: Test (${{ matrix.arch }}) @@ -134,16 +140,17 @@ jobs: - name: Test env: OVERRIDE_ARCH: "${{ matrix.arch }}" - GIT_URL: "${{ steps.build.outputs.git_url }}" - run: make ci.container.test + GIT_URL: "${{ needs.build.outputs.git_url }}" + run: make container.test release: - # TODO: Uncomment before merge - # if: github.repository_owner == 'searxng' - if: false + if: github.repository_owner == 'searxng' && github.ref_name == 'master' name: Release runs-on: ubuntu-24.04-arm - needs: test + needs: + - build + - test + steps: - if: env.DOCKERHUB_USERNAME != '' name: Checkout @@ -170,6 +177,6 @@ jobs: - if: env.DOCKERHUB_USERNAME != '' name: Release env: - GIT_URL: "${{ steps.build.outputs.git_url }}" - DOCKER_TAG: "${{ steps.build.outputs.docker_tag }}" - run: make ci.container.push + GIT_URL: "${{ needs.build.outputs.git_url }}" + DOCKER_TAG: "${{ needs.build.outputs.docker_tag }}" + run: make container.push diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 713ee40e7..6d9f2cac5 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -24,9 +24,7 @@ env: jobs: release: - # TODO: Uncomment - # if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' - if: false + if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' name: Release runs-on: ubuntu-24.04-arm permissions: diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a64e7a07e..52ce01663 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -9,7 +9,7 @@ on: - master pull_request: branches: - - container-gha + - master concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} @@ -23,8 +23,6 @@ env: jobs: test: - # TODO: Remove this - if: false name: Python ${{ matrix.python-version }} runs-on: ubuntu-24.04 strategy: @@ -61,11 +59,14 @@ jobs: run: make V=1 ci.test theme: - # TODO: Remove this - if: false name: Theme runs-on: ubuntu-24.04-arm steps: + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "${{ env.PYTHON_VERSION }}" + - name: Checkout uses: actions/checkout@v4 with: @@ -82,44 +83,6 @@ jobs: key: "nodejs-${{ runner.arch }}-${{ hashFiles('./.nvmrc', './package.json') }}" path: "./client/simple/node_modules/" - - name: Build - run: make themes.all - - # TODO: Remove this - container-build: - # TODO: Uncomment - # if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' - name: Build (${{ matrix.arch }}) - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - arch: amd64 - os: ubuntu-24.04 - emulation: false - - arch: arm64 - os: ubuntu-24.04-arm - emulation: false - - arch: armv7 - os: ubuntu-24.04-arm - emulation: true - - permissions: - # Organization GHCR - packages: write - - steps: - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "${{ env.PYTHON_VERSION }}" - - - name: Checkout - uses: actions/checkout@v4 - with: - persist-credentials: "false" - - name: Setup cache Python uses: actions/cache@v4 with: @@ -127,72 +90,8 @@ jobs: restore-keys: "python-${{ env.PYTHON_VERSION }}-${{ runner.arch }}-" path: "./local/" - - name: Setup cache container mounts - uses: actions/cache@v4 - with: - key: "container-mounts-${{ matrix.arch }}-${{ hashFiles('./Dockerfile*') }}" - restore-keys: "container-mounts-${{ matrix.arch }}-" - path: | - /var/tmp/buildah-cache/ - /var/tmp/buildah-cache-*/ - - - if: ${{ matrix.emulation }} - name: Setup QEMU - uses: docker/setup-qemu-action@v3 - - - name: Login to GHCR - uses: docker/login-action@v3 - with: - registry: "ghcr.io" - username: "${{ github.repository_owner }}" - password: "${{ secrets.GITHUB_TOKEN }}" + - name: Setup venv + run: make V=1 install - name: Build - env: - OVERRIDE_ARCH: "${{ matrix.arch }}" - run: make ci.container.build - - # TODO: Remove this - container-test: - name: Test (${{ matrix.arch }}) - runs-on: ${{ matrix.os }} - needs: container-build - strategy: - fail-fast: false - matrix: - include: - - arch: amd64 - os: ubuntu-24.04 - emulation: false - - arch: arm64 - os: ubuntu-24.04-arm - emulation: false - - arch: armv7 - os: ubuntu-24.04-arm - emulation: true - - permissions: - # Organization GHCR - packages: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - persist-credentials: "false" - - - if: ${{ matrix.emulation }} - name: Setup QEMU - uses: docker/setup-qemu-action@v3 - - - name: Login to GHCR - uses: docker/login-action@v3 - with: - registry: "ghcr.io" - username: "${{ github.repository_owner }}" - password: "${{ secrets.GITHUB_TOKEN }}" - - - name: Test - env: - OVERRIDE_ARCH: "${{ matrix.arch }}" - run: make ci.container.test + run: make themes.all diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml index 690bc7ed1..9fa97c7f5 100644 --- a/.github/workflows/l10n.yml +++ b/.github/workflows/l10n.yml @@ -26,9 +26,7 @@ env: jobs: update: - # TODO: Uncomment - # if: github.repository_owner == 'searxng' && github.event.workflow_run.conclusion == 'success' - if: false + if: github.repository_owner == 'searxng' && github.event.workflow_run.conclusion == 'success' name: Update runs-on: ubuntu-24.04-arm permissions: diff --git a/Dockerfile b/Dockerfile index f31a1183b..92dfb4939 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,8 +43,6 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ # healthcheck wget \ - # lxml (ARMv7) - libxslt1.1 \ # uwsgi libpcre3 \ libxml2 \ diff --git a/Dockerfile.compat b/Dockerfile.compat index 4b44d9537..471b8bfdd 100644 --- a/Dockerfile.compat +++ b/Dockerfile.compat @@ -16,7 +16,7 @@ WORKDIR /usr/local/searxng/ COPY ./requirements.txt ./requirements.txt -RUN --mount=type=cache,id=pip,target=$HOME/.cache/pip python -m venv ./venv \ +RUN --mount=type=cache,id=pip,target=/root/.cache/pip python -m venv ./venv \ && . ./venv/bin/activate \ && pip install -r requirements.txt \ && pip install "uwsgi~=2.0" @@ -45,6 +45,10 @@ FROM docker.io/library/python:3.13-slim RUN apt-get update \ && apt-get install -y --no-install-recommends \ + # healthcheck + wget \ + # lxml (ARMv7) + libxslt1.1 \ # uwsgi libpcre3 \ libxml2 \ @@ -97,8 +101,6 @@ VOLUME $DATA_PATH EXPOSE 8080 -USER searxng:searxng - HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:8080/healthz || exit 1 ENTRYPOINT ["/usr/local/searxng/dockerfiles/docker-entrypoint.sh"] diff --git a/Makefile b/Makefile index 0b35621cc..8c20eed35 100644 --- a/Makefile +++ b/Makefile @@ -77,8 +77,9 @@ test.shell: MANAGE += weblate.translations.commit weblate.push.translations MANAGE += data.all data.traits data.useragents data.locales data.currencies MANAGE += docs.html docs.live docs.gh-pages docs.prebuild docs.clean +MANAGE += podman.build MANAGE += docker.build docker.buildx -MANAGE += container.build container.buildx +MANAGE += container.build container.test container.push MANAGE += gecko.driver MANAGE += node.env node.env.dev node.clean MANAGE += py.build py.clean @@ -88,7 +89,6 @@ MANAGE += test.yamllint test.pylint test.black test.pybabel test.unit test.cover MANAGE += themes.all themes.fix themes.test MANAGE += static.build.commit static.build.drop static.build.restore MANAGE += nvm.install nvm.clean nvm.status nvm.nodejs -MANAGE += ci.container.build ci.container.test ci.container.push PHONY += $(MANAGE) @@ -97,9 +97,8 @@ $(MANAGE): # short hands of selected targets -PHONY += docs docker themes +PHONY += docs container themes docs: docs.html container: container.build -docker: container.build themes: themes.all diff --git a/manage b/manage index f0c53024a..3e431a4e7 100755 --- a/manage +++ b/manage @@ -11,6 +11,9 @@ source "$(dirname "${BASH_SOURCE[0]}")/utils/lib.sh" # shellcheck source=utils/lib.sh source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_nvm.sh" +# shellcheck source=utils/lib_sxng_container.sh +source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_sxng_container.sh" + # shellcheck source=utils/lib_sxng_data.sh source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_sxng_data.sh" @@ -77,9 +80,6 @@ docs.: gh-pages : deploy on gh-pages branch prebuild : build reST include files (./${DOCS_BUILD}/includes) clean : clean documentation build -container. : - build : build container image - buildx : build container image with BuildKit gecko.driver: download & install geckodriver if not already installed (required for robot_tests) @@ -97,14 +97,11 @@ pyenv.: OK : test if virtualenv is OK format.: python : format Python code source using black -ci.: - container.: - build : build container image - release : push container images to remote registry EOF go.help node.help weblate.help + container.help data.help test.help themes.help @@ -144,132 +141,6 @@ docker.buildx() { container.buildx } -# Alias -docker.build() { - container.build -} - -container.buildx() { - container.build buildx -} - -container.build() { - pyenv.install - - local parch=${OVERRIDE_ARCH:-$(uname -m)} - local container_engine - local dockerfile - local arch - local variant - local platform - - # Setup arch specific - case $parch in - "X64" | "x86_64" | "amd64") - dockerfile="Dockerfile" - arch="amd64" - variant="" - platform="linux/$arch" - ;; - "ARM64" | "aarch64" | "arm64") - dockerfile="Dockerfile" - arch="arm64" - variant="" - platform="linux/$arch" - ;; - "ARMV7" | "armhf" | "armv7l" | "armv7") - dockerfile="Dockerfile.compat" - arch="arm" - variant="v7" - platform="linux/$arch/$variant" - ;; - *) - err_msg "Unsupported architecture; (PARCH=\"$parch\")" - exit 1 - ;; - esac - build_msg CONTAINER "Selected platform: $platform" - - # Check if podman or docker is installed - if command -v podman &>/dev/null; then - container_engine="podman" - elif command -v docker &>/dev/null; then - container_engine="docker" - else - die 1 "podman/docker is not installed" - fi - build_msg CONTAINER "Engine: $container_engine" - - # Check if git is installed - if ! command -v git &>/dev/null; then - die 1 "git is not installed" - fi - - ( - set -e - pyenv.activate - - # Check if it is a git repository - if [ ! -d .git ]; then - die 1 "This is not Git repository" - fi - - if ! git remote get-url origin 2>/dev/null; then - die 1 "there is no remote origin" - fi - - # This is a git repository - git update-index -q --refresh - python -m searx.version freeze - eval "$(python -m searx.version)" - - # Get the last git commit id - version_gitcommit=$(echo "$VERSION_TAG" | cut -d+ -f2) - build_msg CONTAINER "Last commit: $version_gitcommit" - - if [ "$container_engine" = "docker" ]; then - if [ "$1" = "buildx" ]; then - docker_builder="buildx build" - else - docker_builder="build" - warn_msg "The legacy builder is deprecated and will be removed in a future release: https://docs.docker.com/engine/deprecated/#legacy-builder-fallback" - fi - - params_build_builder="$docker_builder --platform=$platform --target=builder" - params_build="$docker_builder --platform=$platform --squash" - else - params_build_builder="build --format=docker --platform=$platform --target=builder --layers --identity-label=false" - params_build="build --format=docker --platform=$platform --layers --squash-all --omit-history --identity-label=false" - fi - - # Define container image org/name - # shellcheck disable=SC2001 - container_image_organization="$(echo "$GIT_URL" | sed 's|.*github\.com/\([^/]*\).*|\1|' || echo "searxng")" - container_image_name="searxng" - - build_msg CONTAINER "Building..." - - # shellcheck disable=SC2086 - "$container_engine" $params_build_builder \ - --build-arg="TIMESTAMP_SETTINGS=$(git log -1 --format="%cd" --date=unix -- ./searx/settings.yml)" \ - --build-arg="TIMESTAMP_UWSGI=$(git log -1 --format="%cd" --date=unix -- ./dockerfiles/uwsgi.ini)" \ - --tag="localhost/$container_image_organization/$container_image_name:builder" \ - --file="./$dockerfile" - - # shellcheck disable=SC2086 - "$container_engine" $params_build \ - --build-arg="GIT_URL=$GIT_URL" \ - --build-arg="SEARXNG_GIT_VERSION=$VERSION_STRING" \ - --build-arg="LABEL_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - --build-arg="LABEL_VCS_REF=$(git rev-parse HEAD)" \ - --build-arg="LABEL_VCS_URL=$GIT_URL" \ - --tag="localhost/$container_image_organization/$container_image_name:latest" \ - --tag="localhost/$container_image_organization/$container_image_name:$DOCKER_TAG" \ - --file="./$dockerfile" - ) - dump_return $? -} - # shellcheck disable=SC2119 gecko.driver() { pyenv.install @@ -378,273 +249,6 @@ docs.prebuild() { dump_return $? } -ci.container.build() { - if ! "$GITHUB_ACTIONS"; then - die 1 "This command is intended to be run in GitHub Actions" - fi - - pyenv.install - - local parch=${OVERRIDE_ARCH:-$(uname -m)} - local dockerfile - local arch - local variant - local platform - - # Setup arch specific - case $parch in - "X64" | "x86_64" | "amd64") - dockerfile="Dockerfile" - arch="amd64" - variant="" - platform="linux/$arch" - ;; - "ARM64" | "aarch64" | "arm64") - dockerfile="Dockerfile" - arch="arm64" - variant="" - platform="linux/$arch" - ;; - "ARMV7" | "armhf" | "armv7l" | "armv7") - dockerfile="Dockerfile.compat" - arch="arm" - variant="v7" - platform="linux/$arch/$variant" - ;; - *) - err_msg "Unsupported architecture; (PARCH=\"$parch\")" - exit 1 - ;; - esac - build_msg CONTAINER "Selected platform: $platform" - - # Check if podman is installed - if ! command -v podman &>/dev/null; then - die 1 "podman is not installed" - fi - - # Check if git is installed - if ! command -v git &>/dev/null; then - die 1 "git is not installed" - fi - - ( - set -e - pyenv.activate - - # Check if it is a git repository - if [ ! -d .git ]; then - die 1 "This is not Git repository" - fi - - if ! git remote get-url origin 2>/dev/null; then - die 1 "there is no remote origin" - fi - - # This is a git repository - git update-index -q --refresh - python -m searx.version freeze - eval "$(python -m searx.version)" - - # Get the last git commit id - version_gitcommit=$(echo "$VERSION_TAG" | cut -d+ -f2) - build_msg CONTAINER "Last commit: $version_gitcommit" - - # Define container image org/name - # shellcheck disable=SC2001 - container_image_organization="$(echo "$GIT_URL" | sed 's|.*github\.com/\([^/]*\).*|\1|' || echo "searxng")" - container_image_name="searxng" - - build_msg CONTAINER "Building..." - - podman build --format=docker --platform="$platform" --target=builder --layers --identity-label=false \ - --cache-from="ghcr.io/$container_image_organization/cache" \ - --cache-to="ghcr.io/$container_image_organization/cache" \ - --build-arg="TIMESTAMP_SETTINGS=$(git log -1 --format="%cd" --date=unix -- ./searx/settings.yml)" \ - --build-arg="TIMESTAMP_UWSGI=$(git log -1 --format="%cd" --date=unix -- ./dockerfiles/uwsgi.ini)" \ - --tag="ghcr.io/$container_image_organization/cache:$container_image_name-$arch$variant-builder" \ - --file="./$dockerfile" - - podman build --format=docker --platform="$platform" --layers --squash-all --omit-history --identity-label=false \ - --cache-from="ghcr.io/$container_image_organization/cache" \ - --cache-to="ghcr.io/$container_image_organization/cache" \ - --build-arg="GIT_URL=$GIT_URL" \ - --build-arg="SEARXNG_GIT_VERSION=$VERSION_STRING" \ - --build-arg="LABEL_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - --build-arg="LABEL_VCS_REF=$(git rev-parse HEAD)" \ - --build-arg="LABEL_VCS_URL=$GIT_URL" \ - --tag="ghcr.io/$container_image_organization/cache:$container_image_name-$arch$variant" \ - --file="./$dockerfile" - - podman push "ghcr.io/$container_image_organization/cache:$container_image_name-$arch$variant" - - # From version.py - { - echo "version_string=$VERSION_STRING" - echo "version_tag=$VERSION_TAG" - echo "docker_tag=$DOCKER_TAG" - echo "git_url=$GIT_URL" - echo "git_branch=$GIT_BRANCH" - } >>"$GITHUB_OUTPUT" - ) - dump_return $? -} - -ci.container.test() { - if ! "$GITHUB_ACTIONS"; then - die 1 "This command is intended to be run in GitHub Actions" - fi - - local parch=${OVERRIDE_ARCH:-$(uname -m)} - local arch - local variant - local platform - - # Setup arch specific - case $parch in - "X64" | "x86_64" | "amd64") - arch="amd64" - variant="" - platform="linux/$arch" - ;; - "ARM64" | "aarch64" | "arm64") - arch="arm64" - variant="" - platform="linux/$arch" - ;; - "ARMV7" | "armhf" | "armv7l" | "armv7") - arch="arm" - variant="v7" - platform="linux/$arch/$variant" - ;; - *) - err_msg "Unsupported architecture; (PARCH=\"$parch\")" - exit 1 - ;; - esac - build_msg CONTAINER "Selected platform: $platform" - - # Check if podman is installed - if ! command -v podman &>/dev/null; then - die 1 "podman is not installed" - fi - - ( - set -e - - # Define container image org/name - # shellcheck disable=SC2001 - container_image_organization="$(echo "$GIT_URL" | sed 's|.*github\.com/\([^/]*\).*|\1|' || echo "searxng")" - container_image_name="searxng" - - podman pull "ghcr.io/$container_image_organization/cache:$container_image_name-$arch$variant" - - name="$container_image_name-$(date +%N)" - - podman create --name="$name" --rm --timeout=60 --network="host" \ - "ghcr.io/$container_image_organization/cache:$container_image_name-$arch$variant" >/dev/null - - podman start "$name" >/dev/null - podman logs -f "$name" & - pid_logs=$! - - # Wait until container is ready - sleep 5 - - curl -vf --max-time 5 "http://localhost:8080/healthz" - - kill $pid_logs &>/dev/null || true - podman stop "$name" >/dev/null - ) - dump_return $? -} - -ci.container.push() { - if ! "$GITHUB_ACTIONS"; then - die 1 "This command is intended to be run in GitHub Actions" - fi - - # Architectures to release - local release_archs=("amd64" "arm64" "armv7") - - local archs=() - local variants=() - local platforms=() - - for arch in "${release_archs[@]}"; do - case $arch in - "X64" | "x86_64" | "amd64") - archs+=("amd64") - variants+=("") - platforms+=("linux/${archs[-1]}") - ;; - "ARM64" | "aarch64" | "arm64") - archs+=("arm64") - variants+=("") - platforms+=("linux/${archs[-1]}") - ;; - "ARMV7" | "armv7" | "armhf" | "arm") - archs+=("arm") - variants+=("v7") - platforms+=("linux/${archs[-1]}/${variants[-1]}") - ;; - *) - err_msg "Unsupported architecture; (ARCH=\"${arch[-1]}\")" - exit 1 - ;; - esac - done - - # Check if podman is installed - if ! command -v podman &>/dev/null; then - die 1 "podman is not installed" - fi - - ( - set -e - - # Define container image org/name - # shellcheck disable=SC2001 - container_image_organization="$(echo "$GIT_URL" | sed 's|.*github\.com/\([^/]*\).*|\1|' || echo "searxng")" - container_image_name="searxng" - - # Pull archs - for i in "${!archs[@]}"; do - podman pull "ghcr.io/$container_image_organization/$container_image_name:cache-${archs[$i]}${variants[$i]}" - done - - # Tags to release - tags=("latest") - tags+=("$DOCKER_TAG") - - # Create manifests - for tag in "${tags[@]}"; do - if ! podman manifest exists "localhost/$container_image_organization/$container_image_name:$tag"; then - podman manifest create "localhost/$container_image_organization/$container_image_name:$tag" - fi - - # Add archs to manifest - for i in "${!archs[@]}"; do - podman manifest add \ - "localhost/$container_image_organization/$container_image_name:$tag" \ - "containers-storage:ghcr.io/$container_image_organization/$container_image_name:cache-${archs[$i]}${variants[$i]}" - done - done - - podman image list - - # Push manifests - for tag in "${tags[@]}"; do - build_msg CONTAINER "Pushing manifest with tag: $tag" - - podman manifest push \ - "localhost/$container_image_organization/$container_image_name:$tag" \ - "docker://docker.io/$container_image_organization/$container_image_name:$tag" - done - ) - dump_return $? -} - # shellcheck disable=SC2119 main() { local _type diff --git a/utils/lib_sxng_container.sh b/utils/lib_sxng_container.sh new file mode 100755 index 000000000..325f1a7e7 --- /dev/null +++ b/utils/lib_sxng_container.sh @@ -0,0 +1,319 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: AGPL-3.0-or-later + +container.help() { + cat </dev/null; then + container_engine="docker" + else + die 1 "Docker is not installed" + fi + elif [ "$1" = "podman" ]; then + if command -v podman &>/dev/null; then + container_engine="podman" + else + die 1 "Podman is not installed" + fi + else + # If no explicit engine is passed, prioritize podman over docker + if command -v podman &>/dev/null; then + container_engine="podman" + elif command -v docker &>/dev/null; then + container_engine="docker" + else + die 1 "Podman/Docker is not installed" + fi + fi + info_msg "Selected engine: $container_engine" + + # Check if git is installed + if ! command -v git &>/dev/null; then + die 1 "Git is not installed" + fi + + ( + set -e + pyenv.activate + + # Check if it is a git repository + if [ ! -d .git ]; then + die 1 "This is not Git repository" + fi + + if ! git remote get-url origin &>/dev/null; then + die 1 "There is no remote origin" + fi + + # This is a git repository + git update-index -q --refresh + python -m searx.version freeze + eval "$(python -m searx.version)" + + info_msg "Set \$VERSION_STRING: $VERSION_STRING" + info_msg "Set \$VERSION_TAG: $VERSION_TAG" + info_msg "Set \$DOCKER_TAG: $DOCKER_TAG" + info_msg "Set \$GIT_URL: $GIT_URL" + info_msg "Set \$GIT_BRANCH: $GIT_BRANCH" + + if [ "$container_engine" = "podman" ]; then + params_build_builder="build --format=docker --platform=$platform --target=builder --layers --identity-label=false" + params_build="build --format=docker --platform=$platform --layers --squash-all --omit-history --identity-label=false" + else + params_build_builder="build --platform=$platform --target=builder" + params_build="build --platform=$platform --squash" + fi + + if [ "$GITHUB_ACTIONS" = "true" ]; then + params_build_builder+=" --cache-from=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache --cache-to=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache" + params_build+=" --cache-from=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache --cache-to=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache" + + # Tags + params_build+=" --tag=ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" + else + # Tags + params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:latest" + params_build+=" --tag=localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$DOCKER_TAG" + fi + + # shellcheck disable=SC2086 + "$container_engine" $params_build_builder \ + --build-arg="TIMESTAMP_SETTINGS=$(git log -1 --format="%cd" --date=unix -- ./searx/settings.yml)" \ + --build-arg="TIMESTAMP_UWSGI=$(git log -1 --format="%cd" --date=unix -- ./dockerfiles/uwsgi.ini)" \ + --tag="localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:builder" \ + --file="./$dockerfile" \ + . + build_msg CONTAINER "Image \"builder\" built" + + # shellcheck disable=SC2086 + "$container_engine" $params_build \ + --build-arg="GIT_URL=$GIT_URL" \ + --build-arg="SEARXNG_GIT_VERSION=$VERSION_STRING" \ + --build-arg="LABEL_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --build-arg="LABEL_VCS_REF=$(git rev-parse HEAD)" \ + --build-arg="LABEL_VCS_URL=$GIT_URL" \ + --file="./$dockerfile" \ + . + build_msg CONTAINER "Image built" + + if [ "$GITHUB_ACTIONS" = "true" ]; then + "$container_engine" push "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" + + # Output to GHA + { + echo "version_string=$VERSION_STRING" + echo "version_tag=$VERSION_TAG" + echo "docker_tag=$DOCKER_TAG" + echo "git_url=$GIT_URL" + echo "git_branch=$GIT_BRANCH" + } >>"$GITHUB_OUTPUT" + fi + ) + dump_return $? +} + +container.test() { + if ! "$GITHUB_ACTIONS"; then + die 1 "This command is intended to be run in GitHub Actions" + fi + + local parch=${OVERRIDE_ARCH:-$(uname -m)} + local arch + local variant + local platform + + # Setup arch specific + case $parch in + "X64" | "x86_64" | "amd64") + arch="amd64" + variant="" + platform="linux/$arch" + ;; + "ARM64" | "aarch64" | "arm64") + arch="arm64" + variant="" + platform="linux/$arch" + ;; + "ARMV7" | "armhf" | "armv7l" | "armv7") + arch="arm" + variant="v7" + platform="linux/$arch/$variant" + ;; + *) + err_msg "Unsupported architecture; (PARCH=\"$parch\")" + exit 1 + ;; + esac + build_msg CONTAINER "Selected platform: $platform" + + # Check if podman is installed + if ! command -v podman &>/dev/null; then + die 1 "podman is not installed" + fi + + ( + set -e + + podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" + + name="$CONTAINER_IMAGE_NAME-$(date +%N)" + + podman create --name="$name" --rm --timeout=60 --network="host" \ + "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-$arch$variant" >/dev/null + + podman start "$name" >/dev/null + podman logs -f "$name" & + pid_logs=$! + + # Wait until container is ready + sleep 5 + + curl -vf --max-time 5 "http://localhost:8080/healthz" + + kill $pid_logs &>/dev/null || true + podman stop "$name" >/dev/null + ) + dump_return $? +} + +container.push() { + if ! "$GITHUB_ACTIONS"; then + die 1 "This command is intended to be run in GitHub Actions" + fi + + # Architectures to release + local release_archs=("amd64" "arm64" "armv7") + + local archs=() + local variants=() + local platforms=() + + for arch in "${release_archs[@]}"; do + case $arch in + "X64" | "x86_64" | "amd64") + archs+=("amd64") + variants+=("") + platforms+=("linux/${archs[-1]}") + ;; + "ARM64" | "aarch64" | "arm64") + archs+=("arm64") + variants+=("") + platforms+=("linux/${archs[-1]}") + ;; + "ARMV7" | "armv7" | "armhf" | "arm") + archs+=("arm") + variants+=("v7") + platforms+=("linux/${archs[-1]}/${variants[-1]}") + ;; + *) + err_msg "Unsupported architecture; (ARCH=\"${arch[-1]}\")" + exit 1 + ;; + esac + done + + # Check if podman is installed + if ! command -v podman &>/dev/null; then + die 1 "podman is not installed" + fi + + ( + set -e + + # Pull archs + for i in "${!archs[@]}"; do + podman pull "ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}" + done + + # Tags to release + tags=("latest") + tags+=("$DOCKER_TAG") + + # Create manifests + for tag in "${tags[@]}"; do + if ! podman manifest exists "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag"; then + podman manifest create "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" + fi + + # Add archs to manifest + for i in "${!archs[@]}"; do + podman manifest add \ + "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \ + "containers-storage:ghcr.io/$CONTAINER_IMAGE_ORGANIZATION/cache:$CONTAINER_IMAGE_NAME-${archs[$i]}${variants[$i]}" + done + done + + podman image list + + # Push manifests + for tag in "${tags[@]}"; do + build_msg CONTAINER "Pushing manifest with tag: $tag" + + podman manifest push \ + "localhost/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" \ + "docker://docker.io/$CONTAINER_IMAGE_ORGANIZATION/$CONTAINER_IMAGE_NAME:$tag" + done + ) + dump_return $? +} + +# Alias +podman.build() { + container.build podman +} + +# Alias +docker.build() { + container.build docker +} + +# Alias +docker.buildx() { + container.build docker +}