diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c6798f..fb4ff7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,90 +1,87 @@ name: CI on: - push: - paths-ignore: - - 'docker/**' pull_request: paths-ignore: - 'docker/**' env: - COMPOSER_ALLOW_SUPERUSER: '1' - SYMFONY_DEPRECATIONS_HELPER: max[self]=0 - ADMIN_LOGIN: admin - ADMIN_PASSWORD: test - DATABASE_URL: mysql://user:password@mysql:3306/test_db + COMPOSER_ALLOW_SUPERUSER: '1' + SYMFONY_DEPRECATIONS_HELPER: max[self]=0 + ADMIN_LOGIN: admin + ADMIN_PASSWORD: test + DATABASE_URL: mysql://user:password@mysql:3306/test_db jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - container: - image: php:8.2-alpine - options: >- - --tmpfs /tmp:exec - --tmpfs /var/tmp:exec - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install GD PHP extension - run: | - apk add $PHPIZE_DEPS libpng-dev - docker-php-ext-configure gd - docker-php-ext-install gd - - name: Install Composer - run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet - - name: Validate Composer - run: composer validate - - name: Update to highest dependencies with Composer - run: composer update --no-interaction --no-progress --ansi - - name: Analyze - run: PHP_CS_FIXER_IGNORE_ENV=True vendor/bin/php-cs-fixer fix --ansi + analyze: + name: Analyze + runs-on: ubuntu-latest + container: + image: php:8.2-alpine + options: >- + --tmpfs /tmp:exec + --tmpfs /var/tmp:exec + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install GD PHP extension + run: | + apk add $PHPIZE_DEPS libpng-dev + docker-php-ext-configure gd + docker-php-ext-install gd + - name: Install Composer + run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet + - name: Validate Composer + run: composer validate + - name: Update to highest dependencies with Composer + run: composer update --no-interaction --no-progress --ansi + - name: Analyze + run: PHP_CS_FIXER_IGNORE_ENV=True vendor/bin/php-cs-fixer fix --ansi - phpunit: - name: PHPUnit (PHP ${{ matrix.php }}) - runs-on: ubuntu-latest - container: - image: php:${{ matrix.php }}-alpine - options: >- - --tmpfs /tmp:exec - --tmpfs /var/tmp:exec - services: - mysql: - image: mariadb:10.7 - env: - MYSQL_DATABASE: test_db - MYSQL_USER: user - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: root - options: >- - --health-cmd "mysqladmin ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 3306:3306 - strategy: - matrix: - php: - - '8.0' - - '8.1' - - '8.2' - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install MySQL / GD PHP extensions - run: | - apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev - docker-php-ext-configure intl - docker-php-ext-configure gd - docker-php-ext-install pdo pdo_mysql intl gd - - name: Install Composer - run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet - - name: Install dependencies with Composer - run: composer install --no-progress --no-interaction --ansi - - name: Migrate database - run: bin/console doctrine:schema:update --force --no-interaction --complete - - name: Run tests with PHPUnit - run: vendor/bin/phpunit --process-isolation --colors=always + phpunit: + name: PHPUnit (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + container: + image: php:${{ matrix.php }}-alpine + options: >- + --tmpfs /tmp:exec + --tmpfs /var/tmp:exec + services: + mysql: + image: mariadb:10.7 + env: + MYSQL_DATABASE: test_db + MYSQL_USER: user + MYSQL_PASSWORD: password + MYSQL_ROOT_PASSWORD: root + options: >- + --health-cmd "mysqladmin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + strategy: + matrix: + php: + - '8.0' + - '8.1' + - '8.2' + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install MySQL / GD PHP extensions + run: | + apk add $PHPIZE_DEPS icu-libs icu-dev libpng-dev + docker-php-ext-configure intl + docker-php-ext-configure gd + docker-php-ext-install pdo pdo_mysql intl gd + - name: Install Composer + run: wget -qO - https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet + - name: Install dependencies with Composer + run: composer install --no-progress --no-interaction --ansi + - name: Migrate database + run: bin/console doctrine:schema:update --force --no-interaction --complete + - name: Run tests with PHPUnit + run: vendor/bin/phpunit --process-isolation --colors=always diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f53f94d..ce20fbf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,146 +2,65 @@ name: Publish Docker image on: workflow_dispatch: + pull_request: + push: + branches: + - main + - renovate/** release: - types: [published] + types: [ published ] + +permissions: + contents: read + packages: write env: - # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io - # github.repository as / - IMAGE_NAME: ${{ github.repository }} + IMAGE_NAME: ${{ github.repository }} # / + BUILD_ARCHITECTURES: linux/amd64,linux/arm64 jobs: - build: + build-image: name: Build Docker images runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - platform: - - linux/amd64 - - linux/arm64 steps: - - - name: Prepare - run: | - platform=${{ matrix.platform }} - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - - name: Checkout - uses: actions/checkout@v4 - - - name: Extract metadata - id: meta - uses: docker/metadata-action@v5 - with: - context: git - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - # "Push by digest" needs an untagged ref - tags: | - type=raw,value= - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - version: v0.12.0 - env: - BUILDX_NO_DEFAULT_ATTESTATIONS: 1 - - - name: Login to GitHub Container Registry (${{ env.REGISTRY }}) - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and Push by digest - id: build - uses: docker/build-push-action@v5 - with: - context: . - file: docker/Dockerfile - platforms: ${{ matrix.platform }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - - - name: Export digest - run: | - mkdir -p /tmp/digests - digest="${{ steps.build.outputs.digest }}" - touch "/tmp/digests/${digest#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@v4 + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v5 with: - name: digests-${{ env.PLATFORM_PAIR }} - path: /tmp/digests/* - if-no-files-found: error - retention-days: 1 + push: false + platforms: ${{ env.BUILD_ARCHITECTURES }} + cache-from: type=gha + cache-to: type=gha,mode=max - merge: - name: Create merged manifest and push to Github Packages + push-image: + name: Push images runs-on: ubuntu-latest - needs: - - build + needs: [ build-image ] + if: ${{ github.event_name == 'release' || github.ref_name == github.event.repository.default_branch}} steps: - - - # Needed to get the git information for the meta step - name: Checkout - uses: actions/checkout@v4 - - - name: Download digests - uses: actions/download-artifact@v4 + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 with: - path: /tmp/digests - pattern: digests-* - merge-multiple: true - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - version: v0.12.0 - env: - BUILDX_NO_DEFAULT_ATTESTATIONS: 1 - - - # Extract metadata to easily get the version and annotations to put in the manifest - name: Docker meta + registry: ${{ env.REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/metadata-action@v5 id: meta - uses: docker/metadata-action@v5 with: context: git images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | - type=semver,pattern={{version}} - type=edge,branch=${{ github.ref_name }} - - - name: Login to Docker Hub - uses: docker/login-action@v3 + type=edge,branch=${{ github.event.repository.default_branch }} + - uses: docker/build-push-action@v5 with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create manifest list and push - working-directory: /tmp/digests - run: | - docker buildx imagetools create \ - $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - --annotation index:org.opencontainers.image.created="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}" \ - --annotation index:org.opencontainers.image.description="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}" \ - --annotation index:org.opencontainers.image.version="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" \ - --annotation index:org.opencontainers.image.licenses="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.licenses'] }}" \ - --annotation index:org.opencontainers.image.title="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.title'] }}" \ - --annotation index:org.opencontainers.image.source="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.source'] }}" \ - --annotation index:org.opencontainers.image.url="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.url'] }}" \ - --annotation index:org.opencontainers.image.revision="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}" \ - $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) - - - name: Inspect image - run: | - docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} \ No newline at end of file + push: true + platforms: ${{ env.BUILD_ARCHITECTURES }} + cache-from: type=gha + cache-to: type=gha,mode=max + annotations: ${{ steps.meta.outputs.annotations }} + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..637e651 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,99 @@ +# https://github.com/docker-library/php/blob/master/8.2/alpine3.18/fpm/Dockerfile#L33 +ARG fpm_user=82:82 + +############################################################### +# Run update, and gets basic packages and packages for runtime +FROM php:8.2-fpm-alpine AS base-image + +RUN apk --no-progress --update add --no-cache \ + curl unzip \ + # Runtime dependencies + # php-intl + icu-libs \ + # PostgreSQL + libpq \ + # GD (map image in mail) + freetype \ + libjpeg-turbo \ + libpng \ + # LDAP + libldap \ + # IMAP (provides libc-client.so) + c-client + +########################################################################### +# Build all extension on a diferent build environment so keep things clean +FROM base-image AS extension-builder + +RUN apk --update --no-cache add \ + # Compilation dependencies + # Intl + --virtual build-deps-intl \ + icu-dev \ + # PostgreSQL + --virtual build-deps-pg \ + libpq-dev \ + # GD (map image in mail) + --virtual build-deps-gd \ + freetype-dev \ + libjpeg-turbo-dev \ + libpng-dev \ + # LDAP + --virtual build-deps-ldap \ + openldap-dev \ + # IMAP + --virtual build-deps-imap \ + imap-dev \ + openssl-dev \ + krb5-dev + +# Leaving a Docker layer per extension so in case it fails I have it cached to develop faster. + +# Intl support +RUN docker-php-ext-install intl +# PDO: MySQL +RUN docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd && \ + docker-php-ext-install pdo_mysql +# PDO: PostgreSQL +RUN docker-php-ext-configure pgsql --with-pgsql=/usr/local/pgsql && \ + docker-php-ext-install pgsql pdo_pgsql +# GD (map image in mail) +RUN docker-php-ext-configure gd --with-freetype && \ + docker-php-ext-install gd && \ + docker-php-ext-enable gd +# LDAP auth support +RUN docker-php-ext-configure ldap && \ + docker-php-ext-install ldap +# IMAP auth support +RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl && \ + docker-php-ext-install imap + + +################################################### +# Installing composer and downloading dependencies +FROM extension-builder AS composer + +# Install Composer 2, then dependencies +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +ADD --chown=www-data:www-data . /var/www/davis/ +WORKDIR /var/www/davis +RUN APP_ENV=prod COMPOSER_ALLOW_SUPERUSER=1 composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader + + +################################################### +# Image that composer all the build steps +FROM scratch AS squash-this-layer + +COPY --from=extension-builder /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d/ +COPY --from=extension-builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions/ +COPY --from=composer /var/www/davis/vendor /var/www/davis/vendor/ + +ADD --chown=$FPM_USER bin migrations public src /var/www/davis/ + +################################################### +# Final image +FROM base-image + +COPY --from=squash-this-layer / / + +USER $FPM_USER diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index dd7784e..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,89 +0,0 @@ -FROM php:8.2-fpm-alpine - -# https://github.com/docker-library/php/blob/master/8.2/alpine3.18/fpm/Dockerfile#L33 -ARG fpm_user=82:82 -ENV FPM_USER=${fpm_user} - -LABEL org.opencontainers.image.authors="tchap@tchap.me" -LABEL org.opencontainers.image.url="https://github.com/tchapi/davis/pkgs/container/davis" -LABEL org.opencontainers.image.description="A simple, fully translatable admin interface for sabre/dav based on Symfony 5 and Bootstrap 4" - -# Run update, and gets basic packages and packages for runtime -RUN apk --no-progress --update add --no-cache \ - curl unzip fcgi \ - # These are for php-intl - icu-libs \ - # This one is for IMAP (to provide libc-client.so) - c-client \ - # This one for LDAP - libldap \ - # These are for GD (map image in mail) - freetype \ - libjpeg-turbo \ - libpng \ - # This is for PostgreSQL - libpq - -# Intl support -RUN apk --update --virtual build-deps-intl add --no-cache icu-dev \ - && docker-php-ext-install intl \ - && apk del build-deps-intl \ - && rm -rf /tmp/* - -# PDO: MySQL -RUN docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd \ - && docker-php-ext-install pdo_mysql - -# PDO: PostgreSQL -RUN apk --update --virtual build-deps-pg add --no-cache libpq-dev \ - && docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \ - && docker-php-ext-install pgsql pdo_pgsql \ - && apk del build-deps-pg \ - && rm -rf /tmp/* - -# GD (map image in mail) -RUN apk --update --virtual build-deps-gd add --no-cache freetype-dev libjpeg-turbo-dev libpng-dev \ - && docker-php-ext-configure gd --with-freetype \ - && docker-php-ext-install gd \ - && docker-php-ext-enable gd \ - && apk del build-deps-gd \ - && rm -rf /tmp/* - -# LDAP auth support -RUN apk --update --virtual build-deps-ldap add --no-cache openldap-dev \ - && docker-php-ext-configure ldap \ - && docker-php-ext-install ldap \ - && apk del build-deps-ldap \ - && rm -rf /tmp/* - -# IMAP auth support -RUN apk --update --virtual build-deps-imap add --no-cache imap-dev openssl-dev krb5-dev \ - && docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ - && docker-php-ext-install imap \ - && apk del build-deps-imap \ - && rm -rf /tmp/* - -# Davis installation -ADD . /var/www/davis -WORKDIR /var/www/davis - -# Install Composer 2, then dependencies -RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -RUN APP_ENV=prod COMPOSER_ALLOW_SUPERUSER=1 composer install --no-ansi --no-dev --no-interaction --no-progress --optimize-autoloader - -# PHP-FPM healthcheck -RUN set -xe && echo "pm.status_path = /status" >> /usr/local/etc/php-fpm.d/zz-docker.conf -RUN curl https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/v0.5.0/php-fpm-healthcheck \ - -o /usr/local/bin/php-fpm-healthcheck -s \ - && chmod +x /usr/local/bin/php-fpm-healthcheck - -# Cleanup (only useful when using --squash) -RUN docker-php-source delete -RUN rm -rf /usr/local/bin/composer - -# Sets the app folder owner as the FPM user -RUN chown -R ${FPM_USER} . - -USER $FPM_USER - -HEALTHCHECK --interval=30s --timeout=1s CMD php-fpm-healthcheck || exit 1