diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8e85703 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +--- +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..9a507e8 --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,12 @@ +# The labels in this file are automatically synced with the repository +# using the micnncim/action-label-syncer action. +--- +- name: C-dependency + color: 1abc9c + description: "Category: Dependency" +- name: PR-block + color: 3498db + description: "Pull Request: Do not merge" +- name: PR-merge + color: 3498db + description: "Pull Request: Merge when ready" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..03439b4 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,33 @@ +--- +name-template: '$RESOLVED_VERSION 🌈' +tag-template: '$RESOLVED_VERSION' +version-template: '$MAJOR.$MINOR' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: minor +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/action_branch.yml b/.github/workflows/action_branch.yml new file mode 100644 index 0000000..55d6950 --- /dev/null +++ b/.github/workflows/action_branch.yml @@ -0,0 +1,33 @@ +--- + +# ------------------------------------------------------------------------------------------------- +# Job Name +# ------------------------------------------------------------------------------------------------- +name: build + + +# ------------------------------------------------------------------------------------------------- +# When to run +# ------------------------------------------------------------------------------------------------- +on: + push: + + +jobs: + + # (1/2) Determine repository params + params: + uses: ./.github/workflows/params.yml + + # (2/2) Build + docker: + needs: [params] + uses: devilbox/github-actions/.github/workflows/docker-name-version-arch.yml@master + with: + enabled: true + can_deploy: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/heads/release-') }} + matrix: ${{ needs.params.outputs.matrix }} + refs: ${{ needs.params.outputs.refs }} + secrets: + dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/action_pull_request.yml b/.github/workflows/action_pull_request.yml new file mode 100644 index 0000000..c80bf78 --- /dev/null +++ b/.github/workflows/action_pull_request.yml @@ -0,0 +1,35 @@ +--- + +# ------------------------------------------------------------------------------------------------- +# Job Name +# ------------------------------------------------------------------------------------------------- +name: build + + +# ------------------------------------------------------------------------------------------------- +# When to run +# ------------------------------------------------------------------------------------------------- +on: + pull_request: + + +jobs: + + # (1/2) Determine repository params + params: + uses: ./.github/workflows/params.yml + # Only run for forks (contributor) + if: github.event.pull_request.head.repo.fork + + # (2/2) Build + docker: + needs: [params] + uses: devilbox/github-actions/.github/workflows/docker-name-version-arch.yml@master + with: + enabled: true + can_deploy: false + matrix: ${{ needs.params.outputs.matrix }} + refs: ${{ needs.params.outputs.refs }} + secrets: + dockerhub_username: "" + dockerhub_password: "" diff --git a/.github/workflows/action_schedule.yml b/.github/workflows/action_schedule.yml new file mode 100644 index 0000000..36d979a --- /dev/null +++ b/.github/workflows/action_schedule.yml @@ -0,0 +1,35 @@ +--- + +# ------------------------------------------------------------------------------------------------- +# Job Name +# ------------------------------------------------------------------------------------------------- +name: nightly + + +# ------------------------------------------------------------------------------------------------- +# When to run +# ------------------------------------------------------------------------------------------------- +on: + # Runs daily + schedule: + - cron: '0 0 * * *' + + +jobs: + + # (1/2) Determine repository params + params: + uses: ./.github/workflows/params.yml + + # (2/2) Build + docker: + needs: [params] + uses: devilbox/github-actions/.github/workflows/docker-name-version-arch.yml@master + with: + enabled: true + can_deploy: true + matrix: ${{ needs.params.outputs.matrix }} + refs: ${{ needs.params.outputs.refs }} + secrets: + dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub_password: ${{ secrets.DOCKERHUB_PASSWORD }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..f83d099 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +--- + +# ------------------------------------------------------------------------------------------------- +# Job Name +# ------------------------------------------------------------------------------------------------- +name: lint + + +# ------------------------------------------------------------------------------------------------- +# When to run +# ------------------------------------------------------------------------------------------------- +on: + # Runs on Pull Requests + pull_request: + + +# ------------------------------------------------------------------------------------------------- +# What to run +# ------------------------------------------------------------------------------------------------- +jobs: + lint: + uses: devilbox/github-actions/.github/workflows/lint-generic.yml@master diff --git a/.github/workflows/params.yml b/.github/workflows/params.yml new file mode 100644 index 0000000..30dc89d --- /dev/null +++ b/.github/workflows/params.yml @@ -0,0 +1,69 @@ +--- + +# ------------------------------------------------------------------------------------------------- +# Job Name +# ------------------------------------------------------------------------------------------------- +name: params + + +# ------------------------------------------------------------------------------------------------- +# Custom Variables +# ------------------------------------------------------------------------------------------------- +env: + MATRIX: >- + [ + { + "NAME": "PHP", + "VERSION": ["5.5"], + "ARCH": ["linux/amd64", "linux/386", "linux/arm64", "linux/arm/v7", "linux/arm/v6"] + } + ] + + +# ------------------------------------------------------------------------------------------------- +# When to run +# ------------------------------------------------------------------------------------------------- +on: + workflow_call: + outputs: + matrix: + description: "The determined version matrix" + value: ${{ jobs.params.outputs.matrix }} + refs: + description: "The determined git ref matrix (only during scheduled run)" + value: ${{ jobs.params.outputs.refs }} + +jobs: + params: + runs-on: ubuntu-latest + + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + refs: ${{ steps.set-refs.outputs.matrix }} + + steps: + - name: "[Set-Output] Matrix" + id: set-matrix + run: | + echo "::set-output name=matrix::$( echo '${{ env.MATRIX }}' | jq -M -c )" + + - name: "[Set-Output] Matrix 'Refs' (master branch and latest tag)" + id: set-refs + uses: cytopia/git-ref-matrix-action@v0.1.4 + with: + repository_default_branch: master + branches: master + num_latest_tags: 1 + if: github.event_name == 'schedule' + + - name: "[DEBUG] Show settings'" + run: | + echo 'Matrix' + echo '--------------------' + echo '${{ steps.set-matrix.outputs.matrix }}' + echo + + echo 'Matrix: Refs' + echo '--------------------' + echo '${{ steps.set-matrix-refs.outputs.matrix }}' + echo diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..1a63d7e --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,19 @@ +--- +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + publish: true + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_TOKEN }} diff --git a/.github/workflows/repository.yml b/.github/workflows/repository.yml new file mode 100644 index 0000000..ca21e7d --- /dev/null +++ b/.github/workflows/repository.yml @@ -0,0 +1,25 @@ +--- +name: Repository + +on: + push: + branches: + - master + paths: + - .github/labels.yml + +jobs: + labels: + name: Labels + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Sync labels + uses: micnncim/action-label-syncer@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + manifest: .github/labels.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7457dff --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +Makefile.docker +Makefile.lint diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..2c7758f --- /dev/null +++ b/.yamllint @@ -0,0 +1,13 @@ +--- +extends: default + +ignore: | + .yamllint + + +rules: + truthy: + allowed-values: ['true', 'false'] + check-keys: False + level: error + line-length: disable diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..de4295f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,133 @@ +FROM debian:stretch +MAINTAINER "cytopia" + +# persistent / runtime deps +RUN set -eux \ + && DEBIAN_FRONTEND=noninteractive apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends --no-install-suggests \ + ca-certificates \ + curl \ + libpcre3 \ + librecode0 \ + libmariadbclient-dev-compat \ + libsqlite3-0 \ + libxml2 \ + && DEBIAN_FRONTEND=noninteractive apt-get purge -qq -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +# phpize deps +RUN set -eux \ + && DEBIAN_FRONTEND=noninteractive apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends --no-install-suggests \ + autoconf \ + file \ + dpkg-dev \ + g++ \ + gcc \ + libc-dev \ + make \ + pkg-config \ + re2c \ + xz-utils \ + && if [ "$(dpkg-architecture --query DEB_HOST_ARCH)" = "i386" ]; then \ + DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends --no-install-suggests \ + g++-multilib \ + gcc-multilib; \ + fi \ + && DEBIAN_FRONTEND=noninteractive apt-get purge -qq -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* + +ENV PHP_INI_DIR /usr/local/etc/php +RUN mkdir -p $PHP_INI_DIR/conf.d + +# compile openssl, otherwise --with-openssl won't work +RUN set -eux \ + && OPENSSL_VERSION="1.0.1t" \ + && cd /tmp \ + && mkdir openssl \ + && update-ca-certificates \ + && curl -sS -k -L --fail "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" -o openssl.tar.gz \ + && curl -sS -k -L --fail "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz.asc" -o openssl.tar.gz.asc \ + && tar -xzf openssl.tar.gz -C openssl --strip-components=1 \ + && cd /tmp/openssl \ + && if [ "$(dpkg-architecture --query DEB_HOST_ARCH)" = "i386" ]; then \ + setarch i386 ./config -m32; \ + else \ + ./config; \ + fi \ + && make depend \ + && make -j"$(nproc)" \ + && make install \ + && rm -rf /tmp/* + +ENV PHP_VERSION 5.5.38 +COPY data/docker-php-source /usr/local/bin/ +COPY data/php-${PHP_VERSION}.tar.xz /usr/src/php.tar.xz + +RUN set -eux \ + && buildDeps=" \ + autoconf2.13 \ + libcurl4-openssl-dev \ + libpcre3-dev \ + libreadline6-dev \ + librecode-dev \ + libsqlite3-dev \ + libssl-dev \ + libxml2-dev \ + " \ + && DEBIAN_FRONTEND=noninteractive apt-get update -qq \ + && DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends --no-install-suggests \ + ${buildDeps} \ + && DEBIAN_FRONTEND=noninteractive apt-get purge -qq -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + && rm -rf /var/lib/apt/lists/* \ + \ + && mkdir -p /usr/src/php \ + && docker-php-source extract \ + && cd /usr/src/php \ + \ + && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ + && debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)" \ + \ + # Fix libmariadbclient lib location + && find /usr/lib/ -name '*mariadbclient*' | xargs -n1 sh -c 'ln -s "${1}" "/usr/lib/$( basename "${1}" | sed "s|libmariadbclient|libmysqlclient|g" )"' -- \ + \ + # https://bugs.php.net/bug.php?id=74125 + && if [ ! -d /usr/include/curl ]; then \ + ln -sT "/usr/include/$debMultiarch/curl" /usr/local/include/curl; \ + fi \ + \ + && ./configure \ + --host="${gnuArch}" \ + --with-config-file-path="$PHP_INI_DIR" \ + --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \ + --enable-fpm \ + --with-fpm-user=www-data \ + --with-fpm-group=www-data \ + --disable-cgi \ + --enable-mysqlnd \ + --with-mysql \ + --with-curl \ + --with-openssl=/usr/local/ssl \ + --with-readline \ + --with-recode \ + --with-zlib \ + && make -j"$(nproc)" \ + && make install \ + && make clean \ + \ + && { find /usr/local/bin /usr/local/sbin -type f -executable -exec strip --strip-all '{}' + || true; } \ + \ + && cd / \ + && docker-php-source delete \ + \ + && DEBIAN_FRONTEND=noninteractive apt-get purge -qq -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false ${buildDeps} \ + && rm -rf /var/lib/apt/lists/* + +COPY data/docker-php-* /usr/local/bin/ + +WORKDIR /var/www/html +COPY data/php-fpm.conf /usr/local/etc/ +COPY data/php.ini /usr/local/etc/php/php.ini + +EXPOSE 9000 +CMD ["php-fpm"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fb7282f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 cytopia + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bc752c8 --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +ifneq (,) +.error This Makefile requires GNU Make. +endif + +# Ensure additional Makefiles are present +MAKEFILES = Makefile.docker Makefile.lint +$(MAKEFILES): URL=https://raw.githubusercontent.com/devilbox/makefiles/master/$(@) +$(MAKEFILES): + @if ! (curl --fail -sS -o $(@) $(URL) || wget -O $(@) $(URL)); then \ + echo "Error, curl or wget required."; \ + echo "Exiting."; \ + false; \ + fi +include $(MAKEFILES) + +# Set default Target +.DEFAULT_GOAL := help + + +# ------------------------------------------------------------------------------------------------- +# Default configuration +# ------------------------------------------------------------------------------------------------- +# Own vars +TAG = latest + +# Makefile.docker overwrites +NAME = PHP +VERSION = 5.5 +IMAGE = devilbox/php-fpm-$(VERSION) +DIR = . +FILE = Dockerfile +DOCKER_TAG = $(TAG) +ARCH = linux/amd64 + +# Makefile.lint overwrites +FL_IGNORES = .git/,.github/,tests/,data/ +SC_IGNORES = .git/,.github/,tests/ + + +# ------------------------------------------------------------------------------------------------- +# Default Target +# ------------------------------------------------------------------------------------------------- +.PHONY: help +help: + @echo "lint Lint project files and repository" + @echo + @echo "build [ARCH=...] [TAG=...] Build Docker image" + @echo "rebuild [ARCH=...] [TAG=...] Build Docker image without cache" + @echo "push [ARCH=...] [TAG=...] Push Docker image to Docker hub" + @echo + @echo "manifest-create [ARCHES=...] [TAG=...] Create multi-arch manifest" + @echo "manifest-push [TAG=...] Push multi-arch manifest" + @echo + @echo "test [ARCH=...] Test built Docker image" + @echo + + +# ------------------------------------------------------------------------------------------------- +# Docker Targets +# ------------------------------------------------------------------------------------------------- +.PHONY: build +build: docker-arch-build + +.PHONY: rebuild +rebuild: docker-arch-rebuild + +.PHONY: push +push: docker-arch-push + + +# ------------------------------------------------------------------------------------------------- +# Manifest Targets +# ------------------------------------------------------------------------------------------------- +.PHONY: manifest-create +manifest-create: docker-manifest-create + +.PHONY: manifest-push +manifest-push: docker-manifest-push + + +# ------------------------------------------------------------------------------------------------- +# Test Targets +# ------------------------------------------------------------------------------------------------- +.PHONY: test +test: _test-integration +test: update-readme + +.PHONY: _test-integration +_test-integration: + ./tests/start-ci.sh $(IMAGE) $(NAME) $(VERSION) $(DOCKER_TAG) $(ARCH) + +.PHONY: update-readme +update-readme: + cat "./README.md" \ + | perl -0 -pe "s/.*/\n$$(./tests/get-modules.sh $(IMAGE) $(NAME) $(VERSION) $(DOCKER_TAG) $(ARCH))\n/s" \ + > "./README.md.tmp" + yes | mv -f "./README.md.tmp" "./README.md" + git diff --quiet || { echo "Build Changes"; git diff; git status; false; } diff --git a/README.md b/README.md index d332d88..c70683d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![](https://images.microbadger.com/badges/image/devilbox/php-fpm-5.5.svg)](https://microbadger.com/images/devilbox/php-fpm-5.5 "php-fpm-5.5") [![License](https://img.shields.io/badge/license-MIT-%233DA639.svg)](https://opensource.org/licenses/MIT) -**Available Architectures:** `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6`, `ppc64le`, `s390x`, `mips64le` +**Available Architectures:** `amd64`, `i386`, `arm64`, `arm/v7`, `arm/v6` This repository will provide you a fully functional PHP-FPM 5.5 Docker image built from [official sources](http://php.net) nightly. PHP 5.5 [reached EOL](http://php.net/eol.php) on 10 Jul 2016. It provides the base for [Devilbox PHP-FPM Docker images](https://github.com/devilbox/docker-php-fpm). @@ -25,7 +25,7 @@ Have a look at the following similar Devilbox base images for which no official * [PHP-FPM 5.2](https://github.com/devilbox/docker-php-fpm-5.2) * [PHP-FPM 5.3](https://github.com/devilbox/docker-php-fpm-5.3) -* [PHP-FPM 5.5](https://github.com/devilbox/docker-php-fpm-5.5) +* [PHP-FPM 5.4](https://github.com/devilbox/docker-php-fpm-5.4) * [PHP-FPM 5.5](https://github.com/devilbox/docker-php-fpm-5.5) * [PHP-FPM 7.4](https://github.com/devilbox/docker-php-fpm-7.4) * [PHP-FPM 8.0](https://github.com/devilbox/docker-php-fpm-8.0) diff --git a/data/docker-php-ext-configure b/data/docker-php-ext-configure new file mode 100755 index 0000000..9e949e1 --- /dev/null +++ b/data/docker-php-ext-configure @@ -0,0 +1,69 @@ +#!/bin/sh +set -e + +# prefer user supplied CFLAGS, but default to our PHP_CFLAGS +: ${CFLAGS:=$PHP_CFLAGS} +: ${CPPFLAGS:=$PHP_CPPFLAGS} +: ${LDFLAGS:=$PHP_LDFLAGS} +export CFLAGS CPPFLAGS LDFLAGS + +srcExists= +if [ -d /usr/src/php ]; then + srcExists=1 +fi +docker-php-source extract +if [ -z "$srcExists" ]; then + touch /usr/src/php/.docker-delete-me +fi + +cd /usr/src/php/ext + +usage() { + echo "usage: $0 ext-name [configure flags]" + echo " ie: $0 gd --with-jpeg-dir=/usr/local/something" + echo + echo 'Possible values for ext-name:' + find . \ + -mindepth 2 \ + -maxdepth 2 \ + -type f \ + -name 'config.m4' \ + | xargs -n1 dirname \ + | xargs -n1 basename \ + | sort \ + | xargs + echo + echo 'Some of the above modules are already compiled into PHP; please check' + echo 'the output of "php -i" to see which modules are already loaded.' +} + +ext="$1" +if [ -z "$ext" ] || [ ! -d "$ext" ]; then + usage >&2 + exit 1 +fi +shift + +pm='unknown' +if [ -e /lib/apk/db/installed ]; then + pm='apk' +fi + +if [ "$pm" = 'apk' ]; then + if \ + [ -n "$PHPIZE_DEPS" ] \ + && ! apk info --installed .phpize-deps > /dev/null \ + && ! apk info --installed .phpize-deps-configure > /dev/null \ + ; then + apk add --no-cache --virtual .phpize-deps-configure $PHPIZE_DEPS + fi +fi + +if command -v dpkg-architecture > /dev/null; then + gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" + set -- --build="$gnuArch" "$@" +fi + +cd "$ext" +phpize +./configure "$@" diff --git a/data/docker-php-ext-enable b/data/docker-php-ext-enable new file mode 100755 index 0000000..a2aa88e --- /dev/null +++ b/data/docker-php-ext-enable @@ -0,0 +1,114 @@ +#!/bin/sh +set -e + +extDir="$(php -r 'echo ini_get("extension_dir");')" +cd "$extDir" + +usage() { + echo "usage: $0 [options] module-name [module-name ...]" + echo " ie: $0 gd mysqli" + echo " $0 pdo pdo_mysql" + echo " $0 --ini-name 0-apc.ini apcu apc" + echo + echo 'Possible values for module-name:' + find -maxdepth 1 \ + -type f \ + -name '*.so' \ + -exec basename '{}' ';' \ + | sort \ + | xargs + echo + echo 'Some of the above modules are already compiled into PHP; please check' + echo 'the output of "php -i" to see which modules are already loaded.' +} + +opts="$(getopt -o 'h?' --long 'help,ini-name:' -- "$@" || { usage >&2 && false; })" +eval set -- "$opts" + +iniName= +while true; do + flag="$1" + shift + case "$flag" in + --help|-h|'-?') usage && exit 0 ;; + --ini-name) iniName="$1" && shift ;; + --) break ;; + *) + { + echo "error: unknown flag: $flag" + usage + } >&2 + exit 1 + ;; + esac +done + +modules= +for module; do + if [ -z "$module" ]; then + continue + fi + if [ -f "$module.so" ] && ! [ -f "$module" ]; then + # allow ".so" to be optional + module="$module.so" + fi + if ! [ -f "$module" ]; then + echo >&2 "error: '$module' does not exist" + echo >&2 + usage >&2 + exit 1 + fi + modules="$modules $module" +done + +if [ -z "$modules" ]; then + usage >&2 + exit 1 +fi + +pm='unknown' +if [ -e /lib/apk/db/installed ]; then + pm='apk' +fi + +apkDel= +if [ "$pm" = 'apk' ]; then + if \ + [ -n "$PHPIZE_DEPS" ] \ + && ! apk info --installed .phpize-deps > /dev/null \ + && ! apk info --installed .phpize-deps-configure > /dev/null \ + ; then + apk add --no-cache --virtual '.docker-php-ext-enable-deps' binutils + apkDel='.docker-php-ext-enable-deps' + fi +fi + +for module in $modules; do + if readelf --wide --syms "$module" | grep -q ' zend_extension_entry$'; then + # https://wiki.php.net/internals/extensions#loading_zend_extensions + absModule="$(readlink -f "$module")" + line="zend_extension=$absModule" + else + line="extension=$module" + fi + + ext="$(basename "$module")" + ext="${ext%.*}" + if php -r 'exit(extension_loaded("'"$ext"'") ? 0 : 1);'; then + # this isn't perfect, but it's better than nothing + # (for example, 'opcache.so' presents inside PHP as 'Zend OPcache', not 'opcache') + echo >&2 + echo >&2 "warning: $ext ($module) is already loaded!" + echo >&2 + continue + fi + + ini="/usr/local/etc/php/conf.d/${iniName:-"docker-php-ext-$ext.ini"}" + if ! grep -q "$line" "$ini" 2>/dev/null; then + echo "$line" >> "$ini" + fi +done + +if [ "$pm" = 'apk' ] && [ -n "$apkDel" ]; then + apk del $apkDel +fi diff --git a/data/docker-php-ext-install b/data/docker-php-ext-install new file mode 100755 index 0000000..d75d6d7 --- /dev/null +++ b/data/docker-php-ext-install @@ -0,0 +1,122 @@ +#!/bin/sh +set -e + +# prefer user supplied CFLAGS, but default to our PHP_CFLAGS +: ${CFLAGS:=$PHP_CFLAGS} +: ${CPPFLAGS:=$PHP_CPPFLAGS} +: ${LDFLAGS:=$PHP_LDFLAGS} +export CFLAGS CPPFLAGS LDFLAGS + +srcExists= +if [ -d /usr/src/php ]; then + srcExists=1 +fi +docker-php-source extract +if [ -z "$srcExists" ]; then + touch /usr/src/php/.docker-delete-me +fi + +cd /usr/src/php/ext + +usage() { + echo "usage: $0 [-jN] ext-name [ext-name ...]" + echo " ie: $0 gd mysqli" + echo " $0 pdo pdo_mysql" + echo " $0 -j5 gd mbstring mysqli pdo pdo_mysql shmop" + echo + echo 'if custom ./configure arguments are necessary, see docker-php-ext-configure' + echo + echo 'Possible values for ext-name:' + find . \ + -mindepth 2 \ + -maxdepth 2 \ + -type f \ + -name 'config.m4' \ + | xargs -n1 dirname \ + | xargs -n1 basename \ + | sort \ + | xargs + echo + echo 'Some of the above modules are already compiled into PHP; please check' + echo 'the output of "php -i" to see which modules are already loaded.' +} + +opts="$(getopt -o 'h?j:' --long 'help,jobs:' -- "$@" || { usage >&2 && false; })" +eval set -- "$opts" + +j=1 +while true; do + flag="$1" + shift + case "$flag" in + --help|-h|'-?') usage && exit 0 ;; + --jobs|-j) j="$1" && shift ;; + --) break ;; + *) + { + echo "error: unknown flag: $flag" + usage + } >&2 + exit 1 + ;; + esac +done + +exts= +for ext; do + if [ -z "$ext" ]; then + continue + fi + if [ ! -d "$ext" ]; then + echo >&2 "error: $PWD/$ext does not exist" + echo >&2 + usage >&2 + exit 1 + fi + exts="$exts $ext" +done + +if [ -z "$exts" ]; then + usage >&2 + exit 1 +fi + +pm='unknown' +if [ -e /lib/apk/db/installed ]; then + pm='apk' +fi + +apkDel= +if [ "$pm" = 'apk' ]; then + if [ -n "$PHPIZE_DEPS" ]; then + if apk info --installed .phpize-deps-configure > /dev/null; then + apkDel='.phpize-deps-configure' + elif ! apk info --installed .phpize-deps > /dev/null; then + apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS + apkDel='.phpize-deps' + fi + fi +fi + +popDir="$PWD" +for ext in $exts; do + cd "$ext" + [ -e Makefile ] || docker-php-ext-configure "$ext" + make -j"$j" + make -j"$j" install + find modules \ + -maxdepth 1 \ + -name '*.so' \ + -exec basename '{}' ';' \ + | xargs -r docker-php-ext-enable + make -j"$j" clean + cd "$popDir" +done + +if [ "$pm" = 'apk' ] && [ -n "$apkDel" ]; then + apk del $apkDel +fi + +if [ -e /usr/src/php/.docker-delete-me ]; then + docker-php-source delete +fi diff --git a/data/docker-php-source b/data/docker-php-source new file mode 100755 index 0000000..9033d24 --- /dev/null +++ b/data/docker-php-source @@ -0,0 +1,34 @@ +#!/bin/sh +set -e + +dir=/usr/src/php + +usage() { + echo "usage: $0 COMMAND" + echo + echo "Manage php source tarball lifecycle." + echo + echo "Commands:" + echo " extract extract php source tarball into directory $dir if not already done." + echo " delete delete extracted php source located into $dir if not already done." + echo +} + +case "$1" in + extract) + mkdir -p "$dir" + if [ ! -f "$dir/.docker-extracted" ]; then + tar -Jxf /usr/src/php.tar.xz -C "$dir" --strip-components=1 + touch "$dir/.docker-extracted" + fi + ;; + + delete) + rm -rf "$dir" + ;; + + *) + usage + exit 1 + ;; +esac diff --git a/data/php-5.5.38.tar.xz b/data/php-5.5.38.tar.xz new file mode 100644 index 0000000..3bde1c4 Binary files /dev/null and b/data/php-5.5.38.tar.xz differ diff --git a/data/php-fpm.conf b/data/php-fpm.conf new file mode 100644 index 0000000..c7706ea --- /dev/null +++ b/data/php-fpm.conf @@ -0,0 +1,23 @@ +; This file was initially adapated from the output of: (on PHP 5.5) +; grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default + +[global] + +error_log = /proc/self/fd/2 +daemonize = no + +[www] + +; if we send this to /proc/self/fd/1, it never appears +access.log = /proc/self/fd/2 + +user = www-data +group = www-data + +listen = 9000 + +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 diff --git a/data/php.ini b/data/php.ini new file mode 100644 index 0000000..fa1dff6 --- /dev/null +++ b/data/php.ini @@ -0,0 +1,13 @@ +;xmlrpc_errors = on +;html_errors = on +;track_errors = on +display_errors = on +;display_startup_errors = on +;error_reporting = E_ALL | E_STRICT +error_reporting = E_ALL +log_errors = on +error_log = /usr/local/logs/php.log +date.timezone = UTC +variables_order = "EGPCS" +cgi.fix_pathinfo= 0 +fastcgi.logging = 1 diff --git a/tests/01-version.sh b/tests/01-version.sh new file mode 100755 index 0000000..24d2944 --- /dev/null +++ b/tests/01-version.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eu +set -o pipefail + +IMAGE="${1}" +#NAME="${2}" +VERSION="${3}" +TAG="${4}" +ARCH="${5}" + + +echo +echo "\$ docker run --rm --platform ${ARCH} --entrypoint=php ${IMAGE}:${TAG} -v | grep -E '^PHP ${VERSION}'" +docker run --rm --platform "${ARCH}" --entrypoint=php "${IMAGE}:${TAG}" -v | grep -E "^PHP ${VERSION}" +echo + +echo +echo "\$ docker run --rm --platform ${ARCH} --entrypoint=php-fpm ${IMAGE}:${TAG} -v | grep -E '^PHP ${VERSION}'" +docker run --rm --platform "${ARCH}" --entrypoint=php-fpm "${IMAGE}:${TAG}" -v | grep -E "^PHP ${VERSION}" +echo diff --git a/tests/02-server-index.php.sh b/tests/02-server-index.php.sh new file mode 100755 index 0000000..051da33 --- /dev/null +++ b/tests/02-server-index.php.sh @@ -0,0 +1,256 @@ +#!/usr/bin/env bash +set -eu +set -o pipefail + +IMAGE="${1}" +#NAME="${2}" +VERSION="${3}" +TAG="${4}" +ARCH="${5}" + + + +### +### Variables +### +WWW_PORT="81" +DOC_ROOT_HOST="$( mktemp -d )" +DOC_ROOT_CONT="/var/www/default" + +CONFIG_HOST="$( mktemp -d )" +CONFIG_CONT="/etc/nginx/conf.d" + +NAME_PHP="devilbox-php-fpm-${VERSION}" + +NAME_WEB="nginx-stable-devilbox" +CONT_WEB="nginx:stable" + + +### +### Create required files +### + +# PHP Index File +{ + echo ' "${DOC_ROOT_HOST}/index.php" +# PHP Error File +{ + echo ' "${DOC_ROOT_HOST}/error.php" + +# Nginx conf +{ + echo "server {" + echo " server_name _;" + echo " listen 80;" + echo " root ${DOC_ROOT_CONT};" + echo " index index.php;" + echo " location ~* \.php\$ {" + echo " fastcgi_index index.php;" + echo " fastcgi_pass ${NAME_PHP}:9000;" + echo " include fastcgi_params;" + echo " fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;" + echo " fastcgi_param SCRIPT_NAME \$fastcgi_script_name;" + echo " }" + echo "}" +} > "${CONFIG_HOST}/php.conf" + + +### +### Fix mount permissions +### +chmod 0777 "${CONFIG_HOST}" +chmod 0777 "${DOC_ROOT_HOST}" +chmod 0644 "${DOC_ROOT_HOST}/index.php" +chmod 0644 "${DOC_ROOT_HOST}/error.php" + + +### +### Start containers +### +PHP_DID="$( docker run -d --platform "${ARCH}" --name "${NAME_PHP}" -v "${DOC_ROOT_HOST}:${DOC_ROOT_CONT}" "${IMAGE}:${TAG}" )" +sleep 4 +WEB_DID="$( docker run -d --platform "${ARCH}" --name "${NAME_WEB}" -v "${DOC_ROOT_HOST}:${DOC_ROOT_CONT}" -v "${CONFIG_HOST}:${CONFIG_CONT}" -p "${WWW_PORT}:80" --link "${NAME_PHP}" "${CONT_WEB}" )" +sleep 4 + + +### +### Test PHP ini +### +#docker exec "${PHP_DID}" + +### +### Test for PHP success +### +echo "[TEST] curl index.php:" +echo "------------------------------------" +if ! curl -sS 127.0.0.1:${WWW_PORT}/index.php 2>/dev/null | grep 'itworks'; then + echo "[FAILED], could not connect to index.php" + echo + + echo "index.php:" + echo "------------------------------------" + cat "${DOC_ROOT_HOST}/index.php" + echo + + echo "curl:" + echo "------------------------------------" + curl -v 127.0.0.1:${WWW_PORT}/index.php + echo + + echo "docker logs php" + echo "------------------------------------" + docker logs "${PHP_DID}" + echo + + echo "docker logs web" + echo "------------------------------------" + docker logs "${WEB_DID}" + echo + + docker stop "${WEB_DID}" || true + docker stop "${PHP_DID}" || true + + docker rm -f "${NAME_WEB}" || true + docker rm -f "${NAME_PHP}" || true + + rm -rf "${DOC_ROOT_HOST}" + rm -rf "${CONFIG_HOST}" + exit 1 +fi +echo "[SUCCESS]" +echo + +### +### Test for Docker access logs +### +echo "[TEST] docker logs 'GET /index.php':" +echo "------------------------------------" +if ! docker logs "${WEB_DID}" 2>&1 | grep 'GET /index.php'; then + echo "[FAILED], could not find access requests in docker logs." + echo + + echo "docker logs php" + echo "------------------------------------" + docker logs "${PHP_DID}" + echo + + echo "docker logs web" + echo "------------------------------------" + docker logs "${WEB_DID}" + echo + + docker stop "${WEB_DID}" || true + docker stop "${PHP_DID}" || true + + docker rm -f "${NAME_WEB}" || true + docker rm -f "${NAME_PHP}" || true + + rm -rf "${DOC_ROOT_HOST}" + rm -rf "${CONFIG_HOST}" + exit 1 +fi +echo "[SUCCESS]" +echo + + +### +### Test for PHP errors +### +echo "[TEST] curl error.php:" +echo "------------------------------------" +if ! curl -sS 127.0.0.1:${WWW_PORT}/error.php 2>/dev/null | grep 'syntax error'; then + echo "[FAILED], could not connect to error.php" + echo + + echo "error.php:" + echo "------------------------------------" + cat "${DOC_ROOT_HOST}/index.php" + echo + + echo "curl:" + echo "------------------------------------" + curl -v 127.0.0.1:${WWW_PORT}/error.php + echo + + echo "docker logs php" + echo "------------------------------------" + docker logs "${PHP_DID}" + echo + + echo "docker logs web" + echo "------------------------------------" + docker logs "${WEB_DID}" + echo + + docker stop "${WEB_DID}" || true + docker stop "${PHP_DID}" || true + + docker rm -f "${NAME_WEB}" || true + docker rm -f "${NAME_PHP}" || true + + rm -rf "${DOC_ROOT_HOST}" + rm -rf "${CONFIG_HOST}" + exit 1 +fi +echo "[SUCCESS]" +echo + +### +### Test for Docker error logs +### +echo "[TEST] docker logs 'syntax error':" +echo "------------------------------------" + +if ! docker logs "${WEB_DID}" 2>&1 | grep 'syntax error'; then + echo "[FAILED], could not find error message in docker logs." + echo + + echo "docker logs php" + echo "------------------------------------" + docker logs "${PHP_DID}" + echo + + echo "docker logs web" + echo "------------------------------------" + docker logs "${WEB_DID}" + echo + + docker stop "${WEB_DID}" || true + docker stop "${PHP_DID}" || true + + docker rm -f "${NAME_WEB}" || true + docker rm -f "${NAME_PHP}" || true + + rm -rf "${DOC_ROOT_HOST}" + rm -rf "${CONFIG_HOST}" + exit 1 +fi +echo "[SUCCESS]" +echo + + +### +### Clean-up +### +echo "[CLEANUP] shutdown:" +echo "------------------------------------" +docker stop "${WEB_DID}" >/dev/null 2>&1 || true +docker stop "${PHP_DID}" >/dev/null 2>&1 || true + +docker rm -f "${NAME_WEB}" >/dev/null 2>&1 || true +docker rm -f "${NAME_PHP}" >/dev/null 2>&1 || true + +rm -rf "${DOC_ROOT_HOST}" >/dev/null 2>&1 || true +rm -rf "${CONFIG_HOST}" >/dev/null 2>&1 || true + +exit 0 diff --git a/tests/get-modules.sh b/tests/get-modules.sh new file mode 100755 index 0000000..641444b --- /dev/null +++ b/tests/get-modules.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + + +IMAGE="${1}" +#NAME="${2}" +#VERSION="${3}" +TAG="${4}" +ARCH="${5}" + +MODULES="$( docker run --rm -t --platform "${ARCH}" --entrypoint=php "${IMAGE}:${TAG}" -m \ + | grep -vE '(^\[)|(^\s*$)' \ + | sort -u -f +)" + +echo "| Module | Built-in |" +echo "|--------------|-----------|" +echo "${MODULES}" | while read -r line; do + line="$( echo "${line}" | sed 's/\r//g' | xargs )" + printf "| %-12s | ✔ |\n" "${line}" +done diff --git a/tests/start-ci.sh b/tests/start-ci.sh new file mode 100755 index 0000000..1777030 --- /dev/null +++ b/tests/start-ci.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -e +set -u +set -o pipefail + +### +### Variables +### + +IFS=$'\n' + +# Current directory +CWD="$( dirname "${0}" )" +IMAGE="${1}" +NAME="${2}" +VERSION="${3}" +TAG="${4}" +ARCH="${5}" + +declare -a TESTS=() + + + + +### +### Run tests +### + +# Get all [0-9]+.sh test files +FILES="$( find "${CWD}" -regex "${CWD}/[0-9].+\.sh" | sort -u )" +for f in ${FILES}; do + TESTS+=("${f}") +done + +# Start a single test +if [ "${#}" -eq "3" ]; then + sh -c "${TESTS[${2}]} ${IMAGE} ${NAME} ${VERSION} ${TAG} ${ARCH}" + +# Run all tests +else + for i in "${TESTS[@]}"; do + echo "################################################################################" + echo "# [${CWD}/${i}] ${IMAGE}:${TAG} ${NAME}-${VERSION} (${ARCH})" + echo "################################################################################" + sh -c "${i} ${IMAGE} ${NAME} ${VERSION} ${TAG} ${ARCH}" + done +fi