diff --git a/.bmeme/build/bin/app b/.bmeme/build/bin/app new file mode 100755 index 0000000..bb5556e --- /dev/null +++ b/.bmeme/build/bin/app @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../common/lib.sh" + +for var in "$@" +do + PARAMS="$PARAMS ${var}" +done + +if [[ -t 1 ]] ; then OPTS=""; else OPTS="-T"; fi + +docker-compose ps ${CONTAINER} | grep -q Up | true +ret=${PIPESTATUS[1]} +if [ $ret -eq 1 ]; then + echo -e "${CR}Error${CN} - It seems that the ${CONTAINER} container is not running" + exit 1 +fi + +docker-compose exec -u $(id -u):$(id -g) ${OPTS} ${CONTAINER} /bin/bash -c "${PARAMS}" diff --git a/.bmeme/build/bin/c b/.bmeme/build/bin/c new file mode 100755 index 0000000..5cfda7a --- /dev/null +++ b/.bmeme/build/bin/c @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -e +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../common/lib.sh" + +PWD=$(pwd) +while [[ "$PWD" != "" && ! -e "$PWD/.bmeme" ]]; do + PWD=${PWD%/*} +done + +FOUND=$PWD + +if [[ "${FOUND}" = "" ]]; then + echo -e "${CR}Error${CN} - Unable to find parent bmeme project!" + exit -1 +fi + +ORIG_DIR=$(pwd) +REL_DIR=${ORIG_DIR#"$FOUND"} + +set -e +QUOTEDCMD=() +for token in "$@"; do + QUOTEDCMD+=($(printf "%q" "$token")) +done +CMD="${QUOTEDCMD[*]}" + +pushd ${FOUND} > /dev/null +${APP_BIN} cd ".${REL_DIR}" \; ${CMD} +popd > /dev/null diff --git a/.bmeme/build/commands/docker/create.sh b/.bmeme/build/commands/docker/create.sh new file mode 100755 index 0000000..5eb5ab9 --- /dev/null +++ b/.bmeme/build/commands/docker/create.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Create and start docker environment" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Creating${CN} docker environment ..." + echo "${LOGPREFIX} CMD docker-compose build --pull --build-arg FIX_UID=\"\$(id -u)\" --build-arg FIX_GID=\"\$(id -g)\"" >> ${LOGFILE} + docker-compose build --pull --build-arg FIX_UID="$(id -u)" --build-arg FIX_GID="$(id -g)" + echo "" + echo -e "============ ${CB}Starting${CN} docker environment ..." + echo "${LOGPREFIX} CMD docker-compose up -d" >> ${LOGFILE} + docker-compose up -d +fi diff --git a/.bmeme/build/commands/docker/destroy.sh b/.bmeme/build/commands/docker/destroy.sh new file mode 100755 index 0000000..4a84be7 --- /dev/null +++ b/.bmeme/build/commands/docker/destroy.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Clean docker environment" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Cleaning${CN} docker environment ..." + docker-compose down -v +fi diff --git a/.bmeme/build/commands/docker/logs.sh b/.bmeme/build/commands/docker/logs.sh new file mode 100755 index 0000000..e443f73 --- /dev/null +++ b/.bmeme/build/commands/docker/logs.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Show docker container logs" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Showing${CN} docker container logs ..." + docker-compose logs -f +fi diff --git a/.bmeme/build/commands/docker/shell.sh b/.bmeme/build/commands/docker/shell.sh new file mode 100755 index 0000000..5bf1c61 --- /dev/null +++ b/.bmeme/build/commands/docker/shell.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Run a shell inside the docker container" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Running${CN} a shell inside the docker container ..." + echo "${LOGPREFIX} CMD ${C_BIN} bash" >> ${LOGFILE} + ${C_BIN} bash +fi diff --git a/.bmeme/build/commands/docker/start.sh b/.bmeme/build/commands/docker/start.sh new file mode 100755 index 0000000..2283e1f --- /dev/null +++ b/.bmeme/build/commands/docker/start.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Start docker environment" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Starting${CN} docker environment ..." + docker-compose start +fi diff --git a/.bmeme/build/commands/docker/status.sh b/.bmeme/build/commands/docker/status.sh new file mode 100755 index 0000000..70cc30f --- /dev/null +++ b/.bmeme/build/commands/docker/status.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Check docker environment status" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Checking${CN} docker environment status ..." + docker-compose ps +fi diff --git a/.bmeme/build/commands/docker/stop.sh b/.bmeme/build/commands/docker/stop.sh new file mode 100755 index 0000000..2988b8f --- /dev/null +++ b/.bmeme/build/commands/docker/stop.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Stop docker environment" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo -e "============ ${CB}Stopping${CN} docker environment ..." + docker-compose stop +fi diff --git a/.bmeme/build/commands/requirements.sh b/.bmeme/build/commands/requirements.sh new file mode 100755 index 0000000..9e71a2b --- /dev/null +++ b/.bmeme/build/commands/requirements.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +source "${SCRIPT_DIR}/../common/lib.sh" + +if [[ "$1" == "descr" ]]; then + echo -n "Check system requirements" + exit 0 +fi + +if [[ "$1" == "run" ]]; then + echo "" + echo -e "Checking ${CG}system requirements${CN} ..." + echo "" + echo "${LOGPREFIX} CMD docker --version" >> ${LOGFILE} + docker --version 2>&1 >> ${LOGFILE} + echo "${LOGPREFIX} CMD docker-compose --version" >> ${LOGFILE} + docker-compose --version 2>&1 >> ${LOGFILE} +fi diff --git a/.bmeme/build/common/lib.sh b/.bmeme/build/common/lib.sh new file mode 100644 index 0000000..365672c --- /dev/null +++ b/.bmeme/build/common/lib.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -e + +CN="\\033[0m" +CR="\\033[91m" +CG="\\033[92m" +CY="\\033[93m" +CB="\\033[94m" +CT="\\033[37m" +TB="\\033[1m" + +LIB_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) +BASE_PRJ_DIR=$(realpath "${LIB_SCRIPT_DIR}/../../..") +if test -f "${BASE_PRJ_DIR}/.env"; then + source "${BASE_PRJ_DIR}/.env" +fi +APP_DIR="${BASE_PRJ_DIR}/app" + +CONTAINER="app" + +BUILD_DIR="${BASE_PRJ_DIR}/.bmeme/build" +LOCAL_PATH="${BUILD_DIR}/bin" +C_BIN="${LOCAL_PATH}/c" +APP_BIN="${LOCAL_PATH}/app" + +LOGDIR="${BASE_PRJ_DIR}/.bmeme/build/log" +LOGFILE="${LOGDIR}/configure.log" +LOGPREFIX=">>>> " + +# Load custom configuration +if test -f "${BUILD_DIR}/config"; then + source "${BUILD_DIR}/config" +fi + +log_begin() { + echo "" >>${LOGFILE} + echo "${LOGPREFIX} SCRIPT EXECUTED" >>${LOGFILE} + echo "${LOGPREFIX} Bin: ${0}" >>${LOGFILE} + echo "${LOGPREFIX} Params: ${@}" >>${LOGFILE} + echo "${LOGPREFIX} Time: $(date +'%Y-%m-%d %H:%M:%S')" >>${LOGFILE} + echo "" >>${LOGFILE} +} + +log_end() { + echo "" >>${LOGFILE} + echo "${LOGPREFIX} SCRIPT TERMINATED" >>${LOGFILE} + echo "${LOGPREFIX} Time: $(date +'%Y-%m-%d %H:%M:%S')" >>${LOGFILE} + echo "" >>${LOGFILE} +} + +show_cmd_error() { + echo -e "${TB}${CR}ERROR${CN}" + echo "" + echo -e "See ${CB}logfile${CN} for details: " + echo -e "\t${CY}tail -f ${LOGFILE}${CN}" + echo "" + exit 1 +} diff --git a/.bmeme/build/configure b/.bmeme/build/configure new file mode 100755 index 0000000..99e742f --- /dev/null +++ b/.bmeme/build/configure @@ -0,0 +1,196 @@ +#!/usr/bin/env bash + +set -e +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=$(which greadlink) +else + READLINK=$(which readlink) +fi + +THIS_SCRIPT_DIR=$(dirname -- "$(${READLINK} -f -- "${BASH_SOURCE[0]}")") +source "${THIS_SCRIPT_DIR}/common/lib.sh" + +mkdir -p "${LOGDIR}" + +if ! test -f "${BASE_PRJ_DIR}/.env"; then + echo + echo -e "It looks like it's the ${CG}first time${CN} you run the ${CB}configure${CN} script." + echo -e "You need to set up environment ${CY}variables${CN} to continue." + echo + echo -e "Please specify the ${CG}vendor${CN} for your new project." + echo "Typically this is your organization name, or the organization the projects belongs to." + echo -n -e "${CY}Vendor:${CN} " + read VENDOR + + echo "" + echo -e "Please specify the ${CG}name${CN} for your new project." + echo -n -e "${CY}Project name:${CN} " + read PROJECT + + echo "" + echo -e "Configuring ${CG}env${CN} file..." + sed "s/@@@PROJECT@@@/${PROJECT}/g;s/@@@VENDOR@@@/${VENDOR}/g" "${BASE_PRJ_DIR}/.env.dist" > "${BASE_PRJ_DIR}/.env" + rm -f "${BASE_PRJ_DIR}/.env.dist" + + source "${BASE_PRJ_DIR}/.env" +fi + +if ! test -f "${BASE_PRJ_DIR}/.env"; then + exit 1 +fi + +# Rif: https://stackoverflow.com/a/37939589 +function version { + echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; +} + +function command_version() { + + ACTUAL_VERSION=$(cat "${BASE_PRJ_DIR}/.bmeme/build/version") + echo -e "Installed version: ${CY}${ACTUAL_VERSION}${CN}" +} + +function command_update() { + + TMPDIR="/tmp/bmeme-build-management" + rm -rf "${TMPDIR}" + mkdir "${TMPDIR}" + git clone git@gitlab.bmemelab.cloud:bmeme/development-kickstarters/bmeme-build-management.git "${TMPDIR}" + REMOTE_VERSION=$(cat "${TMPDIR}/version") + ACTUAL_VERSION=$(cat "${BASE_PRJ_DIR}/.bmeme/build/version") + echo -e "Installed version: ${CY}${ACTUAL_VERSION}${CN}" + if [ $(version "${REMOTE_VERSION}") -gt $(version "${ACTUAL_VERSION}") ]; then + echo -e "A ${CR}new version${CN} is available: ${CY}${REMOTE_VERSION}${CN}" + else + echo -e "Your version is ${CG}up to date${CN}" + fi + rm -rf "${TMPDIR}" +} + +function command_upgrade() { + + TMPDIR="/tmp/bmeme-build-management" + rm -rf "${TMPDIR}" + mkdir "${TMPDIR}" + git clone git@gitlab.bmemelab.cloud:bmeme/development-kickstarters/bmeme-build-management.git "${TMPDIR}" + REMOTE_VERSION=$(cat "${TMPDIR}/version") + ACTUAL_VERSION=$(cat "${BASE_PRJ_DIR}/.bmeme/build/version") + echo -e "Installed version: ${CY}${ACTUAL_VERSION}${CN}" + if [ $(version "${REMOTE_VERSION}") -gt $(version "${ACTUAL_VERSION}") ]; then + echo -e "Installing ${CR}new version${CN}: ${CY}${REMOTE_VERSION}${CN}" + cp "${TMPDIR}/src/build/bin/app" "${BASE_PRJ_DIR}/.bmeme/build/bin/app" + cp "${TMPDIR}/src/build/bin/c" "${BASE_PRJ_DIR}/.bmeme/build/bin/c" + cp "${TMPDIR}/src/build/configure" "${BASE_PRJ_DIR}/.bmeme/build/configure" + cp "${TMPDIR}/version" "${BASE_PRJ_DIR}/.bmeme/build/version" + else + echo -e "Your version is ${CG}up to date${CN}" + fi + rm -rf "${TMPDIR}" +} + +function command_env() { + if [[ ":$PATH:" != *":$LOCAL_PATH:"* ]]; then + echo -n "export PATH=\"${LOCAL_PATH}:\${PATH}\"" + fi +} + +function command_help() { + + echo "" + echo "==============================================================================================" + echo -e " ${CB}${TB}${COMPOSE_PROJECT_NAME}${CN} - ${CB}Help${CN} " + echo "==============================================================================================" + echo "" + echo -en "${CY}" + printf "%-25s" " version" + echo -e "${CN} -- Show script version" + echo -en "${CY}" + printf "%-25s" " update" + echo -e "${CN} -- Check for available upgrades" + echo -en "${CY}" + printf "%-25s" " upgrade" + echo -e "${CN} -- Upgrade script to last available version" + echo -en "${CY}" + printf "%-25s" " help" + echo -e "${CN} -- Show help on how to use this script" + for i in $(ls "${BASE_PRJ_DIR}/.bmeme/build/commands"); do + if [[ -d "${BASE_PRJ_DIR}/.bmeme/build/commands/$i" ]]; then + for l in $(ls "${BASE_PRJ_DIR}/.bmeme/build/commands/$i"); do + CMD_DESCR=$("${BASE_PRJ_DIR}/.bmeme/build/commands/$i/$l" descr) + echo -en "${CY}" + printf "%-25s" " ${i}:${l%.*}" + echo -e "${CN} -- ${CMD_DESCR}" + done + continue + fi + CMD_DESCR=$("${BASE_PRJ_DIR}/.bmeme/build/commands/$i" descr) + echo -en "${CY}" + printf "%-25s" " ${i%.*}" + echo -e "${CN} -- ${CMD_DESCR}" + done + echo -en "${CY}" + + echo "" + if [[ ":$PATH:" == *":$LOCAL_PATH:"* ]]; then + echo -e "Your ${CG}\$PATH${CN} is correctly set." + else + echo -e "Your ${CG}\$PATH${CN} is not configured." + echo -e "Run: ${CY}eval \$(${0} env)${CN}" + fi + echo "" + + echo "" + echo "" + echo -e "${CR}${TB}DNS aliases${CN}" + echo "" + echo -e "These aliases work if you have ${CY}dinghy-http-proxy${CN} on MacOs or ${CY}dnsdock${CN} on Linux configured." + echo "" + if [[ "${PROJECT_PORT}" == "80" ]]; then + echo -e " - ${CG}http://app.${PROJECT_NAME}.${PROJECT_VENDOR}.docker${CN}" + else + echo -e " - ${CG}http://app.${PROJECT_NAME}.${PROJECT_VENDOR}.docker${CN} (MacOs)" + echo -e " - ${CG}http://app.${PROJECT_NAME}.${PROJECT_VENDOR}.docker:${PROJECT_PORT}${CN} (Linux)" + fi + echo "" + echo "" + echo -e "${CR}${TB}Script log file${CN}" + echo -e " - tail -f ${CG}${LOGFILE}${CN}" + echo "" +} + +log_begin + +if [ $# -eq 0 ]; then + command_help + log_end + exit 0 +fi + +for cmdLineArg in "$@"; do + + if [[ "$cmdLineArg" == "help" ]]; then + command_help + elif [[ "$cmdLineArg" == "version" ]]; then + command_version + elif [[ "$cmdLineArg" == "update" ]]; then + command_update + elif [[ "$cmdLineArg" == "upgrade" ]]; then + command_upgrade + elif [[ "$cmdLineArg" == "env" ]]; then + command_env + elif [[ "$cmdLineArg" =~ .*":".* ]] && [[ -f "${BASE_PRJ_DIR}/.bmeme/build/commands/${cmdLineArg%:*}/${cmdLineArg#*:}.sh" ]]; then + bash -c "${BASE_PRJ_DIR}/.bmeme/build/commands/${cmdLineArg%:*}/${cmdLineArg#*:}.sh run" + elif [[ -f "${BASE_PRJ_DIR}/.bmeme/build/commands/${cmdLineArg}.sh" ]]; then + bash -c "${BASE_PRJ_DIR}/.bmeme/build/commands/${cmdLineArg}.sh run" + else + echo -e "Unrecognized command: ${CR}${cmdLineArg}${CN}" + echo + echo -e "Try: ${CY}${0} help${CN}" + log_end + exit 1 + fi + +done + +log_end +exit 0 diff --git a/.bmeme/build/version b/.bmeme/build/version new file mode 100644 index 0000000..cb174d5 --- /dev/null +++ b/.bmeme/build/version @@ -0,0 +1 @@ +1.2.1 \ No newline at end of file diff --git a/.bmeme/docker-compose.yml b/.bmeme/docker-compose.yml new file mode 100644 index 0000000..c7f5709 --- /dev/null +++ b/.bmeme/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.4" +services: + app: + build: + context: docker/ + command: /bin/sleep infinity + working_dir: /usr/src/ + volumes: + - "../:/usr/src:cached" + expose: + - "80" + environment: + - DNSDOCK_ALIAS=app.${PROJECT_NAME}.${PROJECT_VENDOR}.docker + - VIRTUAL_HOST=app.${PROJECT_NAME}.${PROJECT_VENDOR}.docker + - VIRTUAL_PORT=80 diff --git a/.bmeme/docker/Dockerfile b/.bmeme/docker/Dockerfile new file mode 100644 index 0000000..e8e2c1e --- /dev/null +++ b/.bmeme/docker/Dockerfile @@ -0,0 +1,8 @@ +FROM perl:5.34-slim + +ARG FIX_UID +ARG FIX_GID + +RUN groupadd -g $FIX_GID -o perl +RUN useradd -m -u $FIX_UID -g $FIX_GID -d /usr/src/app -o -s /bin/bash perl +USER perl diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..f22d180 --- /dev/null +++ b/.env.dist @@ -0,0 +1,9 @@ +# Custom variables. +PROJECT_NAME=@@@PROJECT@@@ +PROJECT_VENDOR=@@@VENDOR@@@ +PROJECT_PORT=8080 + +# Compose CLI environment variables. +# @see https://docs.docker.com/compose/reference/envvars +COMPOSE_PROJECT_NAME=@@@PROJECT@@@_@@@VENDOR@@@ +COMPOSE_FILE=.bmeme/docker-compose.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cb4ba0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.bmeme/build/log +/.vscode/ +/.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..1986455 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# Perl docker development kickstarter +[![Maintained? Yes](https://img.shields.io/badge/Maintained%3F-Yes-success)](https://github.com/bmeme/docker-perl-kickstarter) ![GitHub last commit](https://img.shields.io/github/last-commit/bmeme/docker-perl-kickstarter) [![GitHub issues](https://img.shields.io/github/issues/bmeme/docker-perl-kickstarter)](https://github.com/bmeme/docker-perl-kickstarter/issues) ![GitHub top language](https://img.shields.io/github/languages/top/bmeme/docker-perl-kickstarter) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/bmeme/docker-perl-kickstarter) + +This project can be used as a starter to create a local environment to develop an application with [Perl](https://nodejs.org/) using [Docker](https://www.docker.com/). + +The idea is that you can use docker to develop your Perl application, without having to install anything on your system. + +Top benefits: + - easy: you can easily configure your environment with the needed tools and versions + - isolation: you will not mess up your system, anything is containerized and could be cleared in a moment + - consistency: Docker provides a consistent environment for your application, the same for every developer or system + +After the initial configuration, you will use [Memento Docker Plugin](https://github.com/bmeme/memento-docker) and act pretty the same as you use to do if you have Perl installed on your system. + +## How to use this kickstarter + +### 0. Check your requirements + +To let anything works, you need to have `docker` and `docker-compose` installed and configured on your system. + +Also, you need [Memento](https://github.com/bmeme/memento), [Memento Kickstarter](https://github.com/bmeme/memento-kickstarter) and [Memento Docker Plugin](https://github.com/bmeme/memento-docker). + +### 1. Clone this repository + +Start by creating a new project using this kickstarter: + +``` +memento kickstarter create perl +``` + +### 2. Define your project name and group + +Move to `` and run `memento docker configure`. +First time you will be prompted for a couple of questions. +You will see a basic help with some information about the available commands. + +To initialize the docker environment for your project, run: + +``` +memento docker configure docker:create +``` + +### 3. Check the result + +You can check that everything is working by running: + +``` +memento docker cmd perl --version +``` + +Eureka! If you can see `perl` version probably anything went well, and you can start developing your awesome application! + +## Contributing + +Any feedback, bug reports or ideas are extremely welcome. + +Reach us through our [website](https://www.bmeme.com) or send us an email at [info@bmeme.com](mailto:info@bmeme.com). + +## License + +[MIT](https://choosealicense.com/licenses/mit/) + +## References + +- [BMEME Digital Factory](https://www.bmeme.com) +- [Memento](https://github.com/bmeme/memento) +- [Perl Docker official images](https://hub.docker.com/_/perl)