Try out new workflow run

This commit is contained in:
cytopia
2022-03-25 00:29:07 +01:00
parent 8679dbee6a
commit 14c25cf670
9 changed files with 705 additions and 488 deletions

33
.github/workflows/zzz-build.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
---
# -------------------------------------------------------------------------------------------------
# Job Name
# -------------------------------------------------------------------------------------------------
name: build
# -------------------------------------------------------------------------------------------------
# When to run
# -------------------------------------------------------------------------------------------------
on:
push:
jobs:
# (1/2) Determine repository params
params:
uses: ./.github/workflows/zzz-params.yml
# (2/2) Build
docker:
needs: [params]
uses: ./.github/workflows/zzz-build.yml
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 }}

70
.github/workflows/zzz-params.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
---
# -------------------------------------------------------------------------------------------------
# Job Name
# -------------------------------------------------------------------------------------------------
name: params
# -------------------------------------------------------------------------------------------------
# Custom Variables
# -------------------------------------------------------------------------------------------------
env:
MATRIX: >-
[
{
"NAME": "PHP",
"VERSION": ["5.5", "5.6"],
"FLAVOUR": ["base"],
"ARCH": ["linux/amd64", "linux/arm64"]
}
]
# -------------------------------------------------------------------------------------------------
# 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

300
.github/workflows/zzz-reuse.yml vendored Normal file
View File

@@ -0,0 +1,300 @@
name: Build multi-arch image
on:
workflow_call:
###
### Variables
###
inputs:
enabled:
description: 'Determines wheather this workflow is enabled at all (will run or skip).'
required: true
type: boolean
can_deploy:
description: 'Determines wheather this workflow will also deploy (login and push).'
required: true
type: boolean
matrix:
description: 'The version build matrix as JSON string ( list of objects: [{NAME, VERSION[], ARCH[]}] ).'
required: true
type: string
refs:
description: 'The ref build matrix as JSON string (list of git refs to build/deploy).'
required: false
type: string
###
### Secrets
###
secrets:
dockerhub_username:
description: 'The username for Dockerhub.'
required: false
dockerhub_password:
description: 'The password for Dockerhub.'
required: false
jobs:
# -----------------------------------------------------------------------------------------------
# JOB (1/3): CONFIGURE
# -----------------------------------------------------------------------------------------------
configure:
name: Configure
runs-on: ubuntu-latest
outputs:
can_login: ${{ steps.set-login.outputs.can_login }}
has_refs: ${{ steps.set-matrix.outputs.has_refs }}
matrix_build: ${{ steps.set-matrix.outputs.matrix_build }}
matrix_deploy: ${{ steps.set-matrix.outputs.matrix_deploy }}
artifact_base: ${{ steps.set-artifact.outputs.base }}
artifact_mods: ${{ steps.set-artifact.outputs.mods }}
artifact_prod: ${{ steps.set-artifact.outputs.prod }}
artifact_work: ${{ steps.set-artifact.outputs.work }}
if: inputs.enabled
steps:
- name: "[Set-Output] Set Docker login capabilities"
id: set-login
shell: bash
run: |
if [ "${{ env.ENV_USER }}" = '' ] || [ "${{ env.ENV_PASS }}" = '' ]; then
echo "::set-output name=can_login::0"
else
echo "::set-output name=can_login::1"
fi
env:
ENV_USER: ${{ secrets.dockerhub_username }}
ENV_PASS: ${{ secrets.dockerhub_password }}
- name: "[Set-Output] Set Build & Deploy Matrix"
id: set-matrix
shell: bash
run: |
if [ "${{ inputs.refs }}" != "" ]; then
MATRIX_BUILD="$( \
jq -M -c \
--argjson refs '${{ inputs.refs }}' \
'map({name:.NAME, version:.VERSION[], flavour:.FLAVOUR[], arch:.ARCH[], refs:$refs[]})' <<<'${{ inputs.matrix }}' \
)"
MATRIX_DEPLOY="$( \
jq -M -c \
--argjson refs '${{ inputs.refs }}' \
'map({name:.NAME, version:.VERSION[], flavour:.FLAVOUR[], refs:$refs[]})' <<<'${{ inputs.matrix }}' \
)"
echo "::set-output name=matrix_build::${MATRIX_BUILD}"
echo "::set-output name=matrix_deploy::${MATRIX_DEPLOY}"
echo "::set-output name=has_refs::1"
else
MATRIX_BUILD="$( \
jq -M -c \
'map({name:.NAME, version:.VERSION[], flavour:.FLAVOUR[], arch:.ARCH[]})' <<<'${{ inputs.matrix }}' \
)"
MATRIX_DEPLOY="$( \
jq -M -c \
'map({name:.NAME, version:.VERSION[], flavour:.FLAVOUR[]})' <<<'${{ inputs.matrix }}' \
)"
echo "::set-output name=matrix_build::${MATRIX_BUILD}"
echo "::set-output name=matrix_deploy::${MATRIX_DEPLOY}"
echo "::set-output name=has_refs::0"
fi
- name: "[Set-Output] Set unique Artifact filenames"
id: set-artifact
shell: bash
run: |
PRE_HASH="$( git rev-parse HEAD | head -c 10 )"
PRE_DATE="$( date +"%s" )"
BASE="${PRE_HASH}-${PRE_DATE}-base"
MODS="${PRE_HASH}-${PRE_DATE}-mods"
PROD="${PRE_HASH}-${PRE_DATE}-prod"
WORK="${PRE_HASH}-${PRE_DATE}-work"
echo "::set-output name=base::${BASE}"
echo "::set-output name=mods::${MODS}"
echo "::set-output name=prod::${PROD}"
echo "::set-output name=work::${WORK}"
- name: "[DEBUG] Workflow Inputs"
shell: bash
run: |
echo 'enabled: ${{ inputs.enabled }} '
echo 'can_deploy: ${{ inputs.can_deploy }} '
echo 'matrix: ${{ inputs.matrix }} '
echo 'refs: ${{ inputs.refs }} '
- name: "[DEBUG] Determined Settings"
shell: bash
run: |
echo 'can_login=${{ steps.set-login.outputs.can_login }}'
echo 'has_refs=${{ steps.set-matrix.outputs.has_refs }}'
echo 'matrix_build=${{ steps.set-matrix.outputs.matrix_build }}'
echo 'matrix_deploy=${{ steps.set-matrix.outputs.matrix_deploy }}'
echo 'artifact_base=${{ steps.set-artifact.outputs.base }}'
echo 'artifact_mods=${{ steps.set-artifact.outputs.mods }}'
echo 'artifact_prod=${{ steps.set-artifact.outputs.prod }}'
echo 'artifact_work=${{ steps.set-artifact.outputs.work }}'
# -----------------------------------------------------------------------------------------------
# JOB (2/3): BUILD
# -----------------------------------------------------------------------------------------------
build-base:
needs: [configure]
name: Build ${{ matrix.name }}-${{ matrix.version }} (${{ matrix.flavour }}) (${{ matrix.arch }}) ${{ matrix.refs }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.configure.outputs.matrix_build) }}
if: inputs.enabled
steps:
# ------------------------------------------------------------
# Setup repository
# ------------------------------------------------------------
- name: "[SETUP] Checkout repository (current)"
uses: actions/checkout@v3
with:
fetch-depth: 0
if: needs.configure.outputs.has_refs == 0
- name: "[SETUP] Checkout repository (ref: ${{ matrix.refs }})"
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ matrix.refs }}
if: needs.configure.outputs.has_refs != 0
- name: "[SETUP] Setup QEMU environment"
uses: docker/setup-qemu-action@v1
with:
image: tonistiigi/binfmt:latest
platforms: all
- name: "[SETUP] Determine Docker tag"
id: tag
uses: cytopia/docker-tag-action@v0.4.15
# ------------------------------------------------------------
# Build
# ------------------------------------------------------------
- name: Build
uses: cytopia/shell-command-retry-action@v0.1.2
with:
command: |
make build VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} ARCH=${{ matrix.arch }} TAG=${{ steps.tag.outputs.docker-tag }}
# ------------------------------------------------------------
# Test
# ------------------------------------------------------------
- name: Test
uses: cytopia/shell-command-retry-action@v0.1.2
with:
command: |
make test VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} ARCH=${{ matrix.arch }} TAG=${{ steps.tag.outputs.docker-tag }}
# ------------------------------------------------------------
# Export
# ------------------------------------------------------------
- name: "[Artifact] Set name"
id: artifact
run: |
PREFIX="${{ needs.configure.outputs.artifact_base }}"
ARCH="${{ matrix.arch }}"
TAG="${{ steps.tag.outputs.docker-tag }}"
NAME="$( echo "${PREFIX}-${ARCH}-${TAG}" | sed 's|/|-|g' )"
echo "::set-output name=file::${NAME}"
echo "file=${NAME}"
- name: "[Artifact] Export"
uses: cytopia/shell-command-retry-action@v0.1.2
with:
command: |
make save VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} ARCH=${{ matrix.arch }} TAG=${{ steps.tag.outputs.docker-tag }} OUTFILE=${{ steps.artifact.outputs.file }}
- name: "[Artifact] Upload"
uses: actions/upload-artifact@v3
with:
name: ${{ steps.artifact.outputs.file }}
path: ${{ steps.artifact.outputs.file }}
# # ------------------------------------------------------------
# # Deploy
# # ------------------------------------------------------------
# - name: Docker login
# uses: docker/login-action@v1
# with:
# username: ${{ secrets.dockerhub_username }}
# password: ${{ secrets.dockerhub_password }}
# if: needs.configure.outputs.can_login == 1 && inputs.can_deploy
#
# - name: Docker push architecture image
# uses: cytopia/shell-command-retry-action@v0.1.2
# with:
# command: |
# make push NAME=${{ matrix.name }} VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} ARCH=${{ matrix.arch }} TAG=${{ steps.tag.outputs.docker-tag }}
# if: needs.configure.outputs.can_login == 1 && inputs.can_deploy
#
# # -----------------------------------------------------------------------------------------------
# # JOB (3/3): DEPLOY
# # -----------------------------------------------------------------------------------------------
# deploy:
# needs: [configure, build]
# name: Deploy ${{ matrix.name }}-${{ matrix.version }} (${{ matrix.flavour }}) ${{ matrix.refs }}
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# include: ${{ fromJson(needs.configure.outputs.matrix_deploy) }}
# if: inputs.enabled && needs.configure.outputs.can_login == 1 && inputs.can_deploy
# steps:
# # ------------------------------------------------------------
# # Setup repository
# # ------------------------------------------------------------
# - name: "[SETUP] Checkout repository (current)"
# uses: actions/checkout@v3
# with:
# fetch-depth: 0
# if: needs.configure.outputs.has_refs == 0
#
# - name: "[SETUP] Checkout repository (ref: ${{ matrix.refs }})"
# uses: actions/checkout@v3
# with:
# fetch-depth: 0
# ref: ${{ matrix.refs }}
# if: needs.configure.outputs.has_refs != 0
#
# - name: "[SETUP] Determine Docker tag"
# id: tag
# uses: cytopia/docker-tag-action@v0.4.15
#
# - name: "[SETUP] Determine manifest arches"
# id: manifest
# run: |
# ARCHES="$( echo '${{ inputs.matrix }}' \
# | jq 'group_by(.NAME, .VERSION, .ARCH)' \
# | jq 'map({NAME: .[].NAME, VERSION: .[].VERSION[], FLAVOUR: .[].FLAVOUR[], ARCHES: .[].ARCH|join(",")})' \
# | jq '.[] | select(.NAME=="${{ matrix.name }}" and .VERSION=="${{ matrix.version }}" and .FLAVOUR=="${{ matrix.flavour }}") | .ARCHES' \
# | jq -c -M \
# )"
# echo "::set-output name=arches::${ARCHES}"
# echo "ARCHES: ${ARCHES}"
#
#
# # ------------------------------------------------------------
# # Deploy
# # ------------------------------------------------------------
# - name: "[DEPLOY] Login"
# uses: docker/login-action@v1
# with:
# username: ${{ secrets.DOCKERHUB_USERNAME }}
# password: ${{ secrets.DOCKERHUB_PASSWORD }}
#
# - name: "[DEPLOY] Create Docker manifest for architectures: ${{ steps.manifest.outputs.arches }}"
# uses: cytopia/shell-command-retry-action@v0.1.2
# with:
# command: |
# make manifest-create NAME=${{ matrix.name }} VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} ARCHES=${{ steps.manifest.outputs.arches }} TAG=${{ steps.tag.outputs.docker-tag }}
#
# - name: "[DEPLOY] Publish Docker manifest: ${{ steps.tag.outputs.docker-tag }}"
# uses: cytopia/shell-command-retry-action@v0.1.2
# with:
# command: |
# make manifest-push NAME=${{ matrix.name }} VERSION=${{ matrix.version }} FLAVOUR=${{ matrix.flavour }} TAG=${{ steps.tag.outputs.docker-tag }}

75
.gitignore vendored
View File

@@ -1,74 +1,3 @@
######################################
# CUSTOM
######################################
build/ansible/*.retry
######################################
# GENERIC
######################################
###### std ######
.lock
*.log
###### patches/diffs ######
*.patch
*.diff
*.orig
*.rej
######################################
# Operating Systems
######################################
###### OSX ######
._*
.DS*
.Spotlight-V100
.Trashes
###### Windows ######
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.lnk
######################################
# Editors
######################################
###### Sublime ######
*.sublime-workspace
*.sublime-project
###### Eclipse ######
.classpath
.buildpath
.project
.settings/
###### Netbeans ######
nbproject/private/
###### Intellij IDE ######
.idea/
.idea_modules/
###### vim ######
*.swp
*.swo
*~
###### TextMate ######
.tm_properties
*.tmproj
###### BBEdit ######
*.bbprojectd
*.bbproject
Makefile.docker
Makefile.lint

642
Makefile
View File

@@ -2,109 +2,86 @@ ifneq (,)
.error This Makefile requires GNU Make.
endif
# -------------------------------------------------------------------------------------------------
# Docker configuration
# -------------------------------------------------------------------------------------------------
CURRENT_DIR = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
DIR = Dockerfiles
IMAGE = devilbox/php-fpm
ARCH = linux/amd64
NO_CACHE =
PHP_EXT_DIR =
# Run checks after each module has been installed (slow, but yields errors faster)
FAIL_FAST = False
# File lint
FL_VERSION = 0.3
FL_IGNORES = .git/,.github/
# -------------------------------------------------------------------------------------------------
# DEFAULT TARGET
# -------------------------------------------------------------------------------------------------
help:
@echo
@echo " _ _ _ _ __ _ ___ "
@echo " _| |___ _ _<_| | |_ _____ / ___| |_ ___ ___| | ___._ _ _ "
@echo " / . / ._| | | | | . / . \ \// | . | . | . |___| || . | ' ' |"
@echo " \___\___|__/|_|_|___\___/\_/_/| _|_|_| _/ |_|| _|_|_|_|"
@echo " |_| |_| |_| "
@echo
@echo
@echo "Targets"
@echo "--------------------------------------------------------------------------------"
@echo
@echo "lint Lint project files and repository"
@echo
@echo "gen-readme [VERSION=] Update README with PHP modules from built images."
@echo "gen-dockerfiles [FAIL_FAST=] Generate Dockerfiles from templates."
@echo
@echo "build-base VERSION= [ARGS=] Build base image by specified version"
@echo "build-mods VERSION= [ARGS=] Build mods image by specified version"
@echo "build-prod VERSION= [ARGS=] Build prod image by specified version"
@echo "build-work VERSION= [ARGS=] Build work image by specified version"
@echo
@echo "rebuild-base VERSION= [ARGS=] Rebuild base image by specified version"
@echo "rebuild-mods VERSION= [ARGS=] Rebuild mods image by specified version"
@echo "rebuild-prod VERSION= [ARGS=] Rebuild prod image by specified version"
@echo "rebuild-work VERSION= [ARGS=] Rebuild work image by specified version"
@echo
@echo "test-base VERSION= Test base image by specified version"
@echo "test-mods VERSION= Test mods image by specified version"
@echo "test-prod VERSION= Test prod image by specified version"
@echo "test-work VERSION= Test work image by specified version"
@echo
@echo
@echo "Variables"
@echo "--------------------------------------------------------------------------------"
@echo
@echo "VERSION One of '5.2', '5.3', '5.4', '5.5', '5.6', '7.0',"
@echo " '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2'."
@echo " For gen-readme target it is optional and if not"
@echo " specified, it will generate for all versions."
@echo
@echo "FAIL_FAST Either 'True' or 'False' (defaults to 'False')."
@echo " If set to 'True', each module install has an"
@echo " immediate check, which is very slow for CI, but"
@echo " yields errors immediately."
@echo " If set to 'False', checks are done at the end."
@echo
@echo "ARGS Can be added to all build-* and rebuild-* targets"
@echo " to supply additional docker build options."
# -------------------------------------------------------------------------------------------------
# Lint Targets
# -------------------------------------------------------------------------------------------------
lint: lint-files
lint: lint-yaml
lint: lint-changelog
lint: lint-workflow
lint-workflow:
@echo "################################################################################"
@echo "# Lint Workflow"
@echo "################################################################################"
@\
GIT_CURR_MAJOR="$$( git tag | sort -V | tail -1 | sed 's|\.[0-9]*$$||g' )"; \
GIT_CURR_MINOR="$$( git tag | sort -V | tail -1 | sed 's|^[0-9]*\.||g' )"; \
GIT_NEXT_TAG="$${GIT_CURR_MAJOR}.$$(( GIT_CURR_MINOR + 1 ))"; \
if ! grep 'refs:' -A 100 .github/workflows/nightly.yml \
| grep " - '$${GIT_NEXT_TAG}'" >/dev/null; then \
echo "[ERR] New Tag required in .github/workflows/nightly.yml: $${GIT_NEXT_TAG}"; \
exit 1; \
else \
echo "[OK] Git Tag present in .github/workflows/nightly.yml: $${GIT_NEXT_TAG}"; \
# 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
#FLAVOUR = base
FILE = Dockerfile-$(VERSION)
DIR = Dockerfiles/$(FLAVOUR)
ifeq ($(strip $(TAG)),latest)
DOCKER_TAG = $(VERSION)-$(FLAVOUR)
BASE_TAG = $(VERSION)-base
MODS_TAG = $(VERSION)-mods
PROD_TAG = $(VERSION)-prod
WORK_TAG = $(VERSION)-work
else
DOCKER_TAG = $(VERSION)-$(FLAVOUR)-$(TAG)
BASE_TAG = $(VERSION)-base-$(TAG)
MODS_TAG = $(VERSION)-mods-$(TAG)
PROD_TAG = $(VERSION)-prod-$(TAG)
WORK_TAG = $(VERSION)-work-$(TAG)
endif
ARCH = linux/amd64
# Makefile.lint overwrites
FL_IGNORES = .git/,.github/,tests/
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
# -------------------------------------------------------------------------------------------------
# Overwrite Targets
# -------------------------------------------------------------------------------------------------
# Append additional target to lint
lint: lint-changelog
lint: lint-ansible
###
### Ensures CHANGELOG has an entry
###
.PHONY: lint-changelog
lint-changelog:
@echo "################################################################################"
@echo "# Lint Changelog"
@@ -121,46 +98,105 @@ lint-changelog:
fi
@echo
lint-files:
@echo "################################################################################"
@echo "# Lint Files"
@echo "################################################################################"
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-cr --text --ignore '$(FL_IGNORES)' --path .
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-crlf --text --ignore '$(FL_IGNORES)' --path .
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-single-newline --text --ignore '$(FL_IGNORES)' --path .
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-trailing-space --text --ignore '$(FL_IGNORES)' --path .
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8 --text --ignore '$(FL_IGNORES)' --path .
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/file-lint:$(FL_VERSION) file-utf8-bom --text --ignore '$(FL_IGNORES)' --path .
@echo
lint-yaml:
@# Lint all files
@echo "################################################################################"
@echo "# Lint Yaml"
@echo "################################################################################"
@docker run --rm $$(tty -s && echo "-it" || echo) -v $(CURRENT_DIR):/data cytopia/yamllint .
@echo
###
### Ensures Ansible Dockerfile generation is current
###
.PHONY: lint-ansible
lint-ansible: gen-dockerfiles
@git diff --quiet || { echo "Build Changes"; git diff; git status; false; }
# -------------------------------------------------------------------------------------------------
# GENERATE TARGETS
# Docker Targets
# -------------------------------------------------------------------------------------------------
gen-readme:
ifeq ($(strip $(VERSION)),)
@echo "Generate README.md for all PHP versions"
cd build; ./gen-readme.sh $(ARCH)
else
@echo "Generate README.md for PHP $(VERSION)"
@$(MAKE) --no-print-directory _check-version
@$(MAKE) --no-print-directory _check-image-exists _EXIST_IMAGE=base
@$(MAKE) --no-print-directory _check-image-exists _EXIST_IMAGE=mods
cd build; ./gen-readme.sh $(ARCH) $(VERSION)
# ---- ONLY FOR "mods" images ----
# When builds mods, we have a builder image and then copy everything to the final
# target image. In order to do so, we pass a build-arg EXT_DIR, which contains
# the variable directory of extensions to copy.
# The only way to "LAZY" fetch it, is by doing a call to the base image and populate
# a Makefile variable with its value upon call.
ifeq ($(strip $(FLAVOUR)),mods)
EXT_DIR=$$( docker run --rm --platform $(ARCH) --entrypoint=php $(IMAGE):$(BASE_TAG) -r \
'echo ini_get("extension_dir");'\
)
endif
.PHONY: build
build: check-flavour-is-set
build: check-parent-image-exists
build: ARGS+=--build-arg EXT_DIR=$(EXT_DIR)
build: docker-arch-build
.PHONY: rebuild
rebuild: check-flavour-is-set
rebuild: check-parent-image-exists
rebuild: ARGS+=--build-arg EXT_DIR=$(EXT_DIR)
rebuild: docker-arch-rebuild
.PHONY: push
push: docker-arch-push
# -------------------------------------------------------------------------------------------------
# Save / Load Targets
# -------------------------------------------------------------------------------------------------
.PHONY: save
docker-save: check-flavour-is-set
docker-save: check-version-is-set
docker-save: check-current-image-exists
.PHONY: load
docker-load: check-flavour-is-set
docker-load: check-version-is-set
docker-load: check-current-image-exists
# -------------------------------------------------------------------------------------------------
# Manifest Targets
# -------------------------------------------------------------------------------------------------
.PHONY: manifest-create
manifest-create: docker-manifest-create
.PHONY: manifest-push
manifest-push: docker-manifest-push
# -------------------------------------------------------------------------------------------------
# Test Targets
# -------------------------------------------------------------------------------------------------
.PHONY: test
test: check-flavour-is-set
test: check-current-image-exists
test: test-integration
.PHONY: test-integration
test-integration:
./tests/test.sh $(IMAGE) $(ARCH) $(VERSION) $(FLAVOUR)
# -------------------------------------------------------------------------------------------------
# Generate Targets
# -------------------------------------------------------------------------------------------------
###
### Generate README (requires images to be built)
###
.PHONY: gen-readme
gen-readme: check-version-is-set
gen-readme:
@echo "################################################################################"
@echo "# Generate README.md for PHP $(VERSION) ($(IMAGE):$(DOCKER_TAG)) on $(ARCH)"
@echo "################################################################################"
./build/gen-readme.sh $(IMAGE) $(ARCH) $(BASE_TAG) $(MODS_TAG) $(VERSION)
git diff --quiet || { echo "Build Changes"; git diff; git status; false; }
###
### Generate Dockerfiles
###
.PHONY: gen-dockerfiles
gen-dockerfiles:
docker run --rm \
--platform $(ARCH) \
$$(tty -s && echo "-it" || echo) \
-e USER=ansible \
-e MY_UID=$$(id -u) \
@@ -175,254 +211,118 @@ gen-dockerfiles:
--diff $(ARGS)
# -------------------------------------------------------------------------------------------------
# BUILD TARGETS
# -------------------------------------------------------------------------------------------------
build-base: _check-version
build-base:
docker build $(NO_CACHE) \
--platform $(ARCH) \
--label "org.opencontainers.image.created"="$$(date --rfc-3339=s)" \
--label "org.opencontainers.image.version"="$$(git rev-parse --abbrev-ref HEAD)" \
--label "org.opencontainers.image.revision"="$$(git rev-parse HEAD))" \
$(ARGS) \
-t $(IMAGE):${VERSION}-base \
-f $(DIR)/base/Dockerfile-${VERSION} $(DIR)/base
build-mods: _check-version
build-mods: _EXIST_IMAGE=base
build-mods: _check-image-exists
build-mods:
ifeq ($(strip $(TARGET)),)
docker build $(NO_CACHE) \
--platform $(ARCH) \
--target builder \
-t $(IMAGE):$(VERSION)-mods \
-f $(DIR)/mods/Dockerfile-$(VERSION) $(DIR)/mods;
@# $(NO_CACHE) is removed, as it would otherwise rebuild the 'builder' image again.
docker build \
--platform $(ARCH) \
--target final \
--label "org.opencontainers.image.created"="$$(date --rfc-3339=s)" \
--label "org.opencontainers.image.version"="$$(git rev-parse --abbrev-ref HEAD)" \
--label "org.opencontainers.image.revision"="$$(git rev-parse HEAD)" \
--build-arg EXT_DIR="$$( docker run --rm --platform $(ARCH) --entrypoint=php $(IMAGE):$(VERSION)-mods -i \
| grep ^extension_dir \
| awk -F '=>' '{print $$2}' \
| xargs \
)" \
$(ARGS) \
-t $(IMAGE):$(VERSION)-mods \
-f $(DIR)/mods/Dockerfile-$(VERSION) $(DIR)/mods;
else
docker build $(NO_CACHE) \
--platform $(ARCH) \
--target $(TARGET) \
--label "org.opencontainers.image.created"="$$(date --rfc-3339=s)" \
--label "org.opencontainers.image.version"="$$(git rev-parse --abbrev-ref HEAD)" \
--label "org.opencontainers.image.revision"="$$(git rev-parse HEAD)" \
$(ARGS) \
-t $(IMAGE):$(VERSION)-mods \
-f $(DIR)/mods/Dockerfile-$(VERSION) $(DIR)/mods
endif
build-prod: _check-version
build-prod: _EXIST_IMAGE=mods
build-prod: _check-image-exists
build-prod:
docker build $(NO_CACHE) \
--platform $(ARCH) \
--label "org.opencontainers.image.created"="$$(date --rfc-3339=s)" \
--label "org.opencontainers.image.version"="$$(git rev-parse --abbrev-ref HEAD)" \
--label "org.opencontainers.image.revision"="$$(git rev-parse HEAD)" \
$(ARGS) \
-t $(IMAGE):${VERSION}-prod \
-f $(DIR)/prod/Dockerfile-${VERSION} $(DIR)/prod
build-work: _check-version
build-work: _EXIST_IMAGE=prod
build-work: _check-image-exists
build-work:
docker build $(NO_CACHE) \
--platform $(ARCH) \
--label "org.opencontainers.image.created"="$$(date --rfc-3339=s)" \
--label "org.opencontainers.image.version"="$$(git rev-parse --abbrev-ref HEAD)" \
--label "org.opencontainers.image.revision"="$$(git rev-parse HEAD)" \
--build-arg ARCH=$(ARCH) \
$(ARGS) \
-t $(IMAGE):${VERSION}-work \
-f $(DIR)/work/Dockerfile-${VERSION} $(DIR)/work
# -------------------------------------------------------------------------------------------------
# REBUILD TARGETS
# HELPER TARGETS
# -------------------------------------------------------------------------------------------------
rebuild-base: _pull-base-image
rebuild-base: NO_CACHE=--no-cache
rebuild-base: build-base
###
### Ensures the VERSION variable is set
###
.PHONY: check-version-is-set
check-version-is-set:
@if [ "$(VERSION)" = "" ]; then \
echo "This make target requires the VERSION variable to be set."; \
echo "make <target> VERSION="; \
echo "Exiting."; \
exit 1; \
fi
###
### Ensures the FLAVOUR variable is set
###
.PHONY: check-flavour-is-set
check-flavour-is-set:
@if [ "$(FLAVOUR)" = "" ]; then \
echo "This make target requires the FLAVOUR variable to be set."; \
echo "make <target> FLAVOUR="; \
echo "Exiting."; \
exit 1; \
fi
@if [ "$(FLAVOUR)" != "base" ] && [ "$(FLAVOUR)" != "mods" ] && [ "$(FLAVOUR)" != "prod" ] && [ "$(FLAVOUR)" != "work" ]; then \
echo "Error, Flavour can only be one of 'base', 'mods', 'prod', or 'work'."; \
echo "Exiting."; \
exit 1; \
fi
rebuild-mods: NO_CACHE=--no-cache
rebuild-mods: build-mods
rebuild-prod: NO_CACHE=--no-cache
rebuild-prod: build-prod
rebuild-work: NO_CACHE=--no-cache
rebuild-work: build-work
# -------------------------------------------------------------------------------------------------
# TEST TARGETS
# -------------------------------------------------------------------------------------------------
test-base: _check-version
test-base: _EXIST_IMAGE=base
test-base: _check-image-exists
test-base:
./tests/test.sh $(IMAGE) $(ARCH) $(VERSION) base
test-mods: _check-version
test-mods: _EXIST_IMAGE=mods
test-mods: _check-image-exists
test-mods: _check-version
./tests/test.sh $(IMAGE) $(ARCH) $(VERSION) mods
test-prod: _check-version
test-prod: _EXIST_IMAGE=prod
test-prod: _check-image-exists
test-prod: _check-version
./tests/test.sh $(IMAGE) $(ARCH) $(VERSION) prod
test-work: _check-version
test-work: _EXIST_IMAGE=work
test-work: _check-image-exists
test-work: _check-version
./tests/test.sh $(IMAGE) $(ARCH) $(VERSION) work
# -------------------------------------------------------------------------------------------------
# DOCKERHUB TARGETS
# -------------------------------------------------------------------------------------------------
login:
ifeq ($(strip $(USERNAME)),)
@$(info This make target requires the USERNAME variable to be set.)
@$(info make login USERNAME= PASSWORD=)
@$(info )
@$(error Exiting)
endif
ifeq ($(strip $(PASSWORD)),)
@$(info This make target requires the PASSWORD variable to be set.)
@$(info make login USERNAME= PASSWORD=)
@$(info )
@$(error Exiting)
endif
@yes | docker login --username $(USERNAME) --password $(PASSWORD)
push:
ifeq ($(strip $(TAG)),)
@$(info This make target requires the TAG variable to be set.)
@$(info make push TAG=)
@$(info )
@$(error Exiting)
endif
docker push $(IMAGE):$(TAG)
tag:
ifeq ($(strip $(OLD_TAG)),)
@$(info This make target requires the OLD_TAG variable to be set.)
@$(info make tag OLD_TAG= NEW_TAG=)
@$(info )
@$(error Exiting)
endif
ifeq ($(strip $(NEW_TAG)),)
@$(info This make target requires the NEW_TAG variable to be set.)
@$(info make tag OLD_TAG= NEW_TAG=)
@$(info )
@$(error Exiting)
endif
docker tag $(IMAGE):$(OLD_TAG) $(IMAGE):$(NEW_TAG)
# -------------------------------------------------------------------------------------------------
# HELPER TARGETS
# -------------------------------------------------------------------------------------------------
_check-version:
ifeq ($(strip $(VERSION)),)
@$(info This make target requires the VERSION variable to be set.)
@$(info make build-<flavour> VERSION=7.3)
@$(info )
@$(error Exiting)
endif
ifeq ($(VERSION),5.2)
else
ifeq ($(VERSION),5.3)
else
ifeq ($(VERSION),5.4)
else
ifeq ($(VERSION),5.5)
else
ifeq ($(VERSION),5.6)
else
ifeq ($(VERSION),7.0)
else
ifeq ($(VERSION),7.1)
else
ifeq ($(VERSION),7.2)
else
ifeq ($(VERSION),7.3)
else
ifeq ($(VERSION),7.4)
else
ifeq ($(VERSION),8.0)
else
ifeq ($(VERSION),8.1)
else
ifeq ($(VERSION),8.2)
else
@$(info VERSION can only be: '5.2', '5.3', '5.4', '5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1' or '8.2')
@$(info )
@$(error Exiting)
endif
endif
endif
endif
endif
endif
endif
endif
endif
endif
endif
endif
endif
@echo "Version $(VERSION) is valid"
_check-image-exists:
@if [ "$$(docker images -q $(IMAGE):$(VERSION)-$(_EXIST_IMAGE))" = "" ]; then \
>&2 echo "Docker image '$(IMAGE):$(VERSION)-$(_EXIST_IMAGE)' was not found locally."; \
###
### Checks if current image exists and is of correct architecture
###
.PHONY: check-current-image-exists
check-current-image-exists: check-flavour-is-set
check-current-image-exists:
@if [ "$$( docker images -q $(IMAGE):$(DOCKER_TAG) )" = "" ]; then \
>&2 echo "Docker image '$(IMAGE):$(DOCKER_TAG)' was not found locally."; \
>&2 echo "Either build it first or explicitly pull it from Dockerhub."; \
>&2 echo "This is a safeguard to not automatically pull the Docker image."; \
>&2 echo; \
false; \
exit 1; \
else \
echo "OK: Image $(IMAGE):$(DOCKER_TAG) exists"; \
fi; \
OS="$$( docker image inspect $(IMAGE):$(DOCKER_TAG) --format '{{.Os}}' )"; \
ARCH="$$( docker image inspect $(IMAGE):$(DOCKER_TAG) --format '{{.Architecture}}' )"; \
if [ "$${OS}/$${ARCH}" != "$(ARCH)" ]; then \
>&2 echo "Docker image '$(IMAGE):$(DOCKER_TAG)' has invalid architecture: $${OS}/$${ARCH}"; \
>&2 echo "Expected: $(ARCH)"; \
>&2 echo; \
exit 1; \
else \
echo "OK: Image $(IMAGE):$(DOCKER_TAG) is of arch $${OS}/$${ARCH}"; \
fi;
_pull-base-image:
@echo "Pulling root image for PHP ${VERSION}"
docker pull --platform $(ARCH) $(shell grep FROM $(DIR)/base/Dockerfile-${VERSION} | sed 's/^FROM\s*//g';)
###
### Checks if parent image exists and is of correct architecture
###
.PHONY: check-parent-image-exists
check-parent-image-exists: check-flavour-is-set
check-parent-image-exists:
@if [ "$(FLAVOUR)" = "work" ]; then \
if [ "$$( docker images -q $(IMAGE):$(PROD_TAG) )" = "" ]; then \
>&2 echo "Docker image '$(IMAGE):$(PROD_TAG)' was not found locally."; \
>&2 echo "Either build it first or explicitly pull it from Dockerhub."; \
>&2 echo "This is a safeguard to not automatically pull the Docker image."; \
>&2 echo; \
exit 1; \
fi; \
OS="$$( docker image inspect $(IMAGE):$(PROD_TAG) --format '{{.Os}}' )"; \
ARCH="$$( docker image inspect $(IMAGE):$(PROD_TAG) --format '{{.Architecture}}' )"; \
if [ "$${OS}/$${ARCH}" != "$(ARCH)" ]; then \
>&2 echo "Docker image '$(IMAGE):$(PROD_TAG)' has invalid architecture: $${OS}/$${ARCH}"; \
>&2 echo "Expected: $(ARCH)"; \
>&2 echo; \
exit 1; \
fi; \
elif [ "$(FLAVOUR)" = "prod" ]; then \
if [ "$$( docker images -q $(IMAGE):$(MODS_TAG) )" = "" ]; then \
>&2 echo "Docker image '$(IMAGE):$(MODS_TAG)' was not found locally."; \
>&2 echo "Either build it first or explicitly pull it from Dockerhub."; \
>&2 echo "This is a safeguard to not automatically pull the Docker image."; \
>&2 echo; \
exit 1; \
fi \
OS="$$( docker image inspect $(IMAGE):$(MODS_TAG) --format '{{.Os}}' )"; \
ARCH="$$( docker image inspect $(IMAGE):$(MODS_TAG) --format '{{.Architecture}}' )"; \
if [ "$${OS}/$${ARCH}" != "$(ARCH)" ]; then \
>&2 echo "Docker image '$(IMAGE):$(MODS_TAG)' has invalid architecture: $${OS}/$${ARCH}"; \
>&2 echo "Expected: $(ARCH)"; \
>&2 echo; \
exit 1; \
fi; \
elif [ "$(FLAVOUR)" = "mods" ]; then \
if [ "$$( docker images -q $(IMAGE):$(BASE_TAG) )" = "" ]; then \
>&2 echo "Docker image '$(IMAGE):$(BASE_TAG)' was not found locally."; \
>&2 echo "Either build it first or explicitly pull it from Dockerhub."; \
>&2 echo "This is a safeguard to not automatically pull the Docker image."; \
>&2 echo; \
exit 1; \
fi \
OS="$$( docker image inspect $(IMAGE):$(BASE_TAG) --format '{{.Os}}' )"; \
ARCH="$$( docker image inspect $(IMAGE):$(BASE_TAG) --format '{{.Architecture}}' )"; \
if [ "$${OS}/$${ARCH}" != "$(ARCH)" ]; then \
>&2 echo "Docker image '$(IMAGE):$(BASE_TAG)' has invalid architecture: $${OS}/$${ARCH}"; \
>&2 echo "Expected: $(ARCH)"; \
>&2 echo; \
exit 1; \
fi; \
fi;

View File

@@ -8,27 +8,18 @@ set -o pipefail
# Get absolute directory of this script
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
ARCH="${1:-linux/amd64}"
IMAGE="${1}"
ARCH="${2}"
TAG_BASE="${3}"
TAG_MODS="${4}"
VERSION="${5:-}"
###
### Show Usage
###
print_usage() {
echo "Usage: gen-readme.sh [<ARCH>]"
echo " gen-readme.sh <ARCH> 5.2"
echo " gen-readme.sh <ARCH> 5.3"
echo " gen-readme.sh <ARCH> 5.4"
echo " gen-readme.sh <ARCH> 5.5"
echo " gen-readme.sh <ARCH> 5.6"
echo " gen-readme.sh <ARCH> 7.0"
echo " gen-readme.sh <ARCH> 7.1"
echo " gen-readme.sh <ARCH> 7.2"
echo " gen-readme.sh <ARCH> 7.3"
echo " gen-readme.sh <ARCH> 7.4"
echo " gen-readme.sh <ARCH> 8.0"
echo " gen-readme.sh <ARCH> 8.1"
echo " gen-readme.sh <ARCH> 8.2"
echo "Usage: gen-readme.sh <IMAGE> <ARCH> <TAG_BASE> <TAG_MODS> [<VERSION>]"
}
@@ -36,25 +27,24 @@ print_usage() {
### Extract PHP modules in alphabetical order and comma separated in one line
###
get_modules() {
tag="${1}"
current_tag="${1}"
# Retrieve all modules
PHP_MODULES="$( docker run --rm --platform "${ARCH}" $(tty -s && echo '-it' || echo) --entrypoint=php devilbox/php-fpm:${tag} -m )"
PHP_MODULES="$( docker run --rm --platform "${ARCH}" "$(tty -s && echo '-it' || echo)" --entrypoint=php "${IMAGE}:${current_tag}" -m )"
ALL_MODULES=
if docker run --rm --platform "${ARCH}" $(tty -s && echo '-it' || echo) --entrypoint=find devilbox/php-fpm:${tag} /usr/local/lib/php/extensions -name 'ioncube.so' | grep -q ioncube.so; then
if docker run --rm --platform "${ARCH}" "$(tty -s && echo '-it' || echo)" --entrypoint=find "${IMAGE}:${current_tag}" /usr/local/lib/php/extensions -name 'ioncube.so' | grep -q ioncube.so; then
ALL_MODULES="${ALL_MODULES},ioncube";
fi
if docker run --rm --platform "${ARCH}" $(tty -s && echo '-it' || echo) --entrypoint=find devilbox/php-fpm:${tag} /usr/local/lib/php/extensions -name 'blackfire.so' | grep -q blackfire.so; then
if docker run --rm --platform "${ARCH}" "$(tty -s && echo '-it' || echo)" --entrypoint=find "${IMAGE}:${current_tag}" /usr/local/lib/php/extensions -name 'blackfire.so' | grep -q blackfire.so; then
ALL_MODULES="${ALL_MODULES},blackfire";
fi
if docker run --rm --platform "${ARCH}" $(tty -s && echo '-it' || echo) --entrypoint=find devilbox/php-fpm:${tag} /usr/local/lib/php/extensions -name 'psr.so' | grep -q psr.so; then
if docker run --rm --platform "${ARCH}" "$(tty -s && echo '-it' || echo)" --entrypoint=find "${IMAGE}:${current_tag}" /usr/local/lib/php/extensions -name 'psr.so' | grep -q psr.so; then
ALL_MODULES="${ALL_MODULES},psr";
fi
if docker run --rm --platform "${ARCH}" $(tty -s && echo '-it' || echo) --entrypoint=find devilbox/php-fpm:${tag} /usr/local/lib/php/extensions -name 'phalcon.so' | grep -q phalcon.so; then
if docker run --rm --platform "${ARCH}" "$(tty -s && echo '-it' || echo)" --entrypoint=find "${IMAGE}:${current_tag}" /usr/local/lib/php/extensions -name 'phalcon.so' | grep -q phalcon.so; then
ALL_MODULES="${ALL_MODULES},phalcon";
fi
@@ -85,15 +75,15 @@ get_modules() {
update_readme() {
v="${1}"
# Those sections must exist in README.md, otherwise this script will exit with errors
sed -i'' "s|<td id=\"${v//.}-base\">.*<\/td>|<td id=\"${v//.}-base\">$( get_modules "${v}-base" )<\/td>|g" "${CWD}/../README.md"
sed -i'' "s|<td id=\"${v//.}-mods\">.*<\/td>|<td id=\"${v//.}-mods\">$( get_modules "${v}-mods" )<\/td>|g" "${CWD}/../README.md"
sed -i'' "s|<td id=\"${v//.}-base\">.*<\/td>|<td id=\"${v//.}-base\">$( get_modules "${TAG_BASE}" )<\/td>|g" "${CWD}/../README.md"
sed -i'' "s|<td id=\"${v//.}-mods\">.*<\/td>|<td id=\"${v//.}-mods\">$( get_modules "${TAG_MODS}" )<\/td>|g" "${CWD}/../README.md"
}
###
### Entrypoint
###
if [ "${#}" -eq "0" ] || [ "${#}" -eq "1" ]; then
if [ "${VERSION}" = "" ]; then
# Update PHP modules for all versions at once
update_readme "5.2"
update_readme "5.3"
@@ -108,31 +98,26 @@ if [ "${#}" -eq "0" ] || [ "${#}" -eq "1" ]; then
update_readme "8.0"
update_readme "8.1"
update_readme "8.2"
elif [ "${#}" -gt "2" ]; then
# Specifying more than 1 argument is wrong
echo "Error, invalid number of arguments."
print_usage
exit 1
else
if [ "${2}" != "5.2" ] \
&& [ "${2}" != "5.3" ] \
&& [ "${2}" != "5.4" ] \
&& [ "${2}" != "5.5" ] \
&& [ "${2}" != "5.6" ] \
&& [ "${2}" != "7.0" ] \
&& [ "${2}" != "7.1" ] \
&& [ "${2}" != "7.2" ] \
&& [ "${2}" != "7.3" ] \
&& [ "${2}" != "7.4" ] \
&& [ "${2}" != "8.0" ] \
&& [ "${2}" != "8.1" ] \
&& [ "${2}" != "8.2" ]; then
if [ "${VERSION}" != "5.2" ] \
&& [ "${VERSION}" != "5.3" ] \
&& [ "${VERSION}" != "5.4" ] \
&& [ "${VERSION}" != "5.5" ] \
&& [ "${VERSION}" != "5.6" ] \
&& [ "${VERSION}" != "7.0" ] \
&& [ "${VERSION}" != "7.1" ] \
&& [ "${VERSION}" != "7.2" ] \
&& [ "${VERSION}" != "7.3" ] \
&& [ "${VERSION}" != "7.4" ] \
&& [ "${VERSION}" != "8.0" ] \
&& [ "${VERSION}" != "8.1" ] \
&& [ "${VERSION}" != "8.2" ]; then
# Argument does not match any of the PHP versions
echo "Error, invalid argument."
print_usage
exit 1
else
# Update PHP modules for one specific PHP version
update_readme "${2}"
update_readme "${VERSION}"
fi
fi