diff --git a/.buildkite/pipeline.pull_request.yml b/.buildkite/pipeline.pull_request.yml new file mode 100644 index 0000000..e870ceb --- /dev/null +++ b/.buildkite/pipeline.pull_request.yml @@ -0,0 +1,41 @@ +env: + PIPELINE_NAME: docker-php + +steps: + - block: ":question::hammer: What version do you want to test?" + fields: + - select: Select a version to test + key: selected-version + required: true + options: + - label: v8.3.6-fpm + value: 8.3.6-fpm + - label: v8.2.4-fpm + value: 8.2.4-fpm + + - label: ":hammer::docker: Running tests for the selected version" + command: | + VERSION="$$(buildkite-agent meta-data get selected-version --default 'NA')" + + bin/php tests --version "$${VERSION}" --build + + + - block: ":question::hammer: Do you want to release this version?" + + - label: Trigger Build and Release pipeline + command: | + VERSION="$$(buildkite-agent meta-data get selected-version --default 'NA')" + + cat <<- YAML | buildkite-agent pipeline upload + steps: + - trigger: ${PIPELINE_NAME} + async: true + label: ":construction::building_construction: Trigger release for version $$VERSION" + build: + commit: "${BUILDKITE_COMMIT}" + branch: "${BUILDKITE_BRANCH}" + env: + VERSION: "$$VERSION" + VERSION_IS_LATEST: no + DEPLOY_SERVICE: release + YAML diff --git a/.buildkite/pipeline.release.yml b/.buildkite/pipeline.release.yml new file mode 100644 index 0000000..287878b --- /dev/null +++ b/.buildkite/pipeline.release.yml @@ -0,0 +1,21 @@ +env: + IMAGE: bandsintown/php + +steps: + - label: ":docker: Build and Push PHP v${VERSION}" + if: build.env("VERSION") != "NA" + plugins: + - docker-compose#v5.2.0: + build: php + push: + - php:${IMAGE}:${VERSION}-${BUILDKITE_COMMIT:0:7} + + - wait + + - label: ":docker: Tag as latest and Push PHP v${VERSION}" + if: build.env("VERSION_IS_LATEST") == "yes" + plugins: + - docker-compose#v5.2.0: + push: + - php:${IMAGE}:${VERSION} + - php:${IMAGE}:latest diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml new file mode 100644 index 0000000..46ec9f3 --- /dev/null +++ b/.buildkite/pipeline.yml @@ -0,0 +1,30 @@ +steps: + # Upload pipeline for branch `master`. + # Conditions: + # Build created from branch `master` + # Not "triggered" by other builds. + - command: "buildkite-agent pipeline upload .buildkite/pipeline.master.yml" + label: ":pipeline: Master pipeline" + if: | + build.branch == "master" && + build.source != "trigger_job" + + # Upload pipeline for Pull Requests. + # Conditions: + # Not created from the `master` branch. + # Not "triggered" by other builds. + - command: "buildkite-agent pipeline upload .buildkite/pipeline.pull_request.yml" + label: ":pipeline: Pull Request pipeline" + if: | + build.branch != "master" && + build.source != "trigger_job" + + # Upload pipeline specified by $DEPLOY_SERVICE. + # Conditions: + # Build triggered by other builds. + # $DEPLOY_SERVICE is specified. + - command: "buildkite-agent pipeline upload .buildkite/pipeline.${DEPLOY_SERVICE}.yml" + label: ":pipeline: Triggered pipeline for ${DEPLOY_SERVICE}" + if: | + build.source == "trigger_job" && + build.env("DEPLOY_SERVICE") != "NA" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..729b5ab --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + + +[*.yml] +indent_size = 2 diff --git a/Dockerfile.template b/Dockerfile.template new file mode 100644 index 0000000..b14a41d --- /dev/null +++ b/Dockerfile.template @@ -0,0 +1,59 @@ +FROM bandsintown/alpine:3.19.1 as builder + +FROM %%PARENT%%:%%VERSION_FULL%%-alpine + +ENV S6_LOGGING=1 S6_OVERLAY_VERSION=1.19.1.1 GODNSMASQ_VERSION=1.0.7 CONSUL_TEMPLATE_VERSION=0.19.4 CONSUL_VERSION=0.8.4 MEMCACHED_DEPS="zlib-dev libmemcached-dev cyrus-sasl-dev" TZ="America/New_York" + +COPY --from=builder /usr/local/bin/consul /usr/local/bin/consul +COPY --from=builder /usr/local/bin/consul-template /usr/local/bin/consul-template +COPY --from=builder /usr/sbin/go-dnsmasq /usr/sbin/go-dnsmasq +COPY --from=builder /etc/cont-init.d /etc/cont-init.d +COPY --from=builder /etc/services.d /etc/services.d +COPY --from=builder /root /root + +RUN apk update && apk upgrade \ + && apk add --update \ + coreutils \ + freetype-dev \ + libjpeg-turbo-dev \ + libltdl \ + libpng-dev \ + curl wget bash tree jq bind-tools su-exec build-base gcc autoconf \ + libmemcached-libs zlib \ + && set -xe \ + && apk add --virtual .phpize-deps $PHPIZE_DEPS \ + && apk add --virtual .memcached-deps $MEMCACHED_DEPS \ + && pecl install memcached-3.1.4 \ + && echo "extension=memcached.so" > /usr/local/etc/php/conf.d/20_memcached.ini \ + && pecl install memcache \ + && echo "extension=memcache.so" > /usr/local/etc/php/conf.d/21_memcache.ini \ + && docker-php-ext-install mysqli \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) gd \ + && rm -rf /usr/share/php8 \ + && rm -rf /tmp/* \ + && apk del .memcached-deps .phpize-deps + +RUN curl -Ls https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-amd64.tar.gz | tar -xz -C / + +RUN mkdir /var/composer && \ + cd /var/composer && \ + php -r "copy('https://getcomposer.org/download/2.7.3/composer.phar', 'composer-setup.php');" && \ + php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ + php composer-setup.php --install-dir=/usr/bin --filename=composer && \ + php -r "unlink('composer-setup.php');" + +COPY rootfs / + +RUN echo $TZ > /etc/timezone \ + && touch /usr/local/var/run/php-fpm.pid \ + && echo -ne "- with $(php -v | head -n 1)\n" >> /root/.built + +EXPOSE 9000 + +# Workaround https://bugs.php.net/bug.php?id=71880 +ENV LOG_STREAM="/tmp/stdout" +RUN mkfifo $LOG_STREAM && chmod 777 $LOG_STREAM + +ENTRYPOINT ["/init"] +CMD ["/bin/sh", "-c", "php-fpm --pid /usr/local/var/run/php-fpm.pid | tail -f $LOG_STREAM"] diff --git a/bin/functions b/bin/functions deleted file mode 100644 index 902a09d..0000000 --- a/bin/functions +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env bash - -if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then - echo "Bash 4 or later is required" - exit 1 -fi - -# Get the version of the system commands -set +e -DOCKER_COMPOSE_COMMAND=$(which docker-compose) -TERRAFORM_COMMAND=$(which terraform) -set -e - -### Logs - -info() { - test "x$BUILDKITE" = "xtrue" && printf "%s\n" "--- $@" || printf "\e[1m%s\e[0m\n" "$@" -} - -debug() { - printf "➜ %s\n" "$@" -} - -success() { - printf "\e[32m✔ %s\n\e[0m" "$@" -} - -warn() { - test "x$BUILDKITE" = "xtrue" && echo "^^^ +++" - printf "\e[33m✖ %s\n\e[0m" "$@" -} - -error() { - test "x$BUILDKITE" = "xtrue" && echo "^^^ +++" - printf "\e[1m\e[31m✖ %s\n\e[0m" "$@" -} - -### Utils - -function has_item() { - local i - for i in "${@:2}"; do - [[ "$i" == "$1" ]] && return 0 - done - return 1 -} - -function merge_map() { - local -n dest_var=$1 - shift - local -n src_var - local key - for src_var in $@; do - for key in "${!src_var[@]}"; do - dest_var[$key]="${src_var[$key]}" - done - done -} - -function realpath (){ - f=$@; - if [ -d "$f" ]; then - base=""; - dir="$f"; - else - base="/$(basename "$f")"; - dir=$(dirname "$f"); - fi; - dir=$(cd "$dir" && /bin/pwd); - echo "$dir$base" -} - -function reverse(){ - local arr=(${@}) - for (( idx=${#arr[@]}-1 ; idx>=0 ; idx-- )) ; do - echo ${arr[idx]} - done -} - -### Remote scripts - -function release_docker(){ - export RELEASE_BUNDLE="docker" - curl -Ls https://s3.amazonaws.com/bit-ops-artifacts/scripts/release/${SCRIPTS_VERSION:-stable}/release.sh | bash -s -} - -function release_github(){ - curl -Ls https://s3.amazonaws.com/bit-ops-artifacts/scripts/release/${SCRIPTS_VERSION:-stable}/github.sh | bash -s -} - -function release_lambda(){ - export RELEASE_BUNDLE="zip" - export RELEASE_BUILDER_TARGET="$(mktemp -d)" - curl -Ls https://s3.amazonaws.com/bit-ops-artifacts/scripts/release/${SCRIPTS_VERSION:-stable}/release.sh | bash -s -} - -function deploy_service(){ - curl -Ls https://s3.amazonaws.com/bit-ops-artifacts/scripts/deploy/${SCRIPTS_VERSION:-stable}/service.sh | bash -s -} - -### Commands as Docker containers - -function docker-compose(){ - if [ "xtrue" != "x${DOCKER_COMPOSE_IN_DOCKER}" ]; then - test -n "${DOCKER_COMPOSE_COMMAND}" || { error "Please install Docker Compose."; exit 1;} - ${DOCKER_COMPOSE_COMMAND} $@ - return $? - fi - - # Get the latest version based on the Github release - local LATEST=$(curl -sI https://github.com/docker/compose/releases/latest | grep "Location:" | awk -v FS=/ '{print $NF}' | tr -d '\r') - local IMAGE="docker/compose:${DOCKER_COMPOSE_VERSION:-${LATEST}}" - - # Setup options for connecting to docker host - test -z "$DOCKER_HOST" && DOCKER_HOST="/var/run/docker.sock" - test -S "$DOCKER_HOST" && DOCKER_ADDR="-v $DOCKER_HOST:$DOCKER_HOST -e DOCKER_HOST" || DOCKER_ADDR="-e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH" - - # Setup volume mounts for compose config and context - test "$(pwd)" != '/' && VOLUMES="-v $(pwd):$(pwd)" - test -n "$COMPOSE_FILE" && compose_dir=$(realpath $(dirname $COMPOSE_FILE)) - test -n "$compose_dir" && VOLUMES="$VOLUMES -v $compose_dir:$compose_dir" - test -n "$HOME" && VOLUMES="$VOLUMES -v $HOME:$HOME -v $HOME:/root" # mount $HOME in /root to share docker.config - - # Only allocate tty if we detect one - test -t 1 && DOCKER_RUN_OPTIONS="-t" - test -t 0 && DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -i" - - # Run docker compose - docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ADDR $COMPOSE_OPTIONS $VOLUMES -w "$(pwd)" $IMAGE "$@" -} - -function terraform(){ - if [ "xtrue" != "x${TERRAFORM_IN_DOCKER}" ]; then - test -n "${TERRAFORM_COMMAND}" || { error "Please install Terraform."; exit 1;} - ${TERRAFORM_COMMAND} $@ - return $? - fi - - local IMAGE="hashicorp/terraform:${TERRAFORM_VERSION:-latest}" - - # Setup volume mounts for compose config and context - test "$(pwd)" != '/' && VOLUMES="-v $(pwd):$(pwd)" - test -n "$HOME" && VOLUMES="$VOLUMES -v $HOME:$HOME -v $HOME:/root" # mount $HOME in /root to share .ssh and .aws - - # Environment variables - test -n "$AWS_ACCESS_KEY_ID" && DOCKER_ENV="-e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" - test -n "$AWS_SECRET_ACCESS_KEY" && DOCKER_ENV="$DOCKER_ENV -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" - test -n "$AWS_DEFAULT_REGION" && DOCKER_ENV="$DOCKER_ENV -e AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" - test -n "$TF_PLUGIN_CACHE_DIR" && DOCKER_ENV="$DOCKER_ENV -e TF_PLUGIN_CACHE_DIR=$TF_PLUGIN_CACHE_DIR" - - # Only allocate tty if we detect one - test -t 1 && DOCKER_RUN_OPTIONS="-t" - test -t 0 && DOCKER_RUN_OPTIONS="$DOCKER_RUN_OPTIONS -i" - - # Run terraform - docker run --rm $DOCKER_RUN_OPTIONS $DOCKER_ENV $VOLUMES -w "$(pwd)" $IMAGE "$@" -} \ No newline at end of file diff --git a/bin/php b/bin/php index e1d9f5d..fbc8e74 100755 --- a/bin/php +++ b/bin/php @@ -3,186 +3,119 @@ set +x set -e BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -BASE_DIR="$( cd "$BIN_DIR/.." && pwd )" - -# Load lib functions -lib_functions="$BIN_DIR/functions" -echo "Using version: ${SCRIPTS_VERSION:-stable}" -curl -Ls -o "${lib_functions}" "https://s3.amazonaws.com/bit-ops-artifacts/scripts/lib/${SCRIPTS_VERSION:-stable}/functions" -source "${lib_functions}" - -# Define valid commands -declare -A valid_commands -valid_commands[build]="Build the Docker image" -valid_commands[tests]="Run the tests" -valid_commands[debug]="Run tests in debug mode" -valid_commands[release]="Release the Docker image to Docker Hub" -valid_commands[help]="Display this help" - -declare -A valid_options -valid_options["--version"]="The version we want to build, tests or release" -valid_options["--verbose"]="Display commands to run" -valid_options["--build"]="Build images before running tests" -valid_options["--tests"]="Comma separated list of tests to run" -valid_options["--test"]="A single test to run (in debug mode)" +root_dir="$( cd "$BIN_DIR/.." && pwd )" + +docker_compose_tests=(test-image test-consul-template) help() { - echo "Usage: $(basename $0) COMMAND OPTIONS" >&2 - echo - echo "A script to manage '$(basename $0)' image" - echo - echo "Commands:" - padding=' ' - for cmd in "${!valid_commands[@]}"; do - description=${valid_commands[$cmd]} - printf " %s %s %s\n" "$cmd" "${padding:${#cmd}}" "$description" - done | sort -n -k3 - echo - echo "Options:" - for cmd in "${!valid_options[@]}"; do - description=${valid_options[$cmd]} - printf " %s %s %s\n" "$cmd" "${padding:${#cmd}}" "$description" - done | sort -n -k3 - exit 0 + cat <<- EOHELP +Usage: $(basename $0) COMMAND OPTIONS +Example: $(basename $0) tests --version 3.19.1 --build + +A thin wrapper around docker compose to manage the '$image_name' image + +COMMANDS: + +help This help message. +tests Run tests for the selected version image. +build Build an image for the selected version. + +OPTIONS: + +--version Required. The version of the image to build or test. +--build Build the image before running the tests. Used with the 'tests' command. +--variant The variant of the image to build or test. Used with the 'build' command. +EOHELP } -parse_opts(){ - until [ "$1" = "" ] ; do - local first_char="${1:0:1}" - if [ "$first_char" = "-" ] ; then - local option="${1:1}" - case $option in - build|-build) - export build="true";; - debug|-debug) - export debug="true";; - tests|-tests) - shift - test -z $1 && help || export TESTS=(${1//,/ }) - ;; - test|-test) - shift - test -z $1 && help || export TEST=$1; export TESTS=($1) - ;; - verbose|-verbose) - export verbose="true" - ;; - versions|-versions) - shift - test -z $1 && help || export VERSIONS=(${1//,/ }) - ;; - version|-version) - shift - test -z $1 && help || export VERSION=$1; export VERSIONS=(${1//,/ }) - ;; - esac - fi - shift - done +function parse_opts() { + while [ $# -gt 0 ]; do + local option="$1" + + case $option in + --build) + run_build_opt="true" + ;; + --version) + VERSION="$2" + [[ $VERSION =~ ^[0-9]+\.[0-9]+(\.[0-9]+)*-fpm$ ]] || { echo "Invalid version format"; exit 1; } + export VERSION + ;; + --variant) + variant="$2" + [[ $variant =~ (test) ]] || { echo "==> Invalid variant"; exit 1; } + ;; + --verbose) + verbose="true" + ;; + esac + + shift + done } -build(){ - for VERSION in "${VERSIONS[@]}"; do - export VERSION=${VERSION}; - build_image - info "Tagging image '$IMAGE:$VERSION' to '$IMAGE:$VERSION-$BUILD_NUM'..." - run_command="docker tag $IMAGE:$VERSION $IMAGE:$VERSION-$BUILD_NUM" - test "xtrue" = "x${verbose}" && debug "${run_command}" + +function run_build() { + # Build image + test -n "${VERSION}" || { echo "Specify the version you want to build"; exit 1; } + echo "==> Building image for ${VERSION} ..." + run_command="docker build -f versions/${VERSION}/Dockerfile -t bandsintown/php:${VERSION} ." + ${run_command} - done -} -build_image(){ - cd $BASE_DIR - test -n "$VERSION" || { error "Please set --version argument to define the version you want to debug"; exit 1;} - info "Building image '$IMAGE:$VERSION'..." - run_command="docker build --pull -t $IMAGE:$VERSION -f $BASE_DIR/latest-versions/$VERSION/Dockerfile ." - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} -} + if [[ -n "${variant}" ]]; then + # Build image variant + echo "==> Building image ${VERSION}-${variant}..." + run_command="docker build -f versions/${VERSION}/${variant}/Dockerfile -t bandsintown/php:${VERSION}-${variant} ." -build_test_image(){ - cd $BASE_DIR - test -n "$VERSION" || { error "Please set --version argument to define the version you want to debug"; exit 1;} - info "Building image '$IMAGE_TEST:$VERSION'..." - run_command="docker build -t $IMAGE_TEST:$VERSION -f $BASE_DIR/versions/$VERSION/Dockerfile-tests ." - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} + ${run_command} + fi } -run_debug(){ - test -n "$VERSION" || { error "Please set --version argument to define the version you want to debug"; exit 1;} - test -n "$TEST" || { error "Please set --test argument to define the test you want to debug"; exit 1; } - test -n "${build}" && { build_image; build_test_image; } - info "Running test in debug '$TEST' on version '$VERSION'..." - rm -f .env && echo "VERSION=${VERSION}" > .env - run_command="docker-compose -f $BASE_DIR/tests.yml -f $BASE_DIR/debug.yml run --rm $TEST" - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} - docker-compose -f $BASE_DIR/tests.yml stop - docker-compose -f $BASE_DIR/tests.yml rm -f -} +function run_tests() { + test -n "${VERSION}" || { error "Specify the version you want to test"; exit 1; } + test -n "${run_build_opt}" && variant="test" && run_build + echo "==> Running tests for ${VERSION} ..." -tests(){ - for VERSION in "${VERSIONS[@]}"; do - test -n "${build}" && { build_image; build_test_image; } - rm -f .env && echo "VERSION=${VERSION}" > .env - for TEST in "${TESTS[@]}"; do - info "Running test '$TEST' on version '$VERSION'..." - run_command="docker-compose -f $BASE_DIR/tests.yml run --rm $TEST" - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} + for dc_test in "${docker_compose_tests[@]}"; do + echo "==> Running test ${dc_test} for ${VERSION} ..." + run_command="docker compose run --rm ${dc_test}" + ${run_command} done - docker-compose -f $BASE_DIR/tests.yml stop - docker-compose -f $BASE_DIR/tests.yml rm -f - done } -release(){ - test -n "${DOCKERHUB_USERNAME}" || { error "Please set the variable DOCKERHUB_USERNAME to release."; exit 1;} - test -n "${DOCKERHUB_PASSWORD}" || { error "Please set the variable DOCKERHUB_PASSWORD to release."; exit 1;} - build - info "Login to Docker Hub..." - docker login --username "${DOCKERHUB_USERNAME}" --password "${DOCKERHUB_PASSWORD}" - for VERSION in "${VERSIONS[@]}"; do - info "Releasing '$IMAGE:$VERSION'..." - run_command="docker push $IMAGE:$VERSION" - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} - info "Releasing '$IMAGE:$VERSION-$BUILD_NUM'..." - run_command="docker push $IMAGE:$VERSION-$BUILD_NUM" - test "xtrue" = "x${verbose}" && debug "${run_command}" - ${run_command} - done + +trap cleanup_containers EXIT + +function cleanup_containers() { + docker compose down + exit } # Check command command="$1" -test "$command" = "help" && help -has_item "$command" "${!valid_commands[@]}" || help - -cd $BASE_DIR - -VERSIONS=($(ls $BASE_DIR/latest-versions)) -IMAGE="bandsintown/$(basename $0)" -IMAGE_TEST="$IMAGE-test" -BUILD_NUM="$(git rev-parse --short HEAD)" -TESTS=(test-image test-consul-template) -# Check programs needed to tests are installed -type docker >/dev/null 2>&1 || { error "Please install Docker to run tests."; exit 1;} -type docker-compose >/dev/null 2>&1 || { error "Please install Docker Compose to run tests."; exit 1;} +cd "$root_dir" # Parse options -parse_opts $* +parse_opts "$@" # Run the command -case "$command" in - help) - help;; - debug) - run_debug $@;; - *) - eval $*;; -esac \ No newline at end of file +case "${command}" in + help|-h|--help) + help + ;; + tests) + shift + run_tests "$@" + ;; + build) + shift + run_build "$@" + ;; + *) + echo "Invalid command" + help + ;; +esac diff --git a/build_dockerfiles.sh b/build_dockerfiles.sh new file mode 100755 index 0000000..0b374f3 --- /dev/null +++ b/build_dockerfiles.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# Originally from CircleCi's `gen-dockerfiles.sh` +# https://github.com/CircleCI-Public/cimg-shared/blob/30bfff7494c192d12364b5658089fad9d3d0e78d/gen-dockerfiles.sh + +# This script generates Dockerfiles for a given set of versions and variants. +# It looks for a `Dockerfile.template` file in the root of the repository and +# an optional `variant.Dockerfile.template` in the `variants` directory. +# Then it creates the Dockerfiles in the `versions/[VERSION]` and +# `versions/[VERSION]/[VARIANT]` directories. + +# A Docker image is a combination of REGISTRY/NAMESPACE/REPOSITORY[:TAG]. +# Import image information +source ./manifest + +export CREATE_VERSIONS=("$@") + +# A version can be a major.minor or major.minor.patch version string. +# Additionally versions/version groups are separated by spaces. +# +# Examples: +# +# 1.13.1 v1.14.2 +# v20.04 +# +# Template variables exists in the `Dockerfile.template` files. The start and +# end with two percent symbles `%%`. During Dockerfile generation, they get +# replaced with actual valuables. Here's what's available to use: +# +# %%VERSION_FULL%% - the complete version passed to the script such as `1.2.3` +# %%VERSION_MAJOR%% - just the major integer of the version such as `1` +# %%VERSION_MINOR%% - the major and minor integers of the version with a decimal in the middle such as `1.2` +# %%ALIAS1%% - what's passed as the alias when passing version strings to the build script (see above) +# %%PARAM1%% - what's passed as the paramater when passing version strings to the build script (see above) + +##### +# Helper functions. +##### + +# Parses all template variables, regardless of if it's a main or variant image +parse_template_variables () { + + local variantPath=${1} + local parent=${2} + local fileTemplate=${3} + local parentTag=${4} + local directory=${5} + + [[ -d "versions/$directory" ]] || mkdir "versions/$directory" + + sed -e 's!%%PARENT%%!'"${parent}"'!g' "${fileTemplate}" > "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's/%%PARENT_TAG%%/'"${parentTag}"'/g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's/%%NAMESPACE%%/'"${namespace}"'/g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's/%%VERSION_FULL%%/'"${vgVersionFull}"'/g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's/%%VERSION_MINOR%%/'"${vgVersionMinor}"'/g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's/%%VERSION_MAJOR%%/'"${vgVersionMajor}"'/g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's!%%PARAM1%%!'"${vgParam1}"'!g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" + sed -i.bak 's!%%ALIAS1%%!'"${vgAlias1}"'!g' "./versions/${vgVersionFull}/${variantPath}Dockerfile" +} + +filepath_templating () { + if [[ -f "./variants/${variant}.Dockerfile.template" ]]; then + fileTemplate="./variants/${variant}.Dockerfile.template" + else + echo "Error: Variant ${variant} doesn't exist. Exiting." + exit 2 + fi +} + +##### +# Starting version loop. +##### +for versionGroup in "$@"; do + + # Process the version group(s) that were passed to this script. + if [[ "$versionGroup" == *"#"* ]]; then + vgParam1=$(cut -d "#" -f2- <<< "$versionGroup") + versionGroup="${versionGroup//$vgParam1}" + versionGroup="${versionGroup//\#}" + fi + + if [[ "$versionGroup" == *"="* ]]; then + vgAlias1=$(cut -d "=" -f2- <<< "$versionGroup") + versionGroup="${versionGroup//$vgAlias1}" + versionGroup="${versionGroup//=}" + fi + + vgVersionFull=$(cut -d "v" -f2- <<< "$versionGroup") + + if [[ $vgVersionFull =~ ^[0-9]+\.[0-9]+ ]]; then + vgVersionMinor=${BASH_REMATCH[0]} + else + echo "Version matching (minor) failed." >&2 + exit 1 + fi + + if [[ $vgVersionFull =~ ^[0-9]+ ]]; then + vgVersionMajor=${BASH_REMATCH[0]} + else + echo "Version matching (major) failed." >&2 + exit 1 + fi + + [[ -d "versions/$vgVersionFull" ]] || mkdir "versions/$vgVersionFull" + + # no parentTag loop; creates Dockerfiles and variants + if [[ -z "${parentTags[0]}" ]]; then + parse_template_variables "" "$parent" "./Dockerfile.template" "$vgVersionFull" "$vgVersionFull" + + for variant in "${variants[@]}"; do + filepath_templating + parse_template_variables "$variant/" "$repository" "$fileTemplate" "$vgVersionFull" "$vgVersionFull/$variant" + done + else + + # parentTag loop; one Dockerfile will be created along with however many variants there are for each parentTag + for parentTag in "${parentTags[@]}"; do + if [[ -n $parentTag ]]; then + parse_template_variables "$parentTag/" "$parent" "./Dockerfile.template" "$parentTag" "$vgVersionFull/$parentTag" + + for variant in "${variants[@]}"; do + filepath_templating + parse_template_variables "$parentTag/$variant/" "$repository" "$fileTemplate" "$vgVersionFull-$parentSlug-$parentTag" "$vgVersionFull/$parentTag/$variant" + done + fi + done + fi + + # This .bak thing fixes a Linux/macOS compatibility issue, but the files are cleaned up + find . -name \*.bak -type f -delete +done diff --git a/debug.yml b/docker-compose.debug.yml similarity index 100% rename from debug.yml rename to docker-compose.debug.yml diff --git a/tests.yml b/docker-compose.tests.yml similarity index 62% rename from tests.yml rename to docker-compose.tests.yml index 3087ee9..7af05fa 100644 --- a/tests.yml +++ b/docker-compose.tests.yml @@ -1,18 +1,27 @@ -version: '2' - # Defines the services services: + php-test: + image: bandsintown/php:${VERSION}-test + build: + context: . + dockerfile: ./versions/${VERSION}/test/Dockerfile + container_name: php-test + environment: + - SERVICE_NAME=php-fpm + - CONSUL_HTTP_ADDR=consul:8500 + ports: + - 9000:9000 # Test image (packages, services, scripts) test-image: - image: bandsintown/php-test:${VERSION} + image: bandsintown/php:${VERSION}-test command: bats /tests/image environment: - VERSION=${VERSION} # Tests for consul template test-consul-template: - image: bandsintown/php-test:${VERSION} + image: bandsintown/php:${VERSION}-test command: dockerize -wait http://consul:8500 -timeout 10s bats /tests/consul-template depends_on: - consul @@ -23,7 +32,7 @@ services: # Consul consul: - image: consul + image: consul:0.7.5 command: "agent -dev -client 0.0.0.0 -ui" ports: - 8500 @@ -32,4 +41,4 @@ services: # We disable the log in order to increase lisibility in CI logging: - driver: "none" \ No newline at end of file + driver: "none" diff --git a/docker-compose.yml b/docker-compose.yml index 7c30b0e..ffded68 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,39 @@ -version: "2" - services: php: + image: bandsintown/php:${VERSION} + build: + context: . + dockerfile: ./versions/${VERSION}/Dockerfile + + php-test: + image: bandsintown/php:${VERSION}-test build: context: . - dockerfile: ./versions/latest/Dockerfile - container_name: php + dockerfile: ./versions/${VERSION}/test/Dockerfile environment: - SERVICE_NAME=php-fpm - CONSUL_HTTP_ADDR=consul:8500 ports: - 9000:9000 + # Test image (packages, services, scripts) + test-image: + image: bandsintown/php:${VERSION}-test + command: bats /tests/image + environment: + - VERSION=${VERSION} + + # Tests for consul template + test-consul-template: + image: bandsintown/php:${VERSION}-test + command: dockerize -wait http://consul:8500 -timeout 10s bats /tests/consul-template + depends_on: + - consul + environment: + - DISABLE_CONSUL_TEMPLATE=true + - CONSUL_HTTP_ADDR=consul:8500 + - VERSION=${VERSION} + # Consul consul: image: consul:0.7.5 @@ -44,4 +66,4 @@ services: volumes: - ./consulator.yml:/app/consulator.yml environment: - - CONSUL_HTTP_ADDR=consul:8500 \ No newline at end of file + - CONSUL_HTTP_ADDR=consul:8500 diff --git a/manifest b/manifest new file mode 100644 index 0000000..0bd953a --- /dev/null +++ b/manifest @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# This file is not used. Added as a reference for the variables that need to be set. + +# A Docker image is a combination of REGISTRY/NAMESPACE/REPOSITORY[:TAG]. +# e.g. docker.io/bandsintown/alpine:3.19.1 +registry=dockerhub +namespace=bandsintown +repository=php + +# The base image our image is based on +parent=php + +# Any variants of our image +# Test images are considered a variant even if we won't publish them +variants=(test) + +# Dependencies for our image +s6_overlay_version="1.19.1.1" +godnsmasq_version="1.0.7" +consul_template_version="0.19.4" +consul_version="0.7.5" diff --git a/variants/test.Dockerfile.template b/variants/test.Dockerfile.template new file mode 100644 index 0000000..72d87ad --- /dev/null +++ b/variants/test.Dockerfile.template @@ -0,0 +1,18 @@ +FROM %%NAMESPACE%%/%%PARENT%%:%%VERSION_FULL%% + +ENV BATS_VERSION=0.4.0 DOCKERIZE_VERSION=v0.2.0 + +COPY tests /tests +WORKDIR /tests + +RUN exec 2>&1 && apk add --update bind-tools bc jq \ + && curl -Ls https://codeload.github.com/sstephenson/bats/zip/v$BATS_VERSION -o /tmp/bats.zip \ + && cd /tmp \ + && unzip -q bats.zip \ + && ./bats-${BATS_VERSION}/install.sh /usr/local \ + && ln -sf /usr/local/libexec/bats /usr/local/bin/bats \ + && wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm -f bats.zip dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + +CMD ["bash"] diff --git a/latest-versions/8.2.4-fpm/Dockerfile b/versions/8.3.6-fpm/Dockerfile similarity index 85% rename from latest-versions/8.2.4-fpm/Dockerfile rename to versions/8.3.6-fpm/Dockerfile index f54c039..e406e0a 100644 --- a/latest-versions/8.2.4-fpm/Dockerfile +++ b/versions/8.3.6-fpm/Dockerfile @@ -1,6 +1,6 @@ -FROM bandsintown/alpine:3.17.3 as builder +FROM bandsintown/alpine:3.19.1 as builder -FROM php:8.2.4-fpm-alpine +FROM php:8.3.6-fpm-alpine ENV S6_LOGGING=1 S6_OVERLAY_VERSION=1.19.1.1 GODNSMASQ_VERSION=1.0.7 CONSUL_TEMPLATE_VERSION=0.19.4 CONSUL_VERSION=0.8.4 MEMCACHED_DEPS="zlib-dev libmemcached-dev cyrus-sasl-dev" TZ="America/New_York" @@ -38,8 +38,8 @@ RUN curl -Ls https://github.com/just-containers/s6-overlay/releases/download/v${ RUN mkdir /var/composer && \ cd /var/composer && \ - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \ - php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" && \ + php -r "copy('https://getcomposer.org/download/2.7.3/composer.phar', 'composer-setup.php');" && \ + php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" \ php composer-setup.php --install-dir=/usr/bin --filename=composer && \ php -r "unlink('composer-setup.php');" diff --git a/latest-versions/8.2.4-fpm/Dockerfile-tests b/versions/8.3.6-fpm/test/Dockerfile similarity index 95% rename from latest-versions/8.2.4-fpm/Dockerfile-tests rename to versions/8.3.6-fpm/test/Dockerfile index 93420d7..bd1f344 100644 --- a/latest-versions/8.2.4-fpm/Dockerfile-tests +++ b/versions/8.3.6-fpm/test/Dockerfile @@ -1,4 +1,4 @@ -FROM bandsintown/php:8.2.4-fpm +FROM bandsintown/php:8.3.6-fpm ENV BATS_VERSION=0.4.0 DOCKERIZE_VERSION=v0.2.0