From 1ce18712fdb268ea728fb8296ca1a9feb11b64bb Mon Sep 17 00:00:00 2001 From: "mathieu.brunot" Date: Fri, 5 Feb 2021 01:35:57 +0100 Subject: [PATCH] :sparkles: Improve CI and update Signed-off-by: mathieu.brunot --- .github/workflows/hooks.yml | 29 ++++++++ README.md | 10 ++- images/.gitkeep | 0 images/1.0/alpine/.dockerignore | 1 + images/1.0/alpine/.dockertags | 1 + images/1.0/alpine/Dockerfile | 7 +- images/1.0/alpine/docker-compose.test.yml | 82 +++++++++++++++-------- images/1.0/alpine/entrypoint.sh | 43 ++++++++---- images/1.0/alpine/hooks/README.md | 3 + images/1.0/alpine/hooks/build | 6 +- images/1.0/alpine/hooks/post_push | 13 ++++ images/1.0/alpine/hooks/post_test | 7 ++ images/1.0/alpine/hooks/pre_test | 8 +++ images/1.0/alpine/hooks/run | 30 +++++++++ images/1.0/alpine/hooks/test | 30 +++++++++ images/1.0/alpine/test/Dockerfile | 10 ++- images/1.0/alpine/test/docker_test.sh | 33 +++++---- images/1.0/debian/.dockerignore | 1 + images/1.0/debian/.dockertags | 1 + images/1.0/debian/Dockerfile | 7 +- images/1.0/debian/docker-compose.test.yml | 58 ++++++++++------ images/1.0/debian/entrypoint.sh | 43 ++++++++---- images/1.0/debian/hooks/README.md | 3 + images/1.0/debian/hooks/build | 6 +- images/1.0/debian/hooks/post_push | 13 ++++ images/1.0/debian/hooks/post_test | 7 ++ images/1.0/debian/hooks/pre_test | 8 +++ images/1.0/debian/hooks/run | 30 +++++++++ images/1.0/debian/hooks/test | 30 +++++++++ images/1.0/debian/test/Dockerfile | 10 ++- images/1.0/debian/test/docker_test.sh | 33 +++++---- template/.dockerignore | 1 + template/Dockerfile.alpine | 2 +- template/Dockerfile.debian | 2 +- template/docker-compose_alpine.yml | 66 ++++++++++++------ template/docker-compose_debian.yml | 46 +++++++++---- template/entrypoint.sh | 10 +-- template/hooks/README.md | 1 - template/hooks/build | 6 +- template/hooks/post_push | 13 ++++ template/hooks/post_test | 7 ++ template/hooks/pre_test | 8 +++ template/hooks/run | 30 +++++++++ template/hooks/test | 30 +++++++++ template/test/Dockerfile | 10 ++- template/test/docker_test.sh | 33 +++++---- update.sh | 48 ++++++++++++- 47 files changed, 696 insertions(+), 180 deletions(-) create mode 100644 .github/workflows/hooks.yml delete mode 100644 images/.gitkeep create mode 100644 images/1.0/alpine/.dockertags create mode 100644 images/1.0/alpine/hooks/README.md mode change 100644 => 100755 images/1.0/alpine/hooks/build create mode 100755 images/1.0/alpine/hooks/post_push create mode 100755 images/1.0/alpine/hooks/post_test create mode 100755 images/1.0/alpine/hooks/pre_test create mode 100755 images/1.0/alpine/hooks/run create mode 100755 images/1.0/alpine/hooks/test create mode 100644 images/1.0/debian/.dockertags create mode 100644 images/1.0/debian/hooks/README.md mode change 100644 => 100755 images/1.0/debian/hooks/build create mode 100755 images/1.0/debian/hooks/post_push create mode 100755 images/1.0/debian/hooks/post_test create mode 100755 images/1.0/debian/hooks/pre_test create mode 100755 images/1.0/debian/hooks/run create mode 100755 images/1.0/debian/hooks/test mode change 100644 => 100755 template/hooks/build create mode 100755 template/hooks/post_push create mode 100755 template/hooks/post_test create mode 100755 template/hooks/pre_test create mode 100755 template/hooks/run create mode 100755 template/hooks/test diff --git a/.github/workflows/hooks.yml b/.github/workflows/hooks.yml new file mode 100644 index 0000000..8d249ae --- /dev/null +++ b/.github/workflows/hooks.yml @@ -0,0 +1,29 @@ + +name: Docker Image CI + +on: + push: + branches: + - master + - develop + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['1.0'] + variant: ['debian', 'alpine'] + + steps: + - name: Check Out Repo + uses: actions/checkout@v2 + + - name: Execute CI hooks + id: docker_build + run: | + cd ./images/${{ matrix.version }}/${{ matrix.variant }}/ + ./hooks/run + + - name: Display docker images + run: docker images diff --git a/README.md b/README.md index 89a1baa..59e33e9 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ After replacing all variables: [![License: AGPL v3][uri_license_image]][uri_license] [![Docs](https://img.shields.io/badge/Docs-Github%20Pages-blue)](https://monogramm.github.io/__app_slug__/) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/Monogramm/docker-__app_slug__/Docker%20Image%20CI)](https://github.com/Monogramm/docker-__app_slug__/actions) [![Build Status](https://travis-ci.org/Monogramm/docker-__app_slug__.svg)](https://travis-ci.org/Monogramm/docker-__app_slug__) [![Docker Automated buid](https://img.shields.io/docker/cloud/build/monogramm/docker-__app_slug__.svg)](https://hub.docker.com/r/monogramm/docker-__app_slug__/) [![Docker Pulls](https://img.shields.io/docker/pulls/monogramm/docker-__app_slug__.svg)](https://hub.docker.com/r/monogramm/docker-__app_slug__/) @@ -41,8 +42,12 @@ __app_description__ [Dockerhub monogramm/docker-__app_slug__](https://hub.docker.com/r/monogramm/docker-__app_slug__/) -- `alpine` `latest` -- `debian` + + +- 1.0.0-debian 1.0-debian debian (`images/1.0/debian/Dockerfile`) +- 1.0.0-alpine 1.0-alpine alpine 1.0.0 1.0 latest (`images/1.0/alpine/Dockerfile`) + + ## How to run this image @@ -61,7 +66,6 @@ See **__app_name__** base image documentation for details. If you got any questions or problems using the image, please visit our [Github Repository](https://github.com/Monogramm/docker-__app_slug__) and write an issue. - [uri_license]: http://www.gnu.org/licenses/agpl.html [uri_license_image]: https://img.shields.io/badge/License-AGPL%20v3-blue.svg diff --git a/images/.gitkeep b/images/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/images/1.0/alpine/.dockerignore b/images/1.0/alpine/.dockerignore index 71ec7b1..8ed1049 100644 --- a/images/1.0/alpine/.dockerignore +++ b/images/1.0/alpine/.dockerignore @@ -1,2 +1,3 @@ # Ignore CI docker-compose docker-compose.yml +.dockertags diff --git a/images/1.0/alpine/.dockertags b/images/1.0/alpine/.dockertags new file mode 100644 index 0000000..9c54034 --- /dev/null +++ b/images/1.0/alpine/.dockertags @@ -0,0 +1 @@ +1.0.0-alpine 1.0-alpine alpine 1.0.0 1.0 latest diff --git a/images/1.0/alpine/Dockerfile b/images/1.0/alpine/Dockerfile index a9a876a..d4049f9 100644 --- a/images/1.0/alpine/Dockerfile +++ b/images/1.0/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine +FROM alpine:3.12 # If based on official images # FROM __app_owner_slug__/__app_slug__:alpine @@ -16,6 +16,11 @@ ARG VCS_REF ARG BUILD_DATE ARG VERSION=1.0.0 +# Keep track of image version +RUN set -ex; \ + mkdir -p '/app/src'; \ + echo "${VERSION} ${VCS_REF} ${BUILD_DATE}" > '/app/src/.docker-version' + # Container labels (http://label-schema.org/) # Container annotations (https://github.com/opencontainers/image-spec) LABEL maintainer="Monogramm maintainers " \ diff --git a/images/1.0/alpine/docker-compose.test.yml b/images/1.0/alpine/docker-compose.test.yml index 7582df9..d62c3a7 100644 --- a/images/1.0/alpine/docker-compose.test.yml +++ b/images/1.0/alpine/docker-compose.test.yml @@ -1,8 +1,13 @@ version: "2.3" +volumes: + __app_slug___data: + __app_slug___db: + __app_slug___web: + networks: - __app_uppercase_slug___external_network: - __app_uppercase_slug___internal_network: + __app_slug___external_network: + __app_slug___internal_network: internal: true services: @@ -19,19 +24,23 @@ services: __app_slug__-nginx: condition: service_started networks: - - __app_uppercase_slug___internal_network + - __app_slug___internal_network volumes_from: - __app_slug__ - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + # If you want, you can sync time & timezone with host + #volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + environment: + - DOCKER_TEST_CONTAINER=__app_slug__ + - DOCKER_WEB_CONTAINER=__app_slug__-nginx __app_slug__: # For development or CI, tag build from local Dockerfile build: context: ./ dockerfile: Dockerfile - args: + args: - TAG=${TAG} - VCS_REF=${VCS_REF} - BUILD_DATE=${BUILD_DATE} @@ -45,18 +54,19 @@ services: links: - __app_slug__-db expose: - - '9000' - #ports: - # - "9000:9000" + - "9000" + ports: + - "9090:9000" networks: - - __app_uppercase_slug___internal_network + - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - __app_uppercase_slug___DB_TYPE=pgsql - - __app_uppercase_slug___DB_HOST=$__app_slug__-db + - __app_uppercase_slug___DB_HOST=__app_slug__-db - __app_uppercase_slug___DB_NAME=$__app_uppercase_slug___DB_NAME - __app_uppercase_slug___DB_USER=$__app_uppercase_slug___DB_USER - __app_uppercase_slug___DB_PASSWORD=$__app_uppercase_slug___DB_PASSWD @@ -65,16 +75,29 @@ services: image: postgres:latest container_name: __app_slug__-db restart: always + healthcheck: + test: + [ + "CMD", + "pg_isready", + "-U", + "${__app_uppercase_slug___DB_USER}", + "-d", + "${__app_uppercase_slug___DB_NAME}", + ] + interval: 60s + retries: 5 + expose: + - "5432" #ports: # - "5432:5432" - healthcheck: - test: ["CMD", "pg_isready", "-U", "${__app_uppercase_slug___DB_USER}", "-d", "${__app_uppercase_slug___DB_NAME}"] networks: - - __app_uppercase_slug___internal_network + - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/db:/var/lib/postgresql/data - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___db:/var/lib/postgresql/data + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - POSTGRES_DB=$__app_uppercase_slug___DB_NAME - POSTGRES_USER=$__app_uppercase_slug___DB_USER @@ -85,21 +108,22 @@ services: container_name: __app_slug__-nginx restart: always ports: - - "80:80" + - "8080:80" # If you need SSL connection - # - "443:443" + # - "8443:443" networks: - - __app_uppercase_slug___internal_network - - __app_uppercase_slug___external_network + - __app_slug___internal_network + - __app_slug___external_network depends_on: __app_slug__: condition: service_started links: - __app_slug__ volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro # TODO Provide a custom nginx conf #- ./nginx.conf:/etc/nginx/nginx.conf:ro # If you need SSL connection, you can provide your own certificates @@ -107,4 +131,4 @@ services: # - ./certs-data:/data/letsencrypt environment: - NGINX_HOST=localhost # set your local domain or your live domain - # - NGINX_CGI=__app_slug__:9000 # same as __app_slug__ container name + #- NGINX_CGI=__app_slug__:9000 # same as __app_slug__ container name diff --git a/images/1.0/alpine/entrypoint.sh b/images/1.0/alpine/entrypoint.sh index 6316e07..1db8fe5 100755 --- a/images/1.0/alpine/entrypoint.sh +++ b/images/1.0/alpine/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh ## ## Docker image for __app_name__. -## Copyright (C) 2020 Monogramm +## Copyright (C) 2021 Monogramm ## set -e @@ -9,7 +9,7 @@ set -e # Functions log() { - echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $@" + echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $*" } # wait for file/directory to exists @@ -24,7 +24,7 @@ wait_for_file() { WAIT_STEP=${2:-10} WAIT_TIMEOUT=${3:--1} - while [ ! -d "${WAIT_FOR_FILE}" ] ; do + while [ ! -d "${WAIT_FOR_FILE}" ]; do if [ "${WAIT_TIMEOUT}" -gt 0 ] && [ "${WAIT_TIME}" -gt "${WAIT_TIMEOUT}" ]; then log "File '${WAIT_FOR_FILE}' was not available on time!" exit 1 @@ -32,7 +32,7 @@ wait_for_file() { log "Waiting file '${WAIT_FOR_FILE}'..." sleep "${WAIT_STEP}" - WAIT_TIME=$(( WAIT_TIME + WAIT_STEP )) + WAIT_TIME=$((WAIT_TIME + WAIT_STEP)) done log "File '${WAIT_FOR_FILE}' exists." } @@ -67,7 +67,7 @@ wait_for_service() { WAIT_STEP=${3:-10} WAIT_TIMEOUT=${4:--1} - while ! nc -z "${WAIT_FOR_ADDR}" "${WAIT_FOR_PORT}" ; do + while ! nc -z "${WAIT_FOR_ADDR}" "${WAIT_FOR_PORT}"; do if [ "${WAIT_TIMEOUT}" -gt 0 ] && [ "${WAIT_TIME}" -gt "${WAIT_TIMEOUT}" ]; then log "Service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}' was not available on time!" exit 1 @@ -75,13 +75,13 @@ wait_for_service() { log "Waiting service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}'..." sleep "${WAIT_STEP}" - WAIT_TIME=$(( WAIT_TIME + WAIT_STEP )) + WAIT_TIME=$((WAIT_TIME + WAIT_STEP)) done log "Service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}' available." } wait_for_services() { - if [ -z "${WAIT_FOR_SERVICES}" ]; then + if [ -z "${WAIT_FOR_SERVICES:-$1}" ]; then log "Missing env var 'WAIT_FOR_SERVICES' defining services to wait for!" exit 1 fi @@ -95,6 +95,25 @@ wait_for_services() { } +# init / update application +init() { + # Check version + if [ ! -f "./.docker-version" ]; then + log "[TODO] __app_name__ init to $(cat /app/src/.docker-version)..." + elif ! cmp --silent "./.docker-version" "/app/src/.docker-version"; then + log "[TODO] __app_name__ update from $(cat ./.docker-version) to $(cat /app/src/.docker-version)..." + fi + + cp -p "/app/src/.docker-version" "./.docker-version" +} + +# start application +start() { + init + + echo "[TODO] Start main service" +} + # display help print_help() { echo "Monogramm Docker entrypoint for __app_name__. @@ -114,9 +133,9 @@ Options: # Execute task based on command case "${1}" in - # Management tasks - ("--help") print_help ;; - # Service tasks - ("start") echo "TODO Start main service" ;; - (*) exec "$@" ;; +# Management tasks +-h|--help) print_help ;; +# Service tasks +start) start ;; +*) exec "$@" ;; esac diff --git a/images/1.0/alpine/hooks/README.md b/images/1.0/alpine/hooks/README.md new file mode 100644 index 0000000..7cf561f --- /dev/null +++ b/images/1.0/alpine/hooks/README.md @@ -0,0 +1,3 @@ +# DockerHub automated tests hooks + +See documentation for details: diff --git a/images/1.0/alpine/hooks/build b/images/1.0/alpine/hooks/build old mode 100644 new mode 100755 index 9c56e86..98efa93 --- a/images/1.0/alpine/hooks/build +++ b/images/1.0/alpine/hooks/build @@ -4,8 +4,8 @@ # https://docs.docker.com/docker-hub/builds/advanced/ docker build \ - --build-arg TAG="${DOCKER_TAG}" \ - --build-arg VCS_REF=`git rev-parse --short HEAD` \ - --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + --build-arg VERSION="${DOCKER_TAG}" \ + --build-arg "VCS_REF=$(git rev-parse --short HEAD)" \ + --build-arg "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ -f "$DOCKERFILE_PATH" \ -t "$IMAGE_NAME" . diff --git a/images/1.0/alpine/hooks/post_push b/images/1.0/alpine/hooks/post_push new file mode 100755 index 0000000..2452e1b --- /dev/null +++ b/images/1.0/alpine/hooks/post_push @@ -0,0 +1,13 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +if [ -f ./.dockertags ]; then + for tag in $(cat ./.dockertags); do + if [ ! "${tag}" = "${DOCKER_TAG}" ]; then + docker tag "$IMAGE_NAME" "$DOCKER_REPO:$tag" + docker push "$DOCKER_REPO:$tag" + fi + done +fi diff --git a/images/1.0/alpine/hooks/post_test b/images/1.0/alpine/hooks/post_test new file mode 100755 index 0000000..de60acb --- /dev/null +++ b/images/1.0/alpine/hooks/post_test @@ -0,0 +1,7 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Removing persisted data...' +#rm -rf "/srv/__app_slug__/data" diff --git a/images/1.0/alpine/hooks/pre_test b/images/1.0/alpine/hooks/pre_test new file mode 100755 index 0000000..78c5041 --- /dev/null +++ b/images/1.0/alpine/hooks/pre_test @@ -0,0 +1,8 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Preparing persisted data...' +#mkdir -p "/srv/__app_slug__/data" +#chown 777 "/srv/__app_slug__/data" diff --git a/images/1.0/alpine/hooks/run b/images/1.0/alpine/hooks/run new file mode 100755 index 0000000..6e51000 --- /dev/null +++ b/images/1.0/alpine/hooks/run @@ -0,0 +1,30 @@ +#!/bin/bash + +# Custom script to run locally DockerHub hooks +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +echo "Custom script to run locally DockerHub hooks..." +set -e + +export SOURCE_BRANCH +SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD) +export SOURCE_COMMIT +SOURCE_COMMIT=$(git rev-parse --short HEAD) +export COMMIT_MSG +COMMIT_MSG=$(git log -1 --format=%s) + +export DOCKER_REPO=monogramm/docker-__app_slug__ +export DOCKERFILE_PATH=Dockerfile +export DOCKER_TAG=1.0 +export IMAGE_NAME=${DOCKER_REPO}:${DOCKER_TAG} + +# Execute hooks in following order +for hook in pre_build build post_build pre_test test post_test; do + if [ -f "./hooks/${hook}" ]; then + echo "Executing ${hook} hook..." + "./hooks/${hook}" + fi +done + +echo "DockerHub hooks finished" diff --git a/images/1.0/alpine/hooks/test b/images/1.0/alpine/hooks/test new file mode 100755 index 0000000..f362dfc --- /dev/null +++ b/images/1.0/alpine/hooks/test @@ -0,0 +1,30 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +for file in docker-compose*.test.yml; do + + echo "Starting Test in ${file}..." + + # Build test container + docker-compose --no-ansi -f "${file}" build sut + # Test container run + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down + + # Test container restart + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down -v + +done diff --git a/images/1.0/alpine/test/Dockerfile b/images/1.0/alpine/test/Dockerfile index d390a46..c50e369 100644 --- a/images/1.0/alpine/test/Dockerfile +++ b/images/1.0/alpine/test/Dockerfile @@ -1,9 +1,13 @@ -FROM alpine:3 +FROM alpine:3.12 COPY docker_test.sh /docker_test.sh RUN set -ex; \ - echo '[TODO] PREPARE TEST OPERATIONS'; \ - chmod 755 /docker_test.sh; + chmod 755 /docker_test.sh; \ + apk add --update \ + curl \ + ; \ + \ + rm -rf /var/cache/apk/* CMD ["/docker_test.sh"] diff --git a/images/1.0/alpine/test/docker_test.sh b/images/1.0/alpine/test/docker_test.sh index 169a0d1..379eebf 100644 --- a/images/1.0/alpine/test/docker_test.sh +++ b/images/1.0/alpine/test/docker_test.sh @@ -2,31 +2,38 @@ set -e +################################################################################ +# Testing docker containers + echo "Waiting to ensure everything is fully ready for the tests..." sleep 60 echo "Checking main containers are reachable..." -if ! ping -c 10 -q __app_slug__-db ; then - echo '__app_name__ Database container is not responding!' +if ! ping -c 10 -q "${DOCKER_TEST_CONTAINER}" ; then + echo 'Main container is not responding!' # TODO Display logs to help bug fixing #echo 'Check the following logs for details:' #tail -n 100 logs/*.log exit 2 fi -if ! ping -c 10 -q __app_slug__ ; then - echo '__app_name__ Main container is not responding!' - # TODO Display logs to help bug fixing - #echo 'Check the following logs for details:' - #tail -n 100 logs/*.log - exit 4 -fi -# XXX Add your own tests +################################################################################ +# Success +echo 'Docker tests successful' + + +################################################################################ +# Automated Unit tests # https://docs.docker.com/docker-hub/builds/automated-testing/ -#echo "Executing __app_name__ app tests..." -## TODO Test result of tests +################################################################################ +# TODO Check API is responding +# curl "http://${DOCKER_WEB_CONTAINER:${DOCKER_TEST_CONTAINER}}:80" + + +################################################################################ # Success -echo 'Docker tests successful' +echo "Docker app '${DOCKER_TEST_CONTAINER}' tests finished" +echo 'Check the CI reports and logs for details.' exit 0 diff --git a/images/1.0/debian/.dockerignore b/images/1.0/debian/.dockerignore index 71ec7b1..8ed1049 100644 --- a/images/1.0/debian/.dockerignore +++ b/images/1.0/debian/.dockerignore @@ -1,2 +1,3 @@ # Ignore CI docker-compose docker-compose.yml +.dockertags diff --git a/images/1.0/debian/.dockertags b/images/1.0/debian/.dockertags new file mode 100644 index 0000000..7ae5cb7 --- /dev/null +++ b/images/1.0/debian/.dockertags @@ -0,0 +1 @@ +1.0.0-debian 1.0-debian debian diff --git a/images/1.0/debian/Dockerfile b/images/1.0/debian/Dockerfile index c0a81ad..e1f7e80 100644 --- a/images/1.0/debian/Dockerfile +++ b/images/1.0/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM debian +FROM debian:buster # If based on official images # FROM __app_owner_slug__/__app_slug__:debian @@ -16,6 +16,11 @@ ARG VCS_REF ARG BUILD_DATE ARG VERSION=1.0.0 +# Keep track of image version +RUN set -ex; \ + mkdir -p '/app/src'; \ + echo "${VERSION} ${VCS_REF} ${BUILD_DATE}" > '/app/src/.docker-version' + # Container labels (http://label-schema.org/) # Container annotations (https://github.com/opencontainers/image-spec) LABEL maintainer="Monogramm maintainers " \ diff --git a/images/1.0/debian/docker-compose.test.yml b/images/1.0/debian/docker-compose.test.yml index 7ee9569..6fb4959 100644 --- a/images/1.0/debian/docker-compose.test.yml +++ b/images/1.0/debian/docker-compose.test.yml @@ -1,8 +1,12 @@ version: "2.3" +volumes: + __app_slug___data: + __app_slug___db: + networks: - __app_uppercase_slug___external_network: - __app_uppercase_slug___internal_network: + __app_slug___external_network: + __app_slug___internal_network: internal: true services: @@ -17,19 +21,23 @@ services: __app_slug__: condition: service_started networks: - - __app_uppercase_slug___internal_network + - __app_slug___internal_network volumes_from: - __app_slug__ - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + # If you want, you can sync time & timezone with host + #volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + environment: + - DOCKER_TEST_CONTAINER=__app_slug__ + - DOCKER_WEB_CONTAINER=__app_slug__ __app_slug__: # For development or CI, tag build from local Dockerfile build: context: ./ dockerfile: Dockerfile - args: + args: - TAG=${TAG} - VCS_REF=${VCS_REF} - BUILD_DATE=${BUILD_DATE} @@ -43,16 +51,17 @@ services: links: - __app_slug__-db expose: - - '80' + - "80" ports: - - "80:80" + - "8080:80" networks: - - __app_uppercase_slug___internal_network - - __app_uppercase_slug___external_network + - __app_slug___internal_network + - __app_slug___external_network volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - __app_uppercase_slug___DB_TYPE=mysql - __app_uppercase_slug___DB_HOST=$__app_slug__-db @@ -66,19 +75,28 @@ services: restart: always command: --character_set_client=utf8 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --character-set-client-handshake=FALSE healthcheck: - test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "--password=${$__app_uppercase_slug___DB_ROOT_PASSWD}"] + test: + [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "--password=${$__app_uppercase_slug___DB_ROOT_PASSWD}", + ] timeout: 10s retries: 5 expose: - - '3306' + - "3306" #ports: # - "3306:3306" networks: - - __app_uppercase_slug___internal_network + - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/db:/var/lib/mysql - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___db:/var/lib/mysql + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - MYSQL_ROOT_PASSWORD=$__app_uppercase_slug___DB_ROOT_PASSWD - MYSQL_DATABASE=$__app_uppercase_slug___DB_NAME diff --git a/images/1.0/debian/entrypoint.sh b/images/1.0/debian/entrypoint.sh index 6316e07..1db8fe5 100755 --- a/images/1.0/debian/entrypoint.sh +++ b/images/1.0/debian/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh ## ## Docker image for __app_name__. -## Copyright (C) 2020 Monogramm +## Copyright (C) 2021 Monogramm ## set -e @@ -9,7 +9,7 @@ set -e # Functions log() { - echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $@" + echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $*" } # wait for file/directory to exists @@ -24,7 +24,7 @@ wait_for_file() { WAIT_STEP=${2:-10} WAIT_TIMEOUT=${3:--1} - while [ ! -d "${WAIT_FOR_FILE}" ] ; do + while [ ! -d "${WAIT_FOR_FILE}" ]; do if [ "${WAIT_TIMEOUT}" -gt 0 ] && [ "${WAIT_TIME}" -gt "${WAIT_TIMEOUT}" ]; then log "File '${WAIT_FOR_FILE}' was not available on time!" exit 1 @@ -32,7 +32,7 @@ wait_for_file() { log "Waiting file '${WAIT_FOR_FILE}'..." sleep "${WAIT_STEP}" - WAIT_TIME=$(( WAIT_TIME + WAIT_STEP )) + WAIT_TIME=$((WAIT_TIME + WAIT_STEP)) done log "File '${WAIT_FOR_FILE}' exists." } @@ -67,7 +67,7 @@ wait_for_service() { WAIT_STEP=${3:-10} WAIT_TIMEOUT=${4:--1} - while ! nc -z "${WAIT_FOR_ADDR}" "${WAIT_FOR_PORT}" ; do + while ! nc -z "${WAIT_FOR_ADDR}" "${WAIT_FOR_PORT}"; do if [ "${WAIT_TIMEOUT}" -gt 0 ] && [ "${WAIT_TIME}" -gt "${WAIT_TIMEOUT}" ]; then log "Service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}' was not available on time!" exit 1 @@ -75,13 +75,13 @@ wait_for_service() { log "Waiting service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}'..." sleep "${WAIT_STEP}" - WAIT_TIME=$(( WAIT_TIME + WAIT_STEP )) + WAIT_TIME=$((WAIT_TIME + WAIT_STEP)) done log "Service '${WAIT_FOR_ADDR}:${WAIT_FOR_PORT}' available." } wait_for_services() { - if [ -z "${WAIT_FOR_SERVICES}" ]; then + if [ -z "${WAIT_FOR_SERVICES:-$1}" ]; then log "Missing env var 'WAIT_FOR_SERVICES' defining services to wait for!" exit 1 fi @@ -95,6 +95,25 @@ wait_for_services() { } +# init / update application +init() { + # Check version + if [ ! -f "./.docker-version" ]; then + log "[TODO] __app_name__ init to $(cat /app/src/.docker-version)..." + elif ! cmp --silent "./.docker-version" "/app/src/.docker-version"; then + log "[TODO] __app_name__ update from $(cat ./.docker-version) to $(cat /app/src/.docker-version)..." + fi + + cp -p "/app/src/.docker-version" "./.docker-version" +} + +# start application +start() { + init + + echo "[TODO] Start main service" +} + # display help print_help() { echo "Monogramm Docker entrypoint for __app_name__. @@ -114,9 +133,9 @@ Options: # Execute task based on command case "${1}" in - # Management tasks - ("--help") print_help ;; - # Service tasks - ("start") echo "TODO Start main service" ;; - (*) exec "$@" ;; +# Management tasks +-h|--help) print_help ;; +# Service tasks +start) start ;; +*) exec "$@" ;; esac diff --git a/images/1.0/debian/hooks/README.md b/images/1.0/debian/hooks/README.md new file mode 100644 index 0000000..7cf561f --- /dev/null +++ b/images/1.0/debian/hooks/README.md @@ -0,0 +1,3 @@ +# DockerHub automated tests hooks + +See documentation for details: diff --git a/images/1.0/debian/hooks/build b/images/1.0/debian/hooks/build old mode 100644 new mode 100755 index 9c56e86..98efa93 --- a/images/1.0/debian/hooks/build +++ b/images/1.0/debian/hooks/build @@ -4,8 +4,8 @@ # https://docs.docker.com/docker-hub/builds/advanced/ docker build \ - --build-arg TAG="${DOCKER_TAG}" \ - --build-arg VCS_REF=`git rev-parse --short HEAD` \ - --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + --build-arg VERSION="${DOCKER_TAG}" \ + --build-arg "VCS_REF=$(git rev-parse --short HEAD)" \ + --build-arg "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ -f "$DOCKERFILE_PATH" \ -t "$IMAGE_NAME" . diff --git a/images/1.0/debian/hooks/post_push b/images/1.0/debian/hooks/post_push new file mode 100755 index 0000000..2452e1b --- /dev/null +++ b/images/1.0/debian/hooks/post_push @@ -0,0 +1,13 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +if [ -f ./.dockertags ]; then + for tag in $(cat ./.dockertags); do + if [ ! "${tag}" = "${DOCKER_TAG}" ]; then + docker tag "$IMAGE_NAME" "$DOCKER_REPO:$tag" + docker push "$DOCKER_REPO:$tag" + fi + done +fi diff --git a/images/1.0/debian/hooks/post_test b/images/1.0/debian/hooks/post_test new file mode 100755 index 0000000..de60acb --- /dev/null +++ b/images/1.0/debian/hooks/post_test @@ -0,0 +1,7 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Removing persisted data...' +#rm -rf "/srv/__app_slug__/data" diff --git a/images/1.0/debian/hooks/pre_test b/images/1.0/debian/hooks/pre_test new file mode 100755 index 0000000..78c5041 --- /dev/null +++ b/images/1.0/debian/hooks/pre_test @@ -0,0 +1,8 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Preparing persisted data...' +#mkdir -p "/srv/__app_slug__/data" +#chown 777 "/srv/__app_slug__/data" diff --git a/images/1.0/debian/hooks/run b/images/1.0/debian/hooks/run new file mode 100755 index 0000000..6e51000 --- /dev/null +++ b/images/1.0/debian/hooks/run @@ -0,0 +1,30 @@ +#!/bin/bash + +# Custom script to run locally DockerHub hooks +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +echo "Custom script to run locally DockerHub hooks..." +set -e + +export SOURCE_BRANCH +SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD) +export SOURCE_COMMIT +SOURCE_COMMIT=$(git rev-parse --short HEAD) +export COMMIT_MSG +COMMIT_MSG=$(git log -1 --format=%s) + +export DOCKER_REPO=monogramm/docker-__app_slug__ +export DOCKERFILE_PATH=Dockerfile +export DOCKER_TAG=1.0 +export IMAGE_NAME=${DOCKER_REPO}:${DOCKER_TAG} + +# Execute hooks in following order +for hook in pre_build build post_build pre_test test post_test; do + if [ -f "./hooks/${hook}" ]; then + echo "Executing ${hook} hook..." + "./hooks/${hook}" + fi +done + +echo "DockerHub hooks finished" diff --git a/images/1.0/debian/hooks/test b/images/1.0/debian/hooks/test new file mode 100755 index 0000000..f362dfc --- /dev/null +++ b/images/1.0/debian/hooks/test @@ -0,0 +1,30 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +for file in docker-compose*.test.yml; do + + echo "Starting Test in ${file}..." + + # Build test container + docker-compose --no-ansi -f "${file}" build sut + # Test container run + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down + + # Test container restart + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down -v + +done diff --git a/images/1.0/debian/test/Dockerfile b/images/1.0/debian/test/Dockerfile index d390a46..c50e369 100644 --- a/images/1.0/debian/test/Dockerfile +++ b/images/1.0/debian/test/Dockerfile @@ -1,9 +1,13 @@ -FROM alpine:3 +FROM alpine:3.12 COPY docker_test.sh /docker_test.sh RUN set -ex; \ - echo '[TODO] PREPARE TEST OPERATIONS'; \ - chmod 755 /docker_test.sh; + chmod 755 /docker_test.sh; \ + apk add --update \ + curl \ + ; \ + \ + rm -rf /var/cache/apk/* CMD ["/docker_test.sh"] diff --git a/images/1.0/debian/test/docker_test.sh b/images/1.0/debian/test/docker_test.sh index 169a0d1..379eebf 100644 --- a/images/1.0/debian/test/docker_test.sh +++ b/images/1.0/debian/test/docker_test.sh @@ -2,31 +2,38 @@ set -e +################################################################################ +# Testing docker containers + echo "Waiting to ensure everything is fully ready for the tests..." sleep 60 echo "Checking main containers are reachable..." -if ! ping -c 10 -q __app_slug__-db ; then - echo '__app_name__ Database container is not responding!' +if ! ping -c 10 -q "${DOCKER_TEST_CONTAINER}" ; then + echo 'Main container is not responding!' # TODO Display logs to help bug fixing #echo 'Check the following logs for details:' #tail -n 100 logs/*.log exit 2 fi -if ! ping -c 10 -q __app_slug__ ; then - echo '__app_name__ Main container is not responding!' - # TODO Display logs to help bug fixing - #echo 'Check the following logs for details:' - #tail -n 100 logs/*.log - exit 4 -fi -# XXX Add your own tests +################################################################################ +# Success +echo 'Docker tests successful' + + +################################################################################ +# Automated Unit tests # https://docs.docker.com/docker-hub/builds/automated-testing/ -#echo "Executing __app_name__ app tests..." -## TODO Test result of tests +################################################################################ +# TODO Check API is responding +# curl "http://${DOCKER_WEB_CONTAINER:${DOCKER_TEST_CONTAINER}}:80" + + +################################################################################ # Success -echo 'Docker tests successful' +echo "Docker app '${DOCKER_TEST_CONTAINER}' tests finished" +echo 'Check the CI reports and logs for details.' exit 0 diff --git a/template/.dockerignore b/template/.dockerignore index 71ec7b1..8ed1049 100644 --- a/template/.dockerignore +++ b/template/.dockerignore @@ -1,2 +1,3 @@ # Ignore CI docker-compose docker-compose.yml +.dockertags diff --git a/template/Dockerfile.alpine b/template/Dockerfile.alpine index f7dfffa..f622436 100644 --- a/template/Dockerfile.alpine +++ b/template/Dockerfile.alpine @@ -1,4 +1,4 @@ -FROM alpine +FROM alpine:3.12 # If based on official images # FROM __app_owner_slug__/__app_slug__:alpine diff --git a/template/Dockerfile.debian b/template/Dockerfile.debian index 34f4f90..f2daa09 100644 --- a/template/Dockerfile.debian +++ b/template/Dockerfile.debian @@ -1,4 +1,4 @@ -FROM debian +FROM debian:buster # If based on official images # FROM __app_owner_slug__/__app_slug__:debian diff --git a/template/docker-compose_alpine.yml b/template/docker-compose_alpine.yml index fb4642c..d62c3a7 100644 --- a/template/docker-compose_alpine.yml +++ b/template/docker-compose_alpine.yml @@ -1,5 +1,10 @@ version: "2.3" +volumes: + __app_slug___data: + __app_slug___db: + __app_slug___web: + networks: __app_slug___external_network: __app_slug___internal_network: @@ -22,16 +27,20 @@ services: - __app_slug___internal_network volumes_from: - __app_slug__ - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + # If you want, you can sync time & timezone with host + #volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + environment: + - DOCKER_TEST_CONTAINER=__app_slug__ + - DOCKER_WEB_CONTAINER=__app_slug__-nginx __app_slug__: # For development or CI, tag build from local Dockerfile build: context: ./ dockerfile: Dockerfile - args: + args: - TAG=${TAG} - VCS_REF=${VCS_REF} - BUILD_DATE=${BUILD_DATE} @@ -45,15 +54,16 @@ services: links: - __app_slug__-db expose: - - '9000' - #ports: - # - "9000:9000" + - "9000" + ports: + - "9090:9000" networks: - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - __app_uppercase_slug___DB_TYPE=pgsql - __app_uppercase_slug___DB_HOST=__app_slug__-db @@ -65,16 +75,29 @@ services: image: postgres:latest container_name: __app_slug__-db restart: always + healthcheck: + test: + [ + "CMD", + "pg_isready", + "-U", + "${__app_uppercase_slug___DB_USER}", + "-d", + "${__app_uppercase_slug___DB_NAME}", + ] + interval: 60s + retries: 5 + expose: + - "5432" #ports: # - "5432:5432" - healthcheck: - test: ["CMD", "pg_isready", "-U", "${__app_uppercase_slug___DB_USER}", "-d", "${__app_uppercase_slug___DB_NAME}"] networks: - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/db:/var/lib/postgresql/data - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___db:/var/lib/postgresql/data + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - POSTGRES_DB=$__app_uppercase_slug___DB_NAME - POSTGRES_USER=$__app_uppercase_slug___DB_USER @@ -85,9 +108,9 @@ services: container_name: __app_slug__-nginx restart: always ports: - - "80:80" + - "8080:80" # If you need SSL connection - # - "443:443" + # - "8443:443" networks: - __app_slug___internal_network - __app_slug___external_network @@ -97,9 +120,10 @@ services: links: - __app_slug__ volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro # TODO Provide a custom nginx conf #- ./nginx.conf:/etc/nginx/nginx.conf:ro # If you need SSL connection, you can provide your own certificates @@ -107,4 +131,4 @@ services: # - ./certs-data:/data/letsencrypt environment: - NGINX_HOST=localhost # set your local domain or your live domain - # - NGINX_CGI=__app_slug__:9000 # same as __app_slug__ container name + #- NGINX_CGI=__app_slug__:9000 # same as __app_slug__ container name diff --git a/template/docker-compose_debian.yml b/template/docker-compose_debian.yml index 887ed71..6fb4959 100644 --- a/template/docker-compose_debian.yml +++ b/template/docker-compose_debian.yml @@ -1,5 +1,9 @@ version: "2.3" +volumes: + __app_slug___data: + __app_slug___db: + networks: __app_slug___external_network: __app_slug___internal_network: @@ -20,16 +24,20 @@ services: - __app_slug___internal_network volumes_from: - __app_slug__ - volumes: - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + # If you want, you can sync time & timezone with host + #volumes: + # - /etc/localtime:/etc/localtime:ro + # - /etc/timezone:/etc/timezone:ro + environment: + - DOCKER_TEST_CONTAINER=__app_slug__ + - DOCKER_WEB_CONTAINER=__app_slug__ __app_slug__: # For development or CI, tag build from local Dockerfile build: context: ./ dockerfile: Dockerfile - args: + args: - TAG=${TAG} - VCS_REF=${VCS_REF} - BUILD_DATE=${BUILD_DATE} @@ -43,16 +51,17 @@ services: links: - __app_slug__-db expose: - - '80' + - "80" ports: - - "80:80" + - "8080:80" networks: - __app_slug___internal_network - __app_slug___external_network volumes: - - $__app_uppercase_slug___HOME/html:/var/www/html - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___data:/var/www/html + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - __app_uppercase_slug___DB_TYPE=mysql - __app_uppercase_slug___DB_HOST=$__app_slug__-db @@ -66,19 +75,28 @@ services: restart: always command: --character_set_client=utf8 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --character-set-client-handshake=FALSE healthcheck: - test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "--password=${$__app_uppercase_slug___DB_ROOT_PASSWD}"] + test: + [ + "CMD", + "mysqladmin", + "ping", + "-h", + "localhost", + "--password=${$__app_uppercase_slug___DB_ROOT_PASSWD}", + ] timeout: 10s retries: 5 expose: - - '3306' + - "3306" #ports: # - "3306:3306" networks: - __app_slug___internal_network volumes: - - $__app_uppercase_slug___HOME/db:/var/lib/mysql - - /etc/localtime:/etc/localtime:ro - - /etc/timezone:/etc/timezone:ro + - __app_slug___db:/var/lib/mysql + # If you want, you can sync time & timezone with host + #- /etc/localtime:/etc/localtime:ro + #- /etc/timezone:/etc/timezone:ro environment: - MYSQL_ROOT_PASSWORD=$__app_uppercase_slug___DB_ROOT_PASSWD - MYSQL_DATABASE=$__app_uppercase_slug___DB_NAME diff --git a/template/entrypoint.sh b/template/entrypoint.sh index aff2820..1db8fe5 100755 --- a/template/entrypoint.sh +++ b/template/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh ## ## Docker image for __app_name__. -## Copyright (C) 2020 Monogramm +## Copyright (C) 2021 Monogramm ## set -e @@ -9,7 +9,7 @@ set -e # Functions log() { - echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $@" + echo "[$0] [$(date +%Y-%m-%dT%H:%M:%S%:z)] $*" } # wait for file/directory to exists @@ -134,8 +134,8 @@ Options: # Execute task based on command case "${1}" in # Management tasks -"--help") print_help ;; - # Service tasks -"start") start ;; +-h|--help) print_help ;; +# Service tasks +start) start ;; *) exec "$@" ;; esac diff --git a/template/hooks/README.md b/template/hooks/README.md index a451cf7..7cf561f 100644 --- a/template/hooks/README.md +++ b/template/hooks/README.md @@ -1,4 +1,3 @@ # DockerHub automated tests hooks See documentation for details: - diff --git a/template/hooks/build b/template/hooks/build old mode 100644 new mode 100755 index 9c56e86..98efa93 --- a/template/hooks/build +++ b/template/hooks/build @@ -4,8 +4,8 @@ # https://docs.docker.com/docker-hub/builds/advanced/ docker build \ - --build-arg TAG="${DOCKER_TAG}" \ - --build-arg VCS_REF=`git rev-parse --short HEAD` \ - --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ + --build-arg VERSION="${DOCKER_TAG}" \ + --build-arg "VCS_REF=$(git rev-parse --short HEAD)" \ + --build-arg "BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ -f "$DOCKERFILE_PATH" \ -t "$IMAGE_NAME" . diff --git a/template/hooks/post_push b/template/hooks/post_push new file mode 100755 index 0000000..2452e1b --- /dev/null +++ b/template/hooks/post_push @@ -0,0 +1,13 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +if [ -f ./.dockertags ]; then + for tag in $(cat ./.dockertags); do + if [ ! "${tag}" = "${DOCKER_TAG}" ]; then + docker tag "$IMAGE_NAME" "$DOCKER_REPO:$tag" + docker push "$DOCKER_REPO:$tag" + fi + done +fi diff --git a/template/hooks/post_test b/template/hooks/post_test new file mode 100755 index 0000000..de60acb --- /dev/null +++ b/template/hooks/post_test @@ -0,0 +1,7 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Removing persisted data...' +#rm -rf "/srv/__app_slug__/data" diff --git a/template/hooks/pre_test b/template/hooks/pre_test new file mode 100755 index 0000000..78c5041 --- /dev/null +++ b/template/hooks/pre_test @@ -0,0 +1,8 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +#echo 'Preparing persisted data...' +#mkdir -p "/srv/__app_slug__/data" +#chown 777 "/srv/__app_slug__/data" diff --git a/template/hooks/run b/template/hooks/run new file mode 100755 index 0000000..ae90e89 --- /dev/null +++ b/template/hooks/run @@ -0,0 +1,30 @@ +#!/bin/bash + +# Custom script to run locally DockerHub hooks +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +echo "Custom script to run locally DockerHub hooks..." +set -e + +export SOURCE_BRANCH +SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD) +export SOURCE_COMMIT +SOURCE_COMMIT=$(git rev-parse --short HEAD) +export COMMIT_MSG +COMMIT_MSG=$(git log -1 --format=%s) + +export DOCKER_REPO= +export DOCKERFILE_PATH=Dockerfile +export DOCKER_TAG= +export IMAGE_NAME=${DOCKER_REPO}:${DOCKER_TAG} + +# Execute hooks in following order +for hook in pre_build build post_build pre_test test post_test; do + if [ -f "./hooks/${hook}" ]; then + echo "Executing ${hook} hook..." + "./hooks/${hook}" + fi +done + +echo "DockerHub hooks finished" diff --git a/template/hooks/test b/template/hooks/test new file mode 100755 index 0000000..f362dfc --- /dev/null +++ b/template/hooks/test @@ -0,0 +1,30 @@ +#!/bin/bash + +# See documentation for details: +# https://docs.docker.com/docker-hub/builds/advanced/ + +for file in docker-compose*.test.yml; do + + echo "Starting Test in ${file}..." + + # Build test container + docker-compose --no-ansi -f "${file}" build sut + # Test container run + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down + + # Test container restart + docker-compose --no-ansi -f "${file}" up -d && sleep 60 + docker-compose --no-ansi -f "${file}" logs -f "sut" + docker-compose --no-ansi -f "${file}" ps + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-db" + docker-compose --no-ansi -f "${file}" logs --no-color "penpot-backend" + docker-compose --no-ansi -f "${file}" ps "sut" | grep "Exit 0" || exit 1 + docker-compose --no-ansi -f "${file}" down -v + +done diff --git a/template/test/Dockerfile b/template/test/Dockerfile index d390a46..c50e369 100644 --- a/template/test/Dockerfile +++ b/template/test/Dockerfile @@ -1,9 +1,13 @@ -FROM alpine:3 +FROM alpine:3.12 COPY docker_test.sh /docker_test.sh RUN set -ex; \ - echo '[TODO] PREPARE TEST OPERATIONS'; \ - chmod 755 /docker_test.sh; + chmod 755 /docker_test.sh; \ + apk add --update \ + curl \ + ; \ + \ + rm -rf /var/cache/apk/* CMD ["/docker_test.sh"] diff --git a/template/test/docker_test.sh b/template/test/docker_test.sh index 169a0d1..379eebf 100644 --- a/template/test/docker_test.sh +++ b/template/test/docker_test.sh @@ -2,31 +2,38 @@ set -e +################################################################################ +# Testing docker containers + echo "Waiting to ensure everything is fully ready for the tests..." sleep 60 echo "Checking main containers are reachable..." -if ! ping -c 10 -q __app_slug__-db ; then - echo '__app_name__ Database container is not responding!' +if ! ping -c 10 -q "${DOCKER_TEST_CONTAINER}" ; then + echo 'Main container is not responding!' # TODO Display logs to help bug fixing #echo 'Check the following logs for details:' #tail -n 100 logs/*.log exit 2 fi -if ! ping -c 10 -q __app_slug__ ; then - echo '__app_name__ Main container is not responding!' - # TODO Display logs to help bug fixing - #echo 'Check the following logs for details:' - #tail -n 100 logs/*.log - exit 4 -fi -# XXX Add your own tests +################################################################################ +# Success +echo 'Docker tests successful' + + +################################################################################ +# Automated Unit tests # https://docs.docker.com/docker-hub/builds/automated-testing/ -#echo "Executing __app_name__ app tests..." -## TODO Test result of tests +################################################################################ +# TODO Check API is responding +# curl "http://${DOCKER_WEB_CONTAINER:${DOCKER_TEST_CONTAINER}}:80" + + +################################################################################ # Success -echo 'Docker tests successful' +echo "Docker app '${DOCKER_TEST_CONTAINER}' tests finished" +echo 'Check the CI reports and logs for details.' exit 0 diff --git a/update.sh b/update.sh index d43312d..ce52aea 100755 --- a/update.sh +++ b/update.sh @@ -11,12 +11,19 @@ declare -A base=( [alpine]='alpine' ) +declare -A dockerVariant=( + [debian]='debian' + [alpine]='alpine' +) + variants=( debian alpine ) min_version='1.0' +dockerLatest='1.0' +dockerDefaultVariant='alpine' # version_greater_or_equal A B returns whether A >= B @@ -33,10 +40,11 @@ latests=( 1.0.0 ) # Remove existing images echo "reset docker images" -find ./images -maxdepth 1 -type d -regextype sed -regex '\./images/[[:digit:]]\+\.[[:digit:]]\+' -exec rm -r '{}' \; -#rm -rf ./images/* +rm -rf ./images/ +mkdir ./images/ echo "update docker images" +readmeTags= travisEnv= for latest in "${latests[@]}"; do version=$(echo "$latest" | cut -d. -f1-2) @@ -69,6 +77,37 @@ for latest in "${latests[@]}"; do s/%%VERSION%%/'"$latest"'/g; ' "$dir/Dockerfile" + sed -ri -e ' + s|DOCKER_TAG=.*|DOCKER_TAG='"$version"'|g; + s|DOCKER_REPO=.*|DOCKER_REPO='"$dockerRepo"'|g; + ' "$dir/hooks/run" + + # Create a list of "alias" tags for DockerHub post_push + tagVariant=${dockerVariant[$variant]} + if [ "$version" = "$dockerLatest" ]; then + if [ "$tagVariant" = "$dockerDefaultVariant" ]; then + export DOCKER_TAGS="$latest-$tagVariant $version-$tagVariant $tagVariant $latest $version latest " + else + export DOCKER_TAGS="$latest-$tagVariant $version-$tagVariant $tagVariant " + fi + elif [ "$version" = "$latest" ]; then + if [ "$tagVariant" = "$dockerDefaultVariant" ]; then + export DOCKER_TAGS="$latest-$tagVariant $latest " + else + export DOCKER_TAGS="$latest-$tagVariant " + fi + else + if [ "$tagVariant" = "$dockerDefaultVariant" ]; then + export DOCKER_TAGS="$latest-$tagVariant $version-$tagVariant $latest $version " + else + export DOCKER_TAGS="$latest-$tagVariant $version-$tagVariant " + fi + fi + echo "${DOCKER_TAGS} " > "$dir/.dockertags" + + # Add README tags + readmeTags="$readmeTags\n- ${DOCKER_TAGS} (\`$dir/Dockerfile\`)" + # Add Travis-CI env var travisEnv='\n - VERSION='"$version"' VARIANT='"$variant$travisEnv" @@ -82,6 +121,11 @@ for latest in "${latests[@]}"; do done +# update README.md +sed '/^/,/^/{/^/!{/^/!d}}' README.md > README.md.tmp +sed -e "s||\n$readmeTags\n|g" README.md.tmp > README.md +rm README.md.tmp + # update .travis.yml travis="$(awk -v 'RS=\n\n' '$1 == "env:" && $2 == "#" && $3 == "Environments" { $0 = "env: # Environments'"$travisEnv"'" } { printf "%s%s", $0, RS }' .travis.yml)" echo "$travis" > .travis.yml