From 85c1e52b061ea2b4ee24ecca9113922230ca460a Mon Sep 17 00:00:00 2001
From: Googol Lee <i@googol.im>
Date: Mon, 2 Sep 2024 18:35:45 +0200
Subject: [PATCH] Use alpine as base images. (#1033)

---
 .github/workflows/build.yml             | 12 ++--
 Dockerfile                              | 34 +++++-----
 README.md                               | 19 +++---
 dev-compose.yaml                        |  5 +-
 scripts/apt/debian-testing.sources      | 13 ----
 scripts/apt/default.pref                | 11 ----
 scripts/install_build_dependencies.sh   | 43 ++-----------
 scripts/install_runtime_dependencies.sh | 13 ++--
 scripts/set_compiler_env.sh             | 84 -------------------------
 9 files changed, 47 insertions(+), 187 deletions(-)
 delete mode 100644 scripts/apt/debian-testing.sources
 delete mode 100644 scripts/apt/default.pref
 delete mode 100755 scripts/set_compiler_env.sh

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d7601a4..5dd576c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,14 +1,15 @@
 name: Docker builds
 
 on:
-  pull_request:
-    branches: [master]
   push:
     branches: [master]
     tags:
       - v*
+  pull_request:
+    branches: [master]
 
 env:
+  IS_PUSHING_IMAGES: ${{ github.event_name != 'pull_request' && github.repository == 'photoview/photoview' }}
   DOCKER_USERNAME: viktorstrate
   DOCKER_IMAGE: viktorstrate/photoview
   DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
@@ -20,9 +21,6 @@ jobs:
     runs-on: ubuntu-latest
 
     steps:
-      - name: Delete huge unnecessary tools folder
-        run: rm -rf /opt/hostedtoolcache
-
       - name: Checkout
         uses: actions/checkout@v4
 
@@ -36,7 +34,7 @@ jobs:
         uses: docker/setup-buildx-action@v3
 
       - name: Docker Login
-        if: success() && github.event_name != 'pull_request' && github.repository == 'photoview/photoview'
+        if: ${{ env.IS_PUSHING_IMAGES == 'true' }}
         uses: docker/login-action@v3
         with:
           username: ${{ env.DOCKER_USERNAME }}
@@ -64,7 +62,7 @@ jobs:
           context: .
           platforms: ${{ env.PLATFORMS }}
           pull: true
-          push: ${{ github.event_name != 'pull_request' && github.repository == 'photoview/photoview' }}
+          push: ${{ env.IS_PUSHING_IMAGES }}
           tags: ${{ steps.docker_meta.outputs.tags }}
           labels: ${{ steps.docker_meta.outputs.labels }}
           cache-from: type=gha
diff --git a/Dockerfile b/Dockerfile
index 36efb44..15080f9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,8 @@
 ### Build UI ###
-FROM --platform=${BUILDPLATFORM:-linux/amd64} node:18 AS ui
+FROM --platform=${BUILDPLATFORM:-linux/amd64} node:18-alpine AS ui
 
 # See for details: https://github.com/hadolint/hadolint/wiki/DL4006
-SHELL ["/bin/bash", "-euo", "pipefail", "-c"]
+SHELL ["/bin/sh", "-euo", "pipefail", "-c"]
 
 ARG REACT_APP_API_ENDPOINT
 ENV REACT_APP_API_ENDPOINT=${REACT_APP_API_ENDPOINT}
@@ -36,11 +36,11 @@ RUN if [ "${BUILD_DATE}" = "undefined" ]; then \
   npm run build -- --base=$UI_PUBLIC_URL
 
 ### Build API ###
-FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.22-bookworm AS api
+FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.22-alpine AS api
 ARG TARGETPLATFORM
 
 # See for details: https://github.com/hadolint/hadolint/wiki/DL4006
-SHELL ["/bin/bash", "-euo", "pipefail", "-c"]
+SHELL ["/bin/sh", "-euo", "pipefail", "-c"]
 
 WORKDIR /app/api
 
@@ -49,43 +49,41 @@ ENV PATH="${GOPATH}/bin:${PATH}"
 ENV CGO_ENABLED=1
 
 # Download dependencies
-COPY scripts/apt/debian-testing.sources /etc/apt/sources.list.d/
-COPY scripts/set_compiler_env.sh /app/scripts/
 COPY scripts/install_build_dependencies.sh /app/scripts/
 COPY scripts/install_runtime_dependencies.sh /app/scripts/
 RUN chmod +x /app/scripts/*.sh \
   && /app/scripts/install_build_dependencies.sh \
+  && mv $(go env GOPATH)/bin/reflex /usr/bin/ \
   && /app/scripts/install_runtime_dependencies.sh
 
 COPY api/go.mod api/go.sum /app/api/
-RUN source /app/scripts/set_compiler_env.sh \
-  && go env \
+RUN go env \
   && go mod download \
   # Patch go-face
   && sed -i 's/-march=native//g' ${GOPATH}/pkg/mod/github.com/!kagami/go-face*/face.go \
   # Build dependencies that use CGO
-  && go install \
-    github.com/mattn/go-sqlite3 \
-    github.com/Kagami/go-face
+  && go install github.com/mattn/go-sqlite3 github.com/Kagami/go-face
 
 COPY api /app/api
-RUN source /app/scripts/set_compiler_env.sh \
-  && go build -v -o photoview .
+RUN go build -v -o photoview .
 
 ### Build release image ###
-FROM --platform=${BUILDPLATFORM:-linux/amd64} debian:testing-slim AS release
+FROM --platform=${BUILDPLATFORM:-linux/amd64} alpine:latest AS release
 ARG TARGETPLATFORM
 
 # See for details: https://github.com/hadolint/hadolint/wiki/DL4006
-SHELL ["/bin/bash", "-euo", "pipefail", "-c"]
+SHELL ["/bin/sh", "-euo", "pipefail", "-c"]
 
 COPY scripts/install_runtime_dependencies.sh /app/scripts/
 RUN chmod +x /app/scripts/install_runtime_dependencies.sh \
   # Create a user to run Photoview server
-  && groupadd -g 999 photoview \
-  && useradd -r -u 999 -g photoview -m photoview \
+  && addgroup -g 9999 photoview \
+  && adduser -u 9999 -G photoview -D photoview \
   # Required dependencies
-  && /app/scripts/install_runtime_dependencies.sh
+  && /app/scripts/install_runtime_dependencies.sh \
+  # Remove build dependencies and cleanup
+  && apk cache clean \
+  && rm -rf /var/cache/apk/*
 
 WORKDIR /home/photoview
 
diff --git a/README.md b/README.md
index 23b1cdd..2f82a71 100644
--- a/README.md
+++ b/README.md
@@ -91,11 +91,11 @@ All the photo galleries can do a lot of what I need, but no single one can do it
 > `7zz` should be installed in case, you'd like to use it in scope of the backup scenario instead of the default .tar.xz format. Read the comment in the `Makefile`, located on top of the `backup` section for more details.
 
 1. Download the content of the `docker-compose example` folder to the folder on your server, where you expect to host the Photoview internal data (database and cache files).
-   
+
    Please note that this folder contains 2 versions of the docker-compose file:
    - `docker-compose.example.yml` - the fully-functional and recommended for the most cases config
    - `docker-compose.minimal.example.yml` - the minimal and simple config for those, who find the previous one too complex and difficult to understand and manage
-   
+
    When downloading files, you need to choose only one of them.
 2. Rename downloaded files and remove the `example` from their names (so, you need to have `.env`, `docker-compose.yml`, and `Makefile` files). If you choose the `docker-compose.minimal.example.yml` on previous step, make sure to rename it to the `docker-compose.yml`.
 3. Open these files in a text editor and read them. Modify where needed according to the documentation comments to properly match your setup. There are comments of 2 types: those, starting with `##`, are explanations and examples, which should not be uncommented; those, starting with `#`, are optional or alternative configuration parts, which might be uncommented in certain circumstances, described in corresponding explanations. It is better to go through the files in the next order: `.env`, `docker-compose.yml`, and `Makefile`.
@@ -107,10 +107,12 @@ All the photo galleries can do a lot of what I need, but no single one can do it
 
    If command(s) return `Permission denied` error, run them under the user, owning corresponding files and folders. Alternatively, run them adding `sudo ` before the command: this will switch the execution context to `root` user and ask for the root password. You have to have permission to run `sudo` in the system.
 
+   NOTE: GID and UID was changed to 9999. If it's different from your local files, please update your files.
+
    If you don't want to give required permissions to `others` group for your files, alternatively, you can:
 
-   - create a group on your host with GID=999 and make all the files and folders inside volumes of the `photoview` service being owned by this group; then set the appropriate permissions to the `group` section.
-   - create on your host a group with GID=999 and a user in this group with UID=999; then change the ownership of all the files and folders inside volumes of the `photoview` service to this user; then set the appropriate permissions to the `user` section.
+   - create a group on your host with GID=9999 and make all the files and folders inside volumes of the `photoview` service being owned by this group; then set the appropriate permissions to the `group` section.
+   - create on your host a group with GID=9999 and a user in this group with UID=9999; then change the ownership of all the files and folders inside volumes of the `photoview` service to this user; then set the appropriate permissions to the `user` section.
 
    If you configured other mounts with media files from other locations on the host (like HOST_PHOTOVIEW_MEDIA_FAMILY or anything else), you need to run the same commands, as in the `Makefile` `readable` target, for each media root folder on your host manually: copy each command to your shell and replace the variable with the absolute path to an additional media root folder without the trailing `/`. Run both commands for each additional root folder.
 5. In case, you don't have `make` installed in your system or don't want to use it for the Photoview management activities, you could use the same commands from the `Makefile` and run them in your shell directly, or create your own scripts. Make sure to apply or replace the variables from your `.env` first in this case. `Makefile` is provided just for your convenience and simplicity, but is optional.
@@ -233,8 +235,13 @@ The site can now be accessed at [localhost:1234](http://localhost:1234).
 
 ## Set up local development environment
 
+In Linux, we recommend to use [Docker Compose or Docker](https://github.com/googollee/photoview?tab=readme-ov-file#set-up-docker-development-environment) as a local development environment.
+
+We can't keep verifying below commands on each environment. People may need to solve dependencies by their own.
+
 ### Install dependencies
 
+
 - API
   - Required packages:
     - `golang` >= 1.22
@@ -245,7 +252,7 @@ The site can now be accessed at [localhost:1234](http://localhost:1234).
         - `dlib`
         - `libjpeg`
         - `libblas` 
-        - `libcblas`, recommended using `libatlas-base`
+        - `libcblas`, recommended using `libatlas-base` in Debian.
         - `liblapack`
   - Optional tools during developing:
     - [`reflex`](https://github.com/cespare/reflex): a source code monitoring tool, which automatically rebuilds and restarts the server, running from the code in development.
@@ -297,8 +304,6 @@ You can install `node` with other package manager if you like.
 Then run the following commands:
 
 ```bash
-# Optional: Set the compiler environment in Debian/Ubuntu
-$ source ./scripts/set_compiler_env.sh
 # Set the compiler environment with `homebrew`
 $ export CPLUS_INCLUDE_PATH="$(brew --prefix)/opt/jpeg/include:$(brew --prefix)/opt/dlib/include"
 $ export LD_LIBRARY_PATH="$(brew --prefix)/opt/jpeg/lib:$(brew --prefix)/opt/dlib/lib"
diff --git a/dev-compose.yaml b/dev-compose.yaml
index 20150ca..d3678a3 100644
--- a/dev-compose.yaml
+++ b/dev-compose.yaml
@@ -13,7 +13,7 @@ services:
     ports:
     - 1234:1234
     command:
-    - /bin/bash
+    - /bin/sh
     - -c
     - |
       npm ci
@@ -36,10 +36,9 @@ services:
     ports:
     - 4001:4001
     command:
-    - /bin/bash
+    - /bin/sh
     - -c
     - |
-      source /app/scripts/set_compiler_env.sh 
       reflex -g '*.go' -s -- go run .
 
   mariadb:
diff --git a/scripts/apt/debian-testing.sources b/scripts/apt/debian-testing.sources
deleted file mode 100644
index 34b2544..0000000
--- a/scripts/apt/debian-testing.sources
+++ /dev/null
@@ -1,13 +0,0 @@
-Types: deb
-# http://snapshot.debian.org/archive/debian/20240722T000000Z
-URIs: http://deb.debian.org/debian
-Suites: testing testing-updates
-Components: main
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
-
-Types: deb
-# http://snapshot.debian.org/archive/debian-security/20240722T000000Z
-URIs: http://deb.debian.org/debian-security
-Suites: testing-security
-Components: main
-Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
diff --git a/scripts/apt/default.pref b/scripts/apt/default.pref
deleted file mode 100644
index 270d102..0000000
--- a/scripts/apt/default.pref
+++ /dev/null
@@ -1,11 +0,0 @@
-Package: *
-Pin: release n=bookworm
-Pin-Priority: 700
-
-Package: *
-Pin: release n=bookworm-updates
-Pin-Priority: 700
-
-Package: *
-Pin: release n=bookworm-security
-Pin-Priority: 700
diff --git a/scripts/install_build_dependencies.sh b/scripts/install_build_dependencies.sh
index 2e0ca59..81d54aa 100755
--- a/scripts/install_build_dependencies.sh
+++ b/scripts/install_build_dependencies.sh
@@ -1,42 +1,11 @@
-#!/bin/bash
+#!/bin/sh
 set -euo pipefail
 
-if [ "$TARGETPLATFORM" == "linux/arm64" ]; then
-  dpkg --add-architecture arm64
-  DEBIAN_ARCH='arm64'
-elif [ "$TARGETPLATFORM" == "linux/arm/v6" ] || [ "$TARGETPLATFORM" == "linux/arm/v7" ]; then
-  dpkg --add-architecture armhf
-  DEBIAN_ARCH='armhf'
-else
-  dpkg --add-architecture amd64
-  DEBIAN_ARCH='amd64'
-fi
+apk update
 
-apt-get update
-
-# Install G++/GCC cross compilers
-if [ "$DEBIAN_ARCH" == "arm64" ]; then
-  apt-get install -y \
-    g++-aarch64-linux-gnu \
-    libc6-dev-arm64-cross
-elif [ "$DEBIAN_ARCH" == "armhf" ]; then
-  apt-get install -y \
-    g++-arm-linux-gnueabihf \
-    libc6-dev-armhf-cross
-else
-  apt-get install -y \
-    g++-x86-64-linux-gnu \
-    libc6-dev-amd64-cross
-fi
-
-# Install go-face dependencies and libheif for HEIF media decoding
-apt-get install -y \
-  libdlib-dev:${DEBIAN_ARCH} \
-  libblas-dev:${DEBIAN_ARCH} \
-  libatlas-base-dev:${DEBIAN_ARCH} \
-  liblapack-dev:${DEBIAN_ARCH} \
-  libjpeg-dev:${DEBIAN_ARCH} \
-  libheif-dev:${DEBIAN_ARCH}
+apk add go g++ blas-dev cblas lapack-dev jpeg-dev libheif-dev
+apk add dlib-dev --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing
 
 # Install tools for development
-apt-get install -y reflex sqlite3
+apk add sqlite
+go install github.com/cespare/reflex@latest
diff --git a/scripts/install_runtime_dependencies.sh b/scripts/install_runtime_dependencies.sh
index e798b29..ff42642 100755
--- a/scripts/install_runtime_dependencies.sh
+++ b/scripts/install_runtime_dependencies.sh
@@ -1,10 +1,9 @@
-#!/bin/bash
+#!/bin/sh
 set -euo pipefail
 
-apt-get update
-apt-get install -y curl libdlib19.2 ffmpeg exiftool libheif1 imagemagick
+apk update
 
-# Remove build dependencies and cleanup
-apt-get autoremove -y
-apt-get clean
-rm -rf /var/lib/apt/lists/*
+apk add curl libheif lapack cblas exiftool
+apk add ffmpeg ffmpeg-libs ffmpeg-libavcodec ffmpeg-libavformat
+apk add imagemagick imagemagick-libs imagemagick-heic imagemagick-jpeg imagemagick-raw imagemagick-tiff imagemagick-webp imagemagick-svg imagemagick-jxl
+apk add dlib --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing
diff --git a/scripts/set_compiler_env.sh b/scripts/set_compiler_env.sh
deleted file mode 100755
index a7744e6..0000000
--- a/scripts/set_compiler_env.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/bin/sh
-
-# Script to configure environment variables for Go compiler
-# to allow cross compilation
-
-: ${TARGETPLATFORM=}
-: ${TARGETOS=}
-: ${TARGETARCH=}
-: ${TARGETVARIANT=}
-
-CGO_ENABLED="$(go env CGO_ENABLED)"
-GOARCH="$(go env GOARCH)"
-GOOS="$(go env GOOS)"
-GOARM="$(go env GOARM)"
-GOBIN="$(go env GOBIN)"
-
-set -eu
-
-if [ ! -z "$TARGETPLATFORM" ]; then
-  TARGETOS="$(echo $TARGETPLATFORM | cut -d"/" -f1)"
-  TARGETARCH="$(echo $TARGETPLATFORM | cut -d"/" -f2)"
-  TARGETVARIANT="$(echo $TARGETPLATFORM | cut -d"/" -f3)"
-fi
-
-if [ ! -z "$TARGETOS" ]; then
-  export GOOS="$TARGETOS"
-fi
-
-if [ ! -z "$TARGETARCH" ]; then
-  export GOARCH="$TARGETARCH"
-fi
-
-if [ "$TARGETARCH" = "arm" ]; then
-  if [ ! -z "$TARGETVARIANT" ]; then
-    case "$TARGETVARIANT" in
-    "v5")
-      export GOARM="5"
-      ;;
-    "v6")
-      export GOARM="6"
-      ;;
-    *)
-      export GOARM="7"
-      ;;
-    esac
-  else
-    export GOARM="7"
-  fi
-fi
-
-if [ "$CGO_ENABLED" = "1" ]; then
-  case "$GOARCH" in
-  "amd64")
-    export COMPILER_ARCH="x86_64-linux-gnu"
-    ;;
-  "ppc64le")
-    export COMPILER_ARCH="powerpc64le-linux-gnu"
-    ;;
-  "s390x")
-    export COMPILER_ARCH="s390x-linux-gnu"
-    ;;
-  "arm64")
-    export COMPILER_ARCH="aarch64-linux-gnu"
-    ;;
-  "arm")
-    case "$GOARM" in
-    "5")
-      export COMPILER_ARCH="arm-linux-gnueabi"
-      ;;
-    *)
-      export COMPILER_ARCH="arm-linux-gnueabihf"
-      ;;
-    esac
-    ;;
-  esac
-fi
-
-export CC="${COMPILER_ARCH}-gcc"
-export CXX="${COMPILER_ARCH}-g++"
-export PKG_CONFIG_PATH="/usr/lib/${COMPILER_ARCH}/pkgconfig/"
-
-if [ -z "$GOBIN" ] && [ -n "$GOPATH" ] && [ -n "$GOARCH" ] && [ -n "$GOOS" ]; then
-  export PATH=${GOPATH}/bin/${GOOS}_${GOARCH}:${PATH}
-fi
