ARG COMPOSER_VERSION
ARG TARGET_PHP_VERSION
FROM composer:${COMPOSER_VERSION} as composer
FROM php:${TARGET_PHP_VERSION}-fpm as base

# make build args available as ENV variables to downstream images
# so that we don't have to pass the same build args again
ARG APP_USER_ID
ARG APP_GROUP_ID
ARG APP_USER_NAME
ARG APP_GROUP_NAME
ARG APP_CODE_PATH

ARG ENV
ENV APP_USER_ID=${APP_USER_ID}
ENV APP_GROUP_ID=${APP_GROUP_ID}
ENV APP_USER_NAME=${APP_USER_NAME}
ENV APP_GROUP_NAME=${APP_GROUP_NAME}
ENV APP_CODE_PATH=${APP_CODE_PATH}
ENV TARGET_PHP_VERSION=${TARGET_PHP_VERSION}

ENV ENV=${ENV}

RUN addgroup -gid $APP_GROUP_ID $APP_GROUP_NAME && \
    adduser --disabled-password --uid $APP_USER_ID --shell /bin/bash --ingroup $APP_GROUP_NAME $APP_USER_NAME && \
    mkdir -p $APP_CODE_PATH && \
    chown $APP_USER_NAME: $APP_CODE_PATH

# install git-secret
# @see https://git-secret.io/installation#alpine
ADD https://gitsecret.jfrog.io/artifactory/api/security/keypair/public/repositories/git-secret-apk /etc/apk/keys/git-secret-apk.rsa.pub

# FYI, we are NOT using a cache mount to store the apk cache via
#   RUN --mount=type=cache,target=/var/cache/apk ln -vs /var/cache/apk /etc/apk/cache && \
# @see https://github.com/FernandoMiguel/BuildKit#new-dockerfile
# @see https://wiki.alpinelinux.org/wiki/Local_APK_cache#Enabling_Local_Cache_on_HDD_installs
# because we run --update anyways to get the latest files
RUN apt update && \
    apt install -y \
        bash \
        git \
        git-secret \
        # required for git-secret
        gawk \
        gnupg \
        make \
        strace \
        sudo \
        vim


# Install intl
RUN apt-get update && apt-get install -y \
        libicu-dev \
        libssl-dev \
        libcurl4-openssl-dev \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        libxml2-dev \
        libmagickwand-dev \
        git \
        zlib1g-dev \
        unzip \
        libzip-dev \
        mupdf-tools \
        imagemagick \
        libmcrypt-dev

# Install fileinfo
RUN docker-php-ext-install -j$(nproc) fileinfo
# Install intl
RUN docker-php-ext-install -j$(nproc) intl
# Install mongodb
RUN pecl install mongodb \
    && docker-php-ext-enable mongodb
# Install mcrypt
RUN pecl install mcrypt \
    && docker-php-ext-enable mcrypt
# Install curl
RUN docker-php-ext-install -j$(nproc) curl
# Install Zip
RUN docker-php-ext-install zip
# Install gd
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd
# Install soap
RUN docker-php-ext-install -j$(nproc) soap
# Install imagick
RUN pecl install imagick \
    && docker-php-ext-enable imagick
# Install mysql
RUN docker-php-ext-install -j$(nproc) pdo_mysql
# Install opcache
RUN docker-php-ext-install -j$(nproc) opcache
RUN apt-get update && apt-get install -y \
        libc-client-dev libkrb5-dev libldap2-dev && \
    rm -r /var/lib/apt/lists/*

# Install ldap
RUN docker-php-ext-install -j$(nproc) ldap

RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl && \
    docker-php-ext-install -j$(nproc) imap

# make bash default shell
RUN sed -e 's;/bin/ash$;/bin/bash;g' -i /etc/passwd

COPY ./.docker/images/php/base/conf.d/zz-app.ini $PHP_INI_DIR/conf.d/zz-app.ini
COPY ./.docker/images/php/base/conf.d/zz-app-${ENV}.ini $PHP_INI_DIR/conf.d/zz-ppp-${ENV}.ini

COPY ./.docker/images/php/base/.bashrc /home/${APP_USER_NAME}/.bashrc
COPY ./.docker/images/php/base/.bashrc /root/.bashrc

COPY --from=composer /usr/bin/composer /usr/local/bin/composer

# Fix git permission issue:
# `git` introduced a security feature to throw an error if the parent directory
# of the `.git` directory is owned by another user.
# @see https://github.blog/2022-04-12-git-security-vulnerability-announced/
# @see https://github.com/actions/checkout/issues/760
#
# Since we might not have full control over the owner
# ( see e.g. https://github.com/docker/for-win/issues/12742 )
# we will add the $APP_CODE_PATH as a "safe" directory to the global git config via
#  git config --system --add safe.directory "/path/to/git/parent/folder"
# @see https://git-scm.com/docs/git-config/2.36.0#Documentation/git-config.txt-safedirectory
#
# Without this fix, git-secret will emit the error
#  git-secret: abort: not in dir with git repo. Use 'git init' or 'git clone', then in repo use 'git secret init'
RUN git config --system --add safe.directory "$APP_CODE_PATH"

WORKDIR $APP_CODE_PATH

FROM base as codebase

# By only copying the composer files required to run composer install
# the layer will be cached and only invalidated when the composer dependencies are changed
COPY ./new/composer.json /dependencies/new/
COPY ./new/composer.lock /dependencies/new/

# use a cache mount to cache the composer dependencies
# this is essentially a cache that lives in Docker BuildKit (i.e. has nothing to do with the host system)
RUN --mount=type=cache,target=/tmp/.composer \
    cd /dependencies/new && \
    # COMPOSER_HOME=/tmp/.composer sets the home directory of composer that
    # also controls where composer looks for the cache
    # so we don't have to download dependencies again (if they are cached)
    # @see https://stackoverflow.com/a/60518444 for the correct if-then-else syntax:
    # - end all commands with ; \
    # - except THEN and ELSE
    if [ "$ENV" == "prod" ] ; \
    then \
      # on production, we don't want test dependencies
      COMPOSER_HOME=/tmp/.composer composer install --no-scripts --no-plugins --no-progress -o --no-dev; \
    else  \
      COMPOSER_HOME=/tmp/.composer composer install --no-scripts --no-plugins --no-progress -o; \
    fi

# copy the full codebase
COPY . /codebase

# move the dependencies
RUN mv /dependencies/vendor /codebase/new/vendor

# remove files we don't require in the image to keep the image size small
RUN cd /codebase && \
    rm -rf .docker/ .build/ .infrastructure/ && \
    if [ "$ENV" == "prod" ] ; \
    then \
      # on production, we don't want tests
      rm -rf tests/; \
    fi

# Remove all secrets that are NOT required for the given ENV:
#  `find /codebase/.secrets -type f -print` lists all files in the .secrets directory
#  `grep -v "/\(shared\|$ENV\)/"` matches only the files that are NOT in the shared/ or $ENV/ (e.g. prod/) directories
#  `grep -v ".secret\$"` ensures that we remove all files that are NOT ending in .secret
#    FYI:
#     the "$" has to be escaped with a "\"
#     "Escaping is possible by adding a \ before the variable"
#     @see https://docs.docker.com/engine/reference/builder/#environment-replacement
#  `xargs rm -f` retrieves the remaining file and deletes them
#    FYI:
#     `xargs` is necessary to convert the stdin to args for `rm`
#     @see https://stackoverflow.com/a/20307392/413531
#     the `-f` flag is required so that `rm` doesn't fail if no files are matched
RUN find /codebase/.secrets -type f -print | grep -v "/\(shared\|$ENV\)/" | xargs rm -f && \
    find /codebase/.secrets -type f -print | grep -v ".secret\$" | xargs rm -f && \
    # list the remaining files for debugging purposes
    find /codebase/.secrets -type f -print

# We need a git repository for git-secret to work (can be an empty one)
RUN cd /codebase && \
    git init

FROM base as prod

# We will use a custom ENTRYPOINT to decrypt the secrets when the container starts.
# This way, we can store the secrets in their encrypted form directly in the image.
# Note: Because we defined a custom ENTRYPOINT, the default CMD of the base image
#       will be overriden. Thus, we must explicitly re-define it here via `CMD ["/bin/sh"]`.
#       This behavior is described in the docs as:
#       "If CMD is defined from the base image, setting ENTRYPOINT will reset CMD to an empty value. In this scenario, CMD must be defined in the current image to have a value."
#       @see https://docs.docker.com/engine/reference/builder/#understand-how-cmd-and-entrypoint-interact
COPY ./.docker/images/php/base/decrypt-secrets.sh /decrypt-secrets.sh
RUN chmod +x /decrypt-secrets.sh
CMD ["/bin/sh"]
ENTRYPOINT ["/decrypt-secrets.sh"]

COPY --from=codebase --chown=$APP_USER_NAME:$APP_GROUP_NAME /codebase $APP_CODE_PATH

COPY --chown=$APP_USER_NAME:$APP_GROUP_NAME ./.build/build-info $APP_CODE_PATH/build-info

FROM base as ci

COPY --from=codebase --chown=$APP_USER_NAME:$APP_GROUP_NAME /codebase $APP_CODE_PATH

FROM base as local

# add app user to sudoers
# see https://ostechnix.com/add-delete-and-grant-sudo-privileges-to-users-in-alpine-linux/ for adding sudo
# see https://askubuntu.com/a/340669 for not requiring a sudo pw
RUN echo "root ALL=(ALL) NOPASSWD: ALL " | tee -a "/etc/sudoers.d/users" && \
    echo "${APP_USER_NAME} ALL=(ALL) NOPASSWD: ALL " | tee -a "/etc/sudoers.d/users"

RUN pecl install xdebug
