diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index 9c4cafe..97f8afa 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -1,66 +1,150 @@ -# This is a basic workflow to help you get started with Actions - name: Build & Publish Images -# Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the main branch push: branches: [main] - paths-ignore: ["**.md"] - # pull_request: - # branches: [ main ] - # paths-ignore: ['**.md'] - # Allows you to run this workflow manually from the Actions tab + paths-ignore: + - "**.md" + workflow_dispatch: + schedule: - cron: "0 3 3 * *" jobs: + prepare: + runs-on: ubuntu-latest + + outputs: + matrix: ${{ steps.matrix.outputs.matrix }} + + steps: + - name: Prepare build matrix + id: matrix + shell: bash + run: | + set -euo pipefail + + IMAGE="alchatti/drupal-devcontainer" + CONTEXT="." + TARGET="" + + PHP_VERSIONS='["8.1", "8.2", "8.3"]' + OS_VERSIONS='["bookworm", "trixie"]' + + DATE="$(date -u +'%Y%m%d')" + TIMESTAMP="$(date -u +'%Y%m%d%H%M%S')" + + BRANCH_NAME="${GITHUB_REF_NAME}" + + if [ "$BRANCH_NAME" = "main" ]; then + TAG_PREFIX="" + else + TAG_PREFIX="$(echo "$BRANCH_NAME" \ + | tr '[:upper:]' '[:lower:]' \ + | sed -E 's/[^a-z0-9_.-]+/-/g' \ + | sed -E 's/^-+|-+$//g')-" + fi + + MATRIX="$(jq -cn \ + --arg image "$IMAGE" \ + --arg context "$CONTEXT" \ + --arg target "$TARGET" \ + --arg date "$DATE" \ + --arg timestamp "$TIMESTAMP" \ + --arg tag_prefix "$TAG_PREFIX" \ + --argjson php_versions "$PHP_VERSIONS" \ + --argjson os_versions "$OS_VERSIONS" ' + [ + $php_versions[] as $php | + $os_versions[] as $os | + { + php: $php, + os: $os + } + ] as $items + | + [ + range(0; $items | length) as $i | + $items[$i] as $item | + ($i == (($items | length) - 1)) as $is_latest | + { + php: $item.php, + os: $item.os, + context: $context, + target: $target, + variant: ($item.php + "-" + $item.os), + build_args: ( + "VARIANT=" + $item.php + "\n" + + "NODE_VERSION=node\n" + + "OS=" + $item.os + "\n" + + "CREATE_DATE=" + $date + "\n" + + "BUILD_TIMESTAMP=" + $timestamp + ), + tags: ( + $image + ":" + $tag_prefix + $item.php + "-" + $item.os + "-" + $timestamp + "\n" + + $image + ":" + $tag_prefix + $item.php + "-" + $item.os + + ( + if $is_latest then + "\n" + $image + ":" + $tag_prefix + $item.php + "-" + $timestamp + + "\n" + $image + ":" + $tag_prefix + $item.php + else + "" + end + ) + ), + test_image_tag: ("local/drupal-devcontainer:" + $item.php + "-" + $item.os + "-ci") + } + ] + ')" + + echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT" + + { + echo "## Prepared Build Matrix" + echo "" + echo "| Field | Value |" + echo "|---|---|" + echo "| Branch | \`${BRANCH_NAME}\` |" + echo "| Tag prefix | \`${TAG_PREFIX}\` |" + echo "| Date | \`${DATE}\` |" + echo "| Timestamp | \`${TIMESTAMP}\` |" + echo "" + echo "### Matrix" + echo "" + echo '```json' + echo "$MATRIX" | jq . + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + build: + needs: prepare + + permissions: + contents: read + strategy: + max-parallel: 4 matrix: - php_ver: ["8.1", "8.2", "8.3"] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Get current date - id: date - run: echo "::set-output name=date::$(date +'%Y%m%d')" - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Docker Login - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: GitHub Container Registry Login - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.PKG_GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - # platforms: linux/arm64 - build-args: | - VARIANT=${{ matrix.php_ver }} - NODE_VERSION=node - CREATE_DATE=${{ steps.date.outputs.date }} - push: true - tags: | - alchatti/drupal-devcontainer:${{ matrix.php_ver }}-${{ steps.date.outputs.date }} - alchatti/drupal-devcontainer:${{ matrix.php_ver }} - ghcr.io/alchatti/drupal-devcontainer:${{ matrix.php_ver }}-${{ steps.date.outputs.date }} - ghcr.io/alchatti/drupal-devcontainer:${{ matrix.php_ver }} + include: ${{ fromJson(needs.prepare.outputs.matrix) }} + + uses: alchatti/.github/.github/workflows/docker-build-push.yml@main + + with: + context: ${{ matrix.context }} + dockerfile: ${{ matrix.context }}/Dockerfile + + build_args: ${{ matrix.build_args }} + tags: ${{ matrix.tags }} + target: ${{ matrix.target }} + + test_command: | + echo "required" + exit 1 + test_platform: linux/amd64 + test_image_tag: ${{ matrix.test_image_tag }} + + cache_from: type=gha,scope=${{ matrix.variant }} + cache_to: type=gha,mode=max,scope=${{ matrix.variant }} + + secrets: inherit diff --git a/Dockerfile b/Dockerfile index 485d383..fd5a17f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,209 +1,150 @@ -# Based on https://github.com/docker-library/drupal/blob/master/9.2/php8.0/apache-bullseye/Dockerfile file +# Drupal Development Container +# Based on: +# https://github.com/docker-library/drupal # Uses mcr.microsoft.com/vscode/devcontainers/php as base image -# For list of tags vsist https://mcr.microsoft.com/v2/vscode/devcontainers/php/tags/list -# Relesases code names at https://wiki.debian.org/DebianReleases#Production_Releases +# Tags: +# https://mcr.microsoft.com/v2/vscode/devcontainers/php/tags/list +# Debian releases: +# https://wiki.debian.org/DebianReleases#Production_Releases ARG VARIANT +ARG OS=bookworm -FROM mcr.microsoft.com/vscode/devcontainers/php:${VARIANT}-bookworm +FROM mcr.microsoft.com/vscode/devcontainers/php:${VARIANT}-${OS} ARG VARIANT +ARG OS=bookworm ARG CREATE_DATE -ARG NODE_VERSION +ARG NODE_VERSION=node ARG TARGETARCH LABEL org.opencontainers.image.title="Drupal Devcontainer Image with Node" -LABEL org.opencontainers.image.description="Drupal development image with PHP $VARIANT, Xdebug, Composer and Node.js" +LABEL org.opencontainers.image.description="Drupal development image with PHP ${VARIANT}, Xdebug, Composer and Node.js" LABEL org.opencontainers.image.authors="Majed Al-Chatti" LABEL org.opencontainers.image.source="https://github.com/alchatti/drupal-devcontainer-image" LABEL org.opencontainers.image.documentation="https://github.com/alchatti/drupal-devcontainer-image" -LABEL org.opencontainers.image.base.name="ghcr.io/alchatti/drupal-devcontainer" -LABEL org.opencontainers.image.ref.name="ghcr.io/alchatti/drupal-devcontainer:$VARIANT" -LABEL org.opencontainers.image.created=$CREATE_DATE - -# ENV Defaults fpr APACHE -ENV APACHE_SERVER_NAME="localhost" -ENV APACHE_DOCUMENT_ROOT "docroot" -ENV WORKSPACE_ROOT "/var/www/html" -# ENV Default theme for Oh My Posh -ENV POSH_THEME_ENVIRONMENT "ys" -# Shortcut to make development easier -ENV W $WORKSPACE_ROOT -ENV D $WORKSPACE_ROOT/$APACHE_DOCUMENT_ROOT - - -RUN echo "${CREATE_DATE}" >> /var/.buildInfo - -# Apache Configurations -RUN sed -ri -e 's!/var/www/html!${WORKSPACE_ROOT}/${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf -RUN echo "ServerName ${APACHE_SERVER_NAME}" >> /etc/apache2/apache2.conf -RUN echo "PassEnv APACHE_DOCUMENT_ROOT" >> /etc/apache2/apache2.conf -RUN echo "PassEnv WORKSPACE_ROOT" >> /etc/apache2/apache2.conf - -# Drupal filesystem -RUN mkdir /mnt/files && \ - chown -R www-data:www-data /mnt/files && \ - chmod -R 775 /mnt/files - -# PHP Development settings overwrite -COPY ./php.ini /usr/local/etc/php/conf.d/x-docker-dev-php.ini +LABEL org.opencontainers.image.base.name="mcr.microsoft.com/vscode/devcontainers/php:${VARIANT}-${OS}" +LABEL org.opencontainers.image.ref.name="ghcr.io/alchatti/drupal-devcontainer:${VARIANT}-${OS}" +LABEL org.opencontainers.image.created="${CREATE_DATE}" + +# Apache defaults +ENV APACHE_SERVER_NAME="localhost" \ + APACHE_DOCUMENT_ROOT="docroot" \ + WORKSPACE_ROOT="/var/www/html" \ + POSH_THEME_ENVIRONMENT="ys" \ + W="/var/www/html" \ + D="/var/www/html/docroot" + +# Prefer project Composer binaries first, then legacy image binaries. +# This makes /var/www/html/vendor/bin/drush win when available, +# and falls back to /opt/drush-legacy/drush when not available. +ENV PATH="/var/www/html/vendor/bin:/opt/drush-legacy:${PATH}" -# Init Script -COPY ./scripts/about.sh /usr/local/bin/ -COPY ./scripts/acli-dump.sh /usr/local/bin/acli-dump -COPY ./scripts/drestore.sh /usr/local/bin/drestore -COPY ./scripts/dump.sh /usr/local/bin/dump -COPY ./scripts/init.sh /usr/local/bin/ -COPY ./scripts/startup.sh /usr/local/bin/ - -RUN chmod +x /usr/local/bin/about.sh && \ - chmod +x /usr/local/bin/acli-dump && \ - chmod +x /usr/local/bin/drestore && \ - chmod +x /usr/local/bin/dump && \ - chmod +x /usr/local/bin/init.sh && \ - chmod +x /usr/local/bin/startup.sh - -RUN ln -s /usr/local/bin/php /usr/bin/php - -# Drush Launcher global drush as fallback -ENV DRUSH_LAUNCHER_FALLBACK /opt/drush -# Add Drush Launcher for Global and local Drush -ADD https://github.com/drush-ops/drush-launcher/releases/latest/download/drush.phar /usr/bin/drush -RUN chmod +rx /usr/bin/drush - -# Install Drush 8.* globally for D6, D7, D8.3- -# for D8.4+ use Drupal Composer site project with Drush listed as a dependency -ADD https://github.com/drush-ops/drush/releases/download/8.4.12/drush.phar /opt/drush -RUN chmod +rx /opt/drush - -# Add Acquia Cli -ADD https://github.com/acquia/cli/releases/latest/download/acli.phar /usr/local/bin/acli -RUN chmod +rx /usr/local/bin/acli - -# Acquia BLT Launcher -ADD https://github.com/acquia/blt-launcher/releases/latest/download/blt.phar /usr/local/bin/blt -RUN chmod +rx /usr/local/bin/blt - - -#Zsh Plugins -ADD https://github.com/zsh-users/zsh-autosuggestions/archive/refs/heads/master.zip /tmp/zsh-autosuggestions.zip -RUN unzip /tmp/zsh-autosuggestions.zip -d /tmp/zsh-autosuggestions \ - && mv /tmp/zsh-autosuggestions/zsh-autosuggestions-master /home/vscode/.oh-my-zsh/plugins/zsh-autosuggestions \ - && chown vscode:vscode /home/vscode/.oh-my-zsh/plugins/zsh-autosuggestions - -ADD https://github.com/zsh-users/zsh-syntax-highlighting/archive/refs/heads/master.zip /tmp/zsh-syntax-highlighting.zip -RUN unzip /tmp/zsh-syntax-highlighting.zip -d /tmp/zsh-syntax-highlighting \ - && mv /tmp/zsh-syntax-highlighting/zsh-syntax-highlighting-master /home/vscode/.oh-my-zsh/plugins/zsh-syntax-highlighting \ - && chown vscode:vscode /home/vscode/.oh-my-zsh/plugins/zsh-syntax-highlighting +RUN set -eux; \ + echo "${CREATE_DATE}" > /var/.buildInfo -# Oh My Posh -ADD https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-linux-${TARGETARCH} /usr/local/bin/oh-my-posh -RUN sudo chmod +x /usr/local/bin/oh-my-posh +# Shared installer scripts from docker-drupal - uses dev branch make sure to update +RUN set -eux; \ + curl -fsSL -o /usr/local/bin/install-php-dependencies \ + https://raw.githubusercontent.com/alchatti/docker-drupal/refs/heads/dev/scripts/install-php-dependencies.sh; \ + curl -fsSL -o /usr/local/bin/install-runtimes \ + https://raw.githubusercontent.com/alchatti/docker-drupal/refs/heads/dev/scripts/install-runtimes.sh; \ + chmod +x \ + /usr/local/bin/install-php-dependencies \ + /usr/local/bin/install-runtimes + +# Base packages, repositories, Apache, and Drupal filesystem +RUN set -eux; \ + # Fish + echo 'deb [signed-by=/etc/apt/keyrings/fish.gpg] http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_12/ /' > /etc/apt/sources.list.d/fish.list; \ + mkdir -p /etc/apt/keyrings; \ + curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:3/Debian_12/Release.key \ + | gpg --dearmor > /etc/apt/keyrings/fish.gpg; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + gnup \ + curl \ + ca-certificate \ + gettext-base \ + libpcre2-32-0 \ + build-essential \ + git-quick-stats \ + fish \ + a2enmod rewrite expire alias; \ + sed -ri -e 's!/var/www/html!${WORKSPACE_ROOT}/${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf; \ + echo "ServerName ${APACHE_SERVER_NAME}" >> /etc/apache2/apache2.conf; \ + echo "PassEnv APACHE_DOCUMENT_ROOT" >> /etc/apache2/apache2.conf; \ + echo "PassEnv WORKSPACE_ROOT" >> /etc/apache2/apache2.conf; \ + mkdir -p /mnt/files/{public,private,config}; \ + chown -R www-data:www-data /mnt/files; \ + chmod -R 775 /mnt/files; \ + install-php-dependencies.sh; \ + install-runtimes.sh --db + +# PHP development settings +COPY ./php.ini /usr/local/etc/php/conf.d/x-docker-dev-php.ini -ADD https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/themes.zip /opt/themes.zip -RUN chmod +x /usr/local/bin/oh-my-posh && \ - unzip /opt/themes.zip -d /opt/.poshthemes && \ - rm /opt/themes.zip && \ - chmod o+r /opt/.poshthemes/*.json +# Helper scripts +COPY --chmod=+x ./scripts /usr/local/bin -# Copy fish config -COPY ./config /home/vscode/.config/ -RUN chown -R vscode:vscode /home/vscode/.config - -# Zsh Startup -RUN echo startup.sh >> /home/vscode/.zshrc - -# Based on https://github.com/docker-library/drupal/blob/master/9.2/php8.0/apache-buster/Dockerfile -# install the PHP extensions we need - -#Enable Apachae Mods -RUN \ - a2enmod rewrite - -# Packages & Repositories - -## Add Fish to sources -RUN echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_12/ /' | sudo tee /etc/apt/sources.list.d/shells:fish:release:3.list; \ - curl -fsSL https://download.opensuse.org/repositories/shells:fish:release:3/Debian_12/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/shells_fish_release_3.gpg > /dev/null - -RUN apt update; \ - apt install -y --no-install-recommends \ - libfreetype6-dev \ - libjpeg-dev \ - zlib1g-dev \ - libpng-dev \ - libpq-dev \ - libzip-dev \ - libwebp-dev \ - default-mysql-client \ - gettext-base \ - libpcre2-32-0 \ - build-essential \ - git-quick-stats \ - fish \ - ; - -# Docker PHP Extensions & Config RUN set -eux; \ - \ - docker-php-ext-configure gd \ - --with-freetype \ - --with-jpeg=/usr \ - --with-webp \ - ; \ - \ - docker-php-ext-install -j "$(nproc)" \ - gd \ - opcache \ - pdo_mysql \ - pdo_pgsql \ - zip - -# Mark these as manually installed so they're not automatically removed by `apt-get autoremove` -RUN ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ - | awk '/=>/ { print $3 }' \ - | sort -u \ - | xargs -r dpkg-query -S \ - | cut -d: -f1 \ - | sort -u \ - | xargs -rt apt-mark manual + ln -sf /usr/local/bin/php /usr/bin/php +# Legacy Drush fallback. +# Project Drush from /var/www/html/vendor/bin/drush takes precedence through PATH. +RUN set -eux; \ + mkdir -p /opt/drush-legacy; \ + curl -fsSL -o /opt/drush-legacy/drush https://github.com/drush-ops/drush/releases/download/8.4.12/drush.phar; \ + chmod +rx /opt/drush-legacy/drush -USER vscode +# Acquia CLI and BLT launcher +RUN set -eux; \ + curl -fsSL -o /usr/local/bin/acli https://github.com/acquia/cli/releases/latest/download/acli.phar; \ + chmod +rx /usr/local/bin/acli -RUN echo "$(oh-my-posh init zsh)" >> ~/.zshrc && \ - sed -ri -e 's!export POSH_THEME=.*!export POSH_THEME="/opt/.poshthemes/$POSH_THEME_ENVIRONMENT.omp.json"!g' ~/.zshrc && \ - echo "exec \$SHELL -l" >> ~/.bashrc +# Oh My Posh +RUN set -eux; \ + curl -fsSL -o /usr/local/bin/oh-my-posh https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-linux-${TARGETARCH}; \ + chmod +x /usr/local/bin/oh-my-posh; \ + curl -fsSL -o /opt/themes.zip https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/themes.zip; \ + unzip /opt/themes.zip -d /opt/.poshthemes; \ + rm /opt/themes.zip; \ + chmod o+r /opt/.poshthemes/*.json + +# Copy shell config +COPY ./config /home/vscode/.config/ -RUN sed -ri -e 's!plugins=.*!plugins=(git zsh-autosuggestions zsh-syntax-highlighting)!g' ~/.zshrc +RUN set -eux; \ + chown -R vscode:vscode /home/vscode/.config -RUN mkdir ~/.acquia -RUN mkdir ~/.drush +USER vscode -RUN mkdir $WORKSPACE_ROOT/$APACHE_DOCUMENT_ROOT && \ - echo '> $WORKSPACE_ROOT/$APACHE_DOCUMENT_ROOT/index.php +# Shell configuration +RUN set -eux; \ + echo "$(oh-my-posh init zsh)" >> ~/.zshrc; \ + echo "exec \$SHELL -l" >> ~/.bashrc; \ + mkdir -p ~/.acquia ~/.drush -# Drupal Coder and phpcs Requirements -RUN composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true -RUN composer global require drupal/coder -RUN ~/.composer/vendor/bin/phpcs --config-set installed_paths ~/.composer/vendor/drupal/coder/coder_sniffer -RUN sudo ln -s ~/.composer/vendor/bin/phpcs /usr/local/bin/phpcs && \ - sudo ln -s ~/.composer/vendor/bin/phpcbf /usr/local/bin/phpcbf +# Placeholder Drupal document root +RUN set -eux; \ + mkdir -p "${WORKSPACE_ROOT}/${APACHE_DOCUMENT_ROOT}"; \ + echo ' "${WORKSPACE_ROOT}/${APACHE_DOCUMENT_ROOT}/index.php" +# Drupal Coder and PHPCS requirements +RUN set -eux; \ + composer global config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true; \ + composer global require drupal/coder; \ + COMPOSER_HOME="$(composer global config home --absolute)"; \ + "${COMPOSER_HOME}/vendor/bin/phpcs" --config-set installed_paths "${COMPOSER_HOME}/vendor/drupal/coder/coder_sniffer"; \ + sudo ln -sf "${COMPOSER_HOME}/vendor/bin/phpcs" /usr/local/bin/phpcs; \ + sudo ln -sf "${COMPOSER_HOME}/vendor/bin/phpcbf" /usr/local/bin/phpcbf USER root -RUN if [ "${NODE_VERSION}" != "none" ] && [ "${NODE_VERSION}" != "" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1 && npm install -g npm@latest"; fi - -# Clean Up +# Optional Node.js installation through nvm from the devcontainer base image RUN set -eux; \ - savedAptMark="$(apt-mark showmanual)"; \ - # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies - apt-mark auto '.*' > /dev/null; \ - apt-mark manual $savedAptMark; \ - apt purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ - rm -rf /var/lib/apt/lists/* \ - rm -r /tmp/*\ - ; + if [ "${NODE_VERSION}" != "none" ] && [ -n "${NODE_VERSION}" ]; then \ + su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} && npm install -g npm@latest"; \ + fi USER vscode