From d01a36b5fe408e1e23b7033d0e0147efcefbf4d0 Mon Sep 17 00:00:00 2001 From: TiB Rkt Arimana <5566338+b23prodtm@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:41:11 +0100 Subject: [PATCH 1/9] Resolve service availability (#214) * --- .balena/balena.yml | 13 + .balena/secrets/secret_master_password | 1 + .balena/secrets/secret_mysql_database | 1 + .balena/secrets/secret_mysql_password | 1 + .balena/secrets/secret_mysql_root_password | 1 + .balena/secrets/secret_mysql_user | 1 + .circleci/build.sh | 20 - .circleci/config.yml | 330 ++++++++---- .env | 2 +- .github/workflows/build.yml | 85 +++ .gitignore | 10 +- .stickler.yml | 2 - .travis.yml | 102 ---- .yarn/install-state.gz | Bin 7392 -> 15001 bytes .yarnrc.yml | 2 - BUILD.md | 75 +++ Dockerfile.aarch64 | 75 +-- Dockerfile.armhf | 75 +-- Dockerfile.template | 75 +-- Dockerfile.x86_64 | 75 +-- FAQ.md | 10 +- INSTALL.md | 488 ++++++++++++++---- README.md | 24 +- Scripts/bootargs.sh | 5 +- Scripts/bootstrap.sh | 32 +- Scripts/composer.sh | 8 +- Scripts/config_etc_pass.sh | 58 +-- Scripts/configure_database.sh | 2 +- Scripts/configure_path.sh | 19 +- Scripts/cp_bkp_old.sh | 2 +- Scripts/docker_site_conf.sh | 16 - Scripts/fooargs.sh | 18 +- Scripts/lib/logging.sh | 63 ++- Scripts/lib/parsing.sh | 278 ++++++---- Scripts/lib/shell_prompt.sh | 26 +- Scripts/lib/test/test_parsing.sh | 137 +++-- Scripts/lib/util.sh | 4 +- Scripts/start_daemon.sh | 118 ++--- aarch64.env | 1 + .../php-cms/e13/etc/constantes.properties | 2 +- .../php-cms/e13/etc/getHashPassword.php | 4 +- armhf.env | 1 + balena-connect-it.sh | 2 +- balena-storage/.env | 2 +- balena-storage/Dockerfile.aarch64.sed | 4 +- balena-storage/Dockerfile.armhf.sed | 2 +- balena-storage/Dockerfile.template.sed | 22 +- balena-storage/Dockerfile.x86_64.sed | 4 +- balena-storage/build.aarch64.sh | 8 + balena-storage/build.armhf.sh | 8 + balena-storage/build.template | 8 + balena-storage/build.x86_64.sh | 8 + balena-storage/common.env | 1 - balena.yml | 4 +- build.aarch64.sh | 8 + build.armhf.sh | 8 + build.template | 8 + build.x86_64.sh | 8 + build/.travis_linux_php.ini | 1 - build/.travis_osx_php.ini | 1 - build/.travis_windows_php.ini | 1 - common.env | 2 +- composer.lock | 123 ++++- configure.sh | 149 +++--- deploy.sh | 24 +- deployment/images/build.sh | 2 - deployment/images/httpd/.env | 2 +- .../images/httpd/.yarn/install-state.gz | Bin 0 -> 13296 bytes deployment/images/httpd/.yarnrc.yml | 1 + deployment/images/httpd/Dockerfile.aarch64 | 27 +- deployment/images/httpd/Dockerfile.armhf | 27 +- deployment/images/httpd/Dockerfile.template | 27 +- deployment/images/httpd/Dockerfile.x86_64 | 27 +- deployment/images/httpd/README.md | 1 + deployment/images/httpd/Scripts/a2enmod.sh | 1 + deployment/images/httpd/build.aarch64.sh | 8 + deployment/images/httpd/build.armhf.sh | 8 + deployment/images/httpd/build.template | 8 + deployment/images/httpd/build.x86_64.sh | 8 + deployment/images/httpd/common.env | 2 +- .../apache2/conf-available/servername.conf | 6 +- .../httpd/etc/apache2/conf-available/ssl.conf | 115 +++++ .../apache2/sites-available/000-default.conf | 2 +- .../etc/apache2/sites-available/001-ssl.conf | 178 ++++++- deployment/images/httpd/package.json | 7 + deployment/images/httpd/yarn.lock | 335 ++++++++++++ deployment/images/mysqldb/.env | 2 +- deployment/images/mysqldb/Dockerfile.aarch64 | 55 +- .../images/mysqldb/Dockerfile.aarch64.sed | 4 +- deployment/images/mysqldb/Dockerfile.armhf | 55 +- .../images/mysqldb/Dockerfile.armhf.sed | 2 +- deployment/images/mysqldb/Dockerfile.template | 55 +- .../images/mysqldb/Dockerfile.template.sed | 22 +- deployment/images/mysqldb/Dockerfile.x86_64 | 55 +- .../images/mysqldb/Dockerfile.x86_64.sed | 4 +- deployment/images/mysqldb/build.aarch64.sh | 8 + deployment/images/mysqldb/build.armhf.sh | 8 + deployment/images/mysqldb/build.template | 8 + deployment/images/mysqldb/build.x86_64.sh | 8 + deployment/images/mysqldb/common.env | 1 - deployment/images/mysqldb/curl.sh | 4 +- deployment/images/mysqldb/init-db.sh | 23 + discord.sh | 98 ---- docker-bake.hcl | 89 ++++ docker-compose-alias.sh | 41 -- docker-compose.aarch64 | 19 +- docker-compose.armhf | 67 ++- docker-compose.template | 12 +- docker-compose.x86_64 | 54 +- docker-compose.yml | 28 +- master_password_hash | 1 + migrate-database.sh | 173 +++---- package.json | 8 +- start-cake.sh | 44 +- test-cake.sh | 52 +- x86_64.env | 1 + yarn.lock | 213 +------- 117 files changed, 3006 insertions(+), 1668 deletions(-) create mode 100644 .balena/balena.yml create mode 100644 .balena/secrets/secret_master_password create mode 100644 .balena/secrets/secret_mysql_database create mode 100644 .balena/secrets/secret_mysql_password create mode 100644 .balena/secrets/secret_mysql_root_password create mode 100644 .balena/secrets/secret_mysql_user delete mode 100755 .circleci/build.sh create mode 100644 .github/workflows/build.yml delete mode 100644 .stickler.yml delete mode 100644 .travis.yml create mode 100644 BUILD.md delete mode 100755 Scripts/docker_site_conf.sh mode change 100755 => 100644 Scripts/lib/shell_prompt.sh mode change 100644 => 100755 Scripts/lib/test/test_parsing.sh create mode 100644 balena-storage/build.aarch64.sh create mode 100644 balena-storage/build.armhf.sh create mode 100644 balena-storage/build.template create mode 100644 balena-storage/build.x86_64.sh delete mode 120000 balena-storage/common.env create mode 100755 build.aarch64.sh create mode 100755 build.armhf.sh create mode 100755 build.template create mode 100755 build.x86_64.sh delete mode 100644 build/.travis_linux_php.ini delete mode 100644 build/.travis_osx_php.ini delete mode 100644 build/.travis_windows_php.ini delete mode 100755 deployment/images/build.sh create mode 100644 deployment/images/httpd/.yarn/install-state.gz create mode 100644 deployment/images/httpd/.yarnrc.yml create mode 100644 deployment/images/httpd/README.md create mode 100644 deployment/images/httpd/build.aarch64.sh create mode 100644 deployment/images/httpd/build.armhf.sh create mode 100644 deployment/images/httpd/build.template create mode 100644 deployment/images/httpd/build.x86_64.sh create mode 100644 deployment/images/httpd/etc/apache2/conf-available/ssl.conf create mode 100644 deployment/images/httpd/package.json create mode 100644 deployment/images/httpd/yarn.lock create mode 100755 deployment/images/mysqldb/build.aarch64.sh create mode 100755 deployment/images/mysqldb/build.armhf.sh create mode 100644 deployment/images/mysqldb/build.template create mode 100755 deployment/images/mysqldb/build.x86_64.sh delete mode 120000 deployment/images/mysqldb/common.env create mode 100644 deployment/images/mysqldb/init-db.sh delete mode 100755 discord.sh create mode 100644 docker-bake.hcl delete mode 100755 docker-compose-alias.sh create mode 100644 master_password_hash diff --git a/.balena/balena.yml b/.balena/balena.yml new file mode 100644 index 000000000..69fcb89cd --- /dev/null +++ b/.balena/balena.yml @@ -0,0 +1,13 @@ +## YAML Template. +build-secrets: + global: + - source: secret_mysql_root_password + dest: mysql_root_password + - source: secret_mysql_user + dest: mysql_user + - source: secret_mysql_password + dest: mysql_password + - source: secret_mysql_database + dest: mysql_database + - source: secret_master_password + dest: master_password \ No newline at end of file diff --git a/.balena/secrets/secret_master_password b/.balena/secrets/secret_master_password new file mode 100644 index 000000000..bef8347e7 --- /dev/null +++ b/.balena/secrets/secret_master_password @@ -0,0 +1 @@ +missing-root-password \ No newline at end of file diff --git a/.balena/secrets/secret_mysql_database b/.balena/secrets/secret_mysql_database new file mode 100644 index 000000000..1e7fb18ad --- /dev/null +++ b/.balena/secrets/secret_mysql_database @@ -0,0 +1 @@ +missing-database-name \ No newline at end of file diff --git a/.balena/secrets/secret_mysql_password b/.balena/secrets/secret_mysql_password new file mode 100644 index 000000000..ada4c3ad8 --- /dev/null +++ b/.balena/secrets/secret_mysql_password @@ -0,0 +1 @@ +missing-password \ No newline at end of file diff --git a/.balena/secrets/secret_mysql_root_password b/.balena/secrets/secret_mysql_root_password new file mode 100644 index 000000000..bef8347e7 --- /dev/null +++ b/.balena/secrets/secret_mysql_root_password @@ -0,0 +1 @@ +missing-root-password \ No newline at end of file diff --git a/.balena/secrets/secret_mysql_user b/.balena/secrets/secret_mysql_user new file mode 100644 index 000000000..40c4c32dd --- /dev/null +++ b/.balena/secrets/secret_mysql_user @@ -0,0 +1 @@ +missing-user-name \ No newline at end of file diff --git a/.circleci/build.sh b/.circleci/build.sh deleted file mode 100755 index b058d0c40..000000000 --- a/.circleci/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -work_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -usage=("" \ -"Usage: $0" \ -" CircleCI needs a primary image backend." \ -" To build it use deployment/build.sh script to push to image registry and tag it:" \ -" deployment/images/build.sh deployment/images/php7 betothreeprod/php7 x86_64-latest" \ -"Then you can run composition process: sudo docker-compose up --build" \ -"") -[ ! "$(command -v circleci)" ] && curl -fLSs https://circle.ci/cli | bash -# https://github.com/koalaman/shellcheck/wiki/SC2207 -# shellcheck source=../deploy.sh -mapfile -t dock < <(find "${work_dir}/../deployment/images" -name "Dockerfile.x86_64") -[ "$#" -lt 1 ] && printf "Usage: %s " "$0" && exit 0 -for d in "${dock[@]}"; do - dir=$(dirname "$d") - docker_build "$dir" "." "$1/$(basename "$dir")" "$(arch)" -done -sed -e "/custom_checkout:/s/\"\"/\"\/tmp\/_circleci_local_build_repo\"/g" "${work_dir}/config.yml" | circleci config process - > "${work_dir}/config-compat.yml" -circleci local execute -c "${work_dir}/config-compat.yml" || echo -e "${usage[0]}" diff --git a/.circleci/config.yml b/.circleci/config.yml index 874e1ed6c..9cbb5a54a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,120 +1,274 @@ version: 2.1 -orbs: - shellcheck: circleci/shellcheck@1.3.16 -jobs: - httpd: + +executors: + docker-executor: docker: - - image: cimg/base:2025.11 - environment: - TAG: latest-x86_64 - IMAGE: httpd - BUILD_ARCH: x86_64 - WORK_DIR: deployment/images/httpd - auth: - username: $DOCKER_USER - password: $DOCKER_PASS + - image: cimg/base:2025.11 + resource_class: medium + +commands: + setup-buildx: + steps: + - run: + name: Install Docker Buildx + command: | + mkdir -p ~/.docker/cli-plugins + BUILDX_VERSION=v0.12.1 + wget -q "https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64" \ + -O ~/.docker/cli-plugins/docker-buildx + chmod +x ~/.docker/cli-plugins/docker-buildx + docker buildx version + + - run: + name: Create buildx builder + command: | + docker buildx create --name multiarch --driver docker-container --use --bootstrap + docker buildx inspect multiarch + + write-secrets: + description: "Store secrets in secret file on build-time" + steps: + - run: + name: Store secrets + command: | + echo "$MYSQL_ROOT_PASSWORD" > .balena/secrets/secret_mysql_root_password + echo "$MYSQL_USER" > .balena/secrets/secret_mysql_user + echo "$MYSQL_PASSWORD" > .balena/secrets/secret_mysql_password + echo "$MYSQL_DATABASE" > .balena/secrets/secret_mysql_database + echo "$MASTER_PASSWORD" > .balena/secrets/secret_master_password + + set-build-tag: + description: "Set BUILD_TAG environment variable based on branch or tag" + steps: + - run: + name: Set build tag + command: | + if [ -n "${CIRCLE_TAG}" ]; then + TAG="${CIRCLE_TAG}" + else + TAG="${CIRCLE_BRANCH}-${CIRCLE_SHA1:0:7}" + fi + # lowercase + TAG=$(echo "$TAG" | tr '[:upper:]' '[:lower:]') + # replace invalid chars with '-' + echo "export BUILD_TAG=$(echo "$TAG" | sed 's/[^a-z0-9._-]/-/g')" >> $BASH_ENV + source $BASH_ENV + echo "Build tag: ${BUILD_TAG}" + +jobs: + shellcheck: + docker: + - image: koalaman/shellcheck-alpine:stable + steps: + - checkout + - run: + name: Run shellcheck + command: | + find . -type f -name "*.sh" -exec shellcheck --exclude=SC2154,SC1091,SC2034,SC2096,SC2038 {} + + + build-x86_64: + executor: docker-executor steps: - checkout - setup_remote_docker: + version: docker24 docker_layer_caching: true + + - setup-buildx + - set-build-tag + - write-secrets + - run: - name: Build Docker image + name: Login to Docker Hub command: | - cd $WORK_DIR - docker build -t $DOCKER_USER/$IMAGE:$TAG -f Dockerfile.$BUILD_ARCH . + echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin + - run: - name: Push application Docker image + name: Build and push x86_64 command: | - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push $DOCKER_USER/$IMAGE:$TAG - mysql: - # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass - # resource_class: b23prodtm/linux - docker: - - image: cimg/base:2025.11 - environment: - TAG: latest-x86_64 - IMAGE: mariadb - BUILD_ARCH: x86_64 - WORK_DIR: deployment/images/mysqldb - auth: - username: $DOCKER_USER - password: $DOCKER_PASS + export DOCKER_ORG=${DOCKER_USER} + export BAKE_TAG=${BUILD_TAG} + export BALENA_ARCH=x86_64 + export PLATFORM=linux/amd64 + + echo "Building ${DOCKER_ORG}/*:${BAKE_TAG} for ${PLATFORM}" + ln -sf "${BALENA_ARCH}.env" ".env" + cp -vf "docker-compose.${BALENA_ARCH}" "docker-compose.yml" + docker buildx bake -f docker-bake.hcl \ + --set "*.platform=${PLATFORM}" \ + --push + + - run: + name: Tag images with architecture suffix + command: | + for service in mysqldb php-fpm httpd balena-storage; do + docker buildx imagetools create \ + --tag ${DOCKER_USER}/${service}:${BUILD_TAG}-x86_64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG} + done + + build-aarch64: + executor: docker-executor steps: - checkout - setup_remote_docker: - version: edge - docker_layer_caching: false + version: docker24 + docker_layer_caching: true + + - setup-buildx + - set-build-tag + - write-secrets + - run: - name: Build Docker image + name: Login to Docker Hub command: | - cd $WORK_DIR - docker build -t $DOCKER_USER/$IMAGE:$TAG -f Dockerfile.$BUILD_ARCH . + echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin + - run: - name: Push application Docker image + name: Build and push aarch64 command: | - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push $DOCKER_USER/$IMAGE:$TAG - php-fpm: - # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass - # resource_class: b23prodtm/linux - docker: - - image: cimg/base:2025.11 - environment: - TAG: latest-x86_64 - IMAGE: php-fpm - BUILD_ARCH: x86_64 - WORK_DIR: . - auth: - username: $DOCKER_USER - password: $DOCKER_PASS + export DOCKER_ORG=${DOCKER_USER} + export BAKE_TAG=${BUILD_TAG} + export BALENA_ARCH=aarch64 + export PLATFORM=linux/arm64 + + echo "Building ${DOCKER_ORG}/*:${BAKE_TAG} for ${PLATFORM}" + ln -sf "${BALENA_ARCH}.env" ".env" + cp -vf "docker-compose.${BALENA_ARCH}" "docker-compose.yml" + docker buildx bake -f docker-bake.hcl \ + --set "*.platform=${PLATFORM}" \ + --push + + - run: + name: Tag images with architecture suffix + command: | + for service in mysqldb php-fpm httpd balena-storage; do + docker buildx imagetools create \ + --tag ${DOCKER_USER}/${service}:${BUILD_TAG}-aarch64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG} + done + + build-armhf: + executor: docker-executor steps: - checkout - setup_remote_docker: - version: edge - docker_layer_caching: false + version: docker24 + docker_layer_caching: true + + - setup-buildx + - set-build-tag + - write-secrets + - run: - name: Build Docker image + name: Login to Docker Hub command: | - cd $WORK_DIR - docker build -t $DOCKER_USER/$IMAGE:$TAG -f Dockerfile.$BUILD_ARCH . + echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin + - run: - name: Push application Docker image + name: Build and push armhf command: | - echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin - docker push $DOCKER_USER/$IMAGE:$TAG - compose: - # The resource_class feature allows configuring CPU and RAM resources for each job. Different resource classes are available for different executors. https://circleci.com/docs/2.0/configuration-reference/#resourceclass - # resource_class: b23prodtm/linux - docker: - - image: cimg/aws:2025.01 - shell: /bin/bash -leo pipefail - environment: - - BASH_ENV: /etc/profile - - BUILD_ARCH: x86_64 + export DOCKER_ORG=${DOCKER_USER} + export BAKE_TAG=${BUILD_TAG} + export BALENA_ARCH=armhf + export PLATFORM=linux/arm/v7 + + echo "Building ${DOCKER_ORG}/*:${BAKE_TAG} for ${PLATFORM}" + ln -sf "${BALENA_ARCH}.env" ".env" + cp -vf "docker-compose.${BALENA_ARCH}" "docker-compose.yml" + docker buildx bake -f docker-bake.hcl \ + --set "*.platform=${PLATFORM}" \ + --push + + - run: + name: Tag images with architecture suffix + command: | + for service in mysqldb php-fpm httpd balena-storage; do + docker buildx imagetools create \ + --tag ${DOCKER_USER}/${service}:${BUILD_TAG}-armhf \ + ${DOCKER_USER}/${service}:${BUILD_TAG} + done + + create-manifests: + executor: docker-executor steps: - checkout - setup_remote_docker: - version: edge - docker_layer_caching: true + version: docker24 + + - set-build-tag + + - run: + name: Login to Docker Hub + command: | + echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin + + - run: + name: Create and push multi-arch manifests + command: | + for service in mysqldb php-fpm httpd balena-storage; do + echo "Creating manifest for ${service}:${BUILD_TAG}" + + # Create multi-arch manifest + docker manifest create ${DOCKER_USER}/${service}:${BUILD_TAG} \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-x86_64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-aarch64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-armhf + + docker manifest push ${DOCKER_USER}/${service}:${BUILD_TAG} + + # Also create/update latest tag for main branch + if [ "${CIRCLE_BRANCH}" = "main" ] || [ "${CIRCLE_BRANCH}" = "master" ]; then + echo "Updating latest tag for ${service}" + docker manifest create ${DOCKER_USER}/${service}:latest \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-x86_64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-aarch64 \ + ${DOCKER_USER}/${service}:${BUILD_TAG}-armhf + + docker manifest push ${DOCKER_USER}/${service}:latest + fi + done + - run: + name: Verify manifests command: | - set -u - TAG=0.1.$CIRCLE_BUILD_NUM - docker-compose -f docker-compose.$BUILD_ARCH build + for service in mysqldb php-fpm httpd balena-storage; do + echo "Verifying ${service}:${BUILD_TAG}" + docker manifest inspect ${DOCKER_USER}/${service}:${BUILD_TAG} + done + workflows: - cleanup: + build-all-branches: jobs: - - shellcheck/check: - ignore: 'SC2154,SC1091,SC2034,SC2096,SC2038' - build-and-compose: - jobs: - - httpd - - mysql - - php-fpm - - compose: + - shellcheck + + - build-x86_64: + context: + - Info-b23prodtm + requires: + - shellcheck + filters: + branches: + only: development + + - build-aarch64: + context: + - Info-b23prodtm + requires: + - shellcheck + + - build-armhf: + context: + - Info-b23prodtm + requires: + - shellcheck + + - create-manifests: requires: - - httpd - - mysql - - php-fpm - version: 2 + - build-x86_64 + - build-aarch64 + - build-armhf + filters: + branches: + only: development + diff --git a/.env b/.env index 7c3af0ef2..7a294362f 120000 --- a/.env +++ b/.env @@ -1 +1 @@ -/var/www/armhf.env \ No newline at end of file +/home/brunotr/acake2php/aarch64.env \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..b2865e1b4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,85 @@ +name: Multi-Arch Build +on: + push: + branches: [ "main", "development" ] + pull_request: +env: + DOCKER_ORG: ${{ secrets.DOCKER_USER }} + MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }} + MYSQL_USER: ${{ secrets.MYSQL_USER }} + MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }} + MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }} + MASTER_PASSWORD: ${{ secrets.MASTER_PASSWORD }} +jobs: + build-services: + runs-on: ubuntu-latest +# runs-on: self-hosted + environment: AUTH + strategy: + fail-fast: false + matrix: + arch: [ x86_64 ] + # arch: [ x86_64, aarch64, armhf ] + steps: + - uses: actions/checkout@v4 + - name: Enable QEMU + uses: docker/setup-qemu-action@v3 + - name: Enable Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASS }} + - name: Sanitize ref name for Docker tag + id: sanitize + run: | + RAW="${{ github.ref_name }}" + TAG=$(echo "$RAW" | tr '[:upper:]' '[:lower:]') + TAG=$(echo "$TAG" | sed 's/[^a-z0-9._-]/-/g') + echo "tag=$TAG" >> $GITHUB_OUTPUT + - name: Build docker-compose stack + env: + BALENA_ARCH: ${{ matrix.arch }} + BAKE_TAG: ${{ steps.sanitize.outputs.tag }} + run: | + case "${BALENA_ARCH}" in + "armhf") export PLATFORM="linux/arm/v7" ;; + "aarch64") export PLATFORM="linux/arm64" ;; + "x86_64") export PLATFORM="linux/amd64" ;; + *) export PLATFORM="linux/$(uname -m)" ;; + esac + ln -sf "${BALENA_ARCH}.env" ".env" + cp -vf "docker-compose.${BALENA_ARCH}" "docker-compose.yml" + + echo "${{ secrets.MYSQL_ROOT_PASSWORD }}" > .balena/secrets/secret_mysql_root_password + echo "${{ secrets.MYSQL_USER }}" > .balena/secrets/secret_mysql_user + echo "${{ secrets.MYSQL_PASSWORD }}" > .balena/secrets/secret_mysql_password + echo "${{ secrets.MYSQL_DATABASE }}" > .balena/secrets/secret_mysql_database + echo "${{ secrets.MASTER_PASSWORD }}" > .balena/secrets/secret_master_password + + docker buildx bake -f docker-bake.hcl \ + --set "*.platform=${PLATFORM}" \ + --push + + deploy: + runs-on: ubuntu-latest +# runs-on: self-hosted + environment: AUTH + needs: build-services + if: github.event_name != 'pull_request' + steps: + - uses: actions/checkout@v4 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASS }} + - name: Set up Docker Compose + uses: docker/setup-compose-action@v1 + with: + version: latest + - name: Deploy stack + env: + BAKE_TAG: ${{ github.ref_name }} + run: docker compose up -d diff --git a/.gitignore b/.gitignore index 6e58a5bc4..884ec99e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # User specific & automatically generated files # ################################################# +master_password_hash !Dockerfile.* !docker-compose.* *.sed @@ -26,8 +27,11 @@ tags *.mo !*empty bin/composer -mysqldb/config/ -mysqldb/conf.d/custom.cnf +balena-storage/*.env +deployment/images/httpd/*.env +deployment/images/mysqldb/*.env +deployment/images/mysqldb/config/ +deployment/images/mysqldb/conf.d/custom.cnf node_modules *.log @@ -49,7 +53,5 @@ Icon? ehthumbs.db Thumbs.db nbproject/private/ -mysqldb/mysqld/ -mysqld/conf.d/custom.cnf upgrade/ package-lock.json diff --git a/.stickler.yml b/.stickler.yml deleted file mode 100644 index 61243f090..000000000 --- a/.stickler.yml +++ /dev/null @@ -1,2 +0,0 @@ -branches: - ignore: ['2.x', '2.next'] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c53c12665..000000000 --- a/.travis.yml +++ /dev/null @@ -1,102 +0,0 @@ -cache: - directories: - - app/tmp/cache - - ".autoconf" - - "$HOME/.composer/cache/files" -# language: php # linux only -language: node_js # cross-platform -node_js: - - "14.5" - -os: - - linux - - osx - - windows - -arch: - - amd64 - - arm64 - -dist: bionic -addons: - mariadb: '10.3' - apt: - sources: - - sourceline: ppa:ondrej/php - -env: - matrix: - - _PHP=7.2 _PKG=php PHPENV_ROOT=~/.phpenv - - _PHP=7.3 _PKG=php PHPENV_ROOT=~/.phpenv - - global: - - DEBIAN_FRONTEND=noninteractive - - PHPENV_ROOT=/usr/local/bin/phpenv - - _SSL=openssl - - MYPHPCMS_LOG=app/tmp/logs - - WEBHOOK_URL=https://discordapp.com/api/webhooks/479745392880386058/YOO7Nnn1IFWUtXX0n0qAZYeMDeV-SLa0lSzjGpNnKGTzG-xA0T3dplVGzSM4ObKLeMWg - - secure: C39DQ1zYpSAOy33Sb8NP89o6k4HUnjHnQ+bQkgbo3WH7WtiN76dGeO9jm1DjMn5np6oKbDi41/fxonaTUIjb9YMksG2YB+NBDYXHyV1H7/xAeC6uTxwUObrLXh8aOUwiiuLPllMdtOLX8JSPxl1Ixc6KyeFywPiMvwuOe4QZW6sVG4sqhZC/UUycYKRSMaOthtuTDPYKjBLaDPiEzkUmdBIo9IhAsidEFAHj4jEmw9gBtac0B2x7GbvvoDivdH3KdNSoPt2SkD0RLX51Qf7AYeAV4fw65cuCp/Aat/uk55x3lN5g18Ww9khY/cFSwPC0JXGQnnJvdcDP2diZNkqE41Yc/Mw3xwfrvp3/v8js3VIBzsGINKiSdXZ/yXqI9iRzvzlfmXRHvd4sFXRzUpi8ZB4PXaboMndbNRoh4PcoRNFtXhyebQCEgZv2x3oiKXw38WT5cga03uMH4E5z5afS3n7NP1tsuiNWd499dJzAkW8OPeLDPqY3a/b5qLeZIK1bo23mvCjPtbm+B2g2QocKcd1oQ+XANCyuT3M/+AIypdMabGOSon2fDKrUUN+SqIX4FcYaHi1sG3qUp23870u9YcT4hK2LCKBcBPkFpxqEZLwdHbJmjHPo6uRyAKOD7r/k1DtIRqkcJ+5BpU4emo6rM9rdh5mWjLtK7vdY8Y/tvP8= - -matrix: - #fast_finish: true - include: - - os: linux - arch: arm64 - - os: osx - arch: amd64 - - os: windows - arch: amd64 - allow_failures: - - os: linux - arch: arm64 - - os: osx - - os: windows - -services: -- mysql -- docker -- memcached - -before_install: - - cd .travis/TravisCI-OSX-PHP - - source build/phpenv_install.sh - - source build/prepare_${TRAVIS_OS_NAME}_env.sh #; source exports - - if [[ "${TRAVIS_OS_NAME}" = "linux" && "${_PHP}" = 7* ]]; then COLLECT_COVERAGE=true ; fi - - if [[ "${TRAVIS_OS_NAME}" = "linux" && "${COLLECT_COVERAGE}" != "true" ]]; then phpenv config-rm xdebug.ini || true ; fi - - if [ ! -z "${ADDITIONAL_PHP_INI}" ]; then build/custom_php_ini.sh; fi - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "${_SSL}" "latest"; - - if [[ "${TRAVIS_OS_NAME}" != "windows" ]]; then [ $(which c_rehash) > /dev/null ] && sudo c_rehash; fi - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "curl" "latest" "--with-openssl" | tail - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "${_PKG}" "${_PHP}" "--with-homebrew-curl" - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "${_PKG}" "${_PHP}-xml" | tail - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "${_PKG}" "${_PHP}-fpm" | tail - - php -i | grep "SSL Version" - - build/handle_${TRAVIS_OS_NAME}_pkg.sh "composer" "latest" | tail - - cd ../.. - #- docker login -u $DOCKER_USER -p $DOCKER_PASS - - sudo mkdir -p /etc/mysql/conf.d/ && sudo cp -f deployment/images/mysqldb/conf.d/my.cnf /etc/mysql/conf.d/my.cnf - - sudo sed -i.bind "/bind-address/s/=.*$/= ${MYSQL_BIND_ADDRESS:-127.0.0.1}/" /etc/mysql/conf.d/my.cnf - - if [[ "${TRAVIS_OS_NAME}" = "linux" ]]; then sudo systemctl restart mysql; fi - - npm install --no-optional - - npm link balena-cloud-apps -install: - - ./deploy.sh "$(arch)" --nobuild --exit - -before_script: - - mkdir -p ${MYPHPCMS_LOG} -script: - - if [[ "${TRAVIS_OS_NAME}" != "linux" ]]; then ./test-cake.sh --travis --phpcs || true; fi - - if [[ "${TRAVIS_OS_NAME}" = "linux" ]]; then ./test-cake.sh --travis; fi - - cat "${MYPHPCMS_LOG}/travis.${TRAVIS_BUILD_NUMBER}.log" || true -after_success: - - ./discord.sh success "$WEBHOOK_URL" - - zip -r acake2php.zip * > /dev/null - - git tag --force "${GIT_RELEASE_VERSION}.${TRAVIS_BUILD_NUMBER}" -deploy: - provider: releases - api_key: - secure: UmXoq0sFQQixpMH12MG1Q3+pQhw05SuN919FgnKCru3X3RTCpXnZB8hgXvQHZn5Hhunq5eBPZ7C/bvk5wkzPXrwpAXyA7wj7PhGn6QbksU6xtkNOQVera/pMoTJW2VtNA2GcWvUvCnht3m73Tp2lguaI3Q6Yt8qq1vHJlDO5hbgvC0LdDrRFLIAA95jA9DWZRCWanHV5aepVNkX0qjQm25BIAwvQtfxkj/DipDYy2eIMh8brh6aZamE3cBv8sXP8b2b89GB670E35Otfseu58a6HxqmLD4dI5kIEzhSFjuelPJne0tmebFKww+mPn7v2SKJZR9xVfQM+mZHAp7SiozEvi1nqhUN31Z64mpDQDPnAB6eO36rGSdxDyYN7Ab24lJuLTcNkRbNOaJzn88q9d5DqGGQRJl6875cJmPFOjUwNteJIvLhDmPX3oHSPvWx3AtpEUtWeCrVt1yc7tyG0kdW8MmzoWvgDhbyvqAefKO3bEzVZeQQy/7LKy1i42+B/7N57oR6sgFX2N/zvdckFKjwHNiydds67Wj++EuJRXr2AhxdraW3iwDBTmkrZ3TjOsxcySbMBJHCOwHF9yAmLklbk4h1haCohkQrQxBp6Rhm3vKJGDOR3rOkjqIzqgHgo3pcXDdBULMpfB3/IbDeh3/RMuTEOXvv7FUNRjb4JIdI= - file: acake2php.zip - on: - repo: b23prodtm/acake2php - branch: feature/gh-releases diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 003dcc30a106add1d34959c9805bf621d40d8ff2..ebbe215eb3da8d934c305f8204df5d3d53f1500c 100644 GIT binary patch literal 15001 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 literal 7392 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 S+uG~@*8c-oKM!k6s{jB`dlNGN diff --git a/.yarnrc.yml b/.yarnrc.yml index f1556331b..3186f3f07 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1 @@ nodeLinker: node-modules - -yarnPath: .yarn/releases/yarn-4.6.0.cjs diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 000000000..7bd1d101d --- /dev/null +++ b/BUILD.md @@ -0,0 +1,75 @@ +# Docker Bake Configuration + +The x-bake configuration has been extracted from `docker-compose.yml` to `docker-bake.hcl`. + +## Usage + +### Building with Docker Buildx Bake + +To build all images, e.g. x86_64 (linux/amd64): +```bash +balena_deploy . x86_64 3 0 +docker buildx bake -f docker-bake.hcl +``` + +To build a specific service: +```bash +docker buildx bake -f docker-bake.hcl db +docker buildx bake -f docker-bake.hcl php-fpm +docker buildx bake -f docker-bake.hcl httpd +docker buildx bake -f docker-bake.hcl balena-storage +``` + +### Setting Variables + +You can override variables using environment variables or command-line flags: + +```bash +# Using environment variables +export DOCKER_ORG=myorg +export BAKE_TAG=v1.0.0 +export PLATFORM=linux/amd64 +export BALENA_ARCH=x86_64 + +# Using command-line +balena_deploy . x86_64 3 0 +docker buildx bake -f docker-bake.hcl \ + --set "*.platform=linux/amd64" \ + --set "db.tags=myorg/mysqldb:v1.0.0" +``` + +### Cross-platform Builds + +To build for other architecture platforms, e.g aarch64 (linux/arm64): +```bash +balena_deploy . aarch64 3 0 +docker buildx bake -f docker-bake.hcl \ + --set "*.platform=linux/arm64" +``` +Multi-arch parallel builds aren't available. Only set 1 platform build at a time! +This is due to the project structure (*.env, multiple Dockerfiles, etc.) + +### Push to Registry + +To build and push to a registry, e.g. x86_64 (linux/amd64): +```bash +balena_deploy . x86_64 3 0 +docker buildx bake -f docker-bake.hcl --push +``` + +## Variables + +- `DOCKER_ORG`: Docker organization/username (default: `betothreeprod`) +- `BAKE_TAG`: Tag for the images (default: `latest`) +- `PLATFORM`: Target platform (default: `linux/amd64`) +- `BALENA_ARCH`: Balena architecture (default: `amd64`) + +## Docker Compose + +The `docker-compose.yml` file has been cleaned up and no longer contains x-bake configuration. You can still use it for local development: + +```bash +docker-compose up -d +``` + +Note: You'll need to use update_templates command line to filter out %%PLATFORM%% and %%BALENA_ARCH%% tags in docker-compose.template to docker-compose.yml. diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 index cee0c3c0b..ac2301756 100644 --- a/Dockerfile.aarch64 +++ b/Dockerfile.aarch64 @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # BEGIN - Multi-stage Build # Stage 1: Install NodeJS dependencies FROM node:20-alpine3.16 AS node-build @@ -6,11 +7,14 @@ WORKDIR /usr/local/project # Copy Node.js dependency files, Fixing global modules issue COPY package.json yarn.lock .yarnrc.yml ./ -COPY .yarn/ .yarn -RUN apk update \ - && apk add --no-cache yarn \ - && yarn install --immutable \ - && yarn cache clean +COPY .yarn .yarn +RUN apk add --no-cache nodejs npm \ + && npm install -g corepack \ + && corepack enable \ + && corepack prepare yarn@4 --activate \ + && corepack install -g yarn \ + && yarn install --immutable \ + && yarn cache clean # Stage 2: Build PHP-FPM environment FROM php:7.4.33-fpm-alpine3.16 @@ -22,7 +26,7 @@ WORKDIR ${HTDOCS} # Add mor NodeJS, CakePHP and Composer commands to system path ENV PATH="${HTDOCS}/node_modules/.bin:${HTDOCS}/app/bin:${HTDOCS}/bin:${PATH}" -COPY --from=node-build /usr/local/project/node_modules/ node_modules +COPY --from=node-build /usr/local/project/node_modules node_modules # END - Multi-stage Build ARG PUID @@ -43,23 +47,25 @@ RUN apk update \ zip \ unzip \ mariadb-client \ - gettext + gettext \ + openssh-client RUN apk del build-base \ && rm -rf /var/cache/apk/* # ---------------------- # About Dockerfile ARGS: -# ALL the following ARGS are DUPLICATED in common.env and $BALENA_ARCH.env -# and Scripts/fooargs.sh for development phase. -# If not set in common.env, it must be set in the host machine environment -# when ./start.sh or ./test.sh or in development phase. +# ALL the following ARGS have defaults values in common.env and $BALENA_ARCH.env +# and Scripts/fooargs.sh (testing fake values) +# For Production, use environment variables in your container orchestrator (Docker). # ---------------------- # Values in default (no-arg) production mode # May be overriden in docker-compose.yml #----------------------- +ARG DEBUG +ENV DEBUG=${DEBUG:-0} ARG CAKEPHP_DEBUG_LEVEL -ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-2} +ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-1} ARG BALENA_MACHINE_NAME ENV BALENA_MACHINE_NAME=${BALENA_MACHINE_NAME:-raspberrypi3-64} ARG SECONDARY_HUB @@ -84,18 +90,8 @@ ARG MYSQL_HOST ENV MYSQL_HOST=${MYSQL_HOST:-"db"} ARG MYSQL_TCP_PORT ENV MYSQL_TCP_PORT=${MYSQL_TCP_PORT:-3306} -ARG MYSQL_DATABASE -ENV MYSQL_DATABASE=${MYSQL_DATABASE:-"aria_db"} -ARG MYSQL_ROOT_PASSWORD -ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"mariadb"} ARG TEST_MYSQL_DATABASE ENV TEST_MYSQL_DATABASE=${TEST_MYSQL_DATABASE:-"test"} -ARG MYSQL_USER -ENV MYSQL_USER=${MYSQL_USER:-"maria"} -ARG MYSQL_PASSWORD -ENV MYSQL_PASSWORD=${MYSQL_PASSWORD:-"maria-abc"} -ARG HASH_PASSWORD -ENV HASH_PASSWORD=${HASH_PASSWORD:-"password"} # Install necessary PHP extensions and dependencies COPY --from=ghcr.io/mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ @@ -120,19 +116,38 @@ RUN addgroup -g $GUID acake2php \ && adduser -h $PWD -G acake2php -D -u $PUID acake2php # Copy application files -COPY --chown=$PUID:$GUID . . +COPY --chown=acake2php:acake2php . . +RUN printf "%s\n" "Ensure to remove secrets folder..." && rm -Rfv .balena # Run initialization scripts -RUN chmod +x Scripts/*.sh \ +RUN chmod +x Scripts/*.sh *.sh \ + && ./deploy.sh ${BALENA_ARCH} --no-build \ && git init \ - && git config --global --add safe.directory $PWD \ - && update_templates \ - && ./configure.sh --runner -p ${HASH_PASSWORD} -s "$(openssl rand -hex 4)" --development \ - && ./migrate-database.sh -r -i -u ${MIGRATE_OPTION} \ - && ./migrate-database.sh -r -u ${MIGRATE_OPTION} --connection=test + && git config --global user.email "b23prodtm@users.sourceforge.net" \ + && git config --global user.name "B R T" \ + && git config --global --add safe.directory $PWD + +# These are now expected at RUNTIME only — no ARG, no default values baked in +# They must be provided via: docker buildx build --secret id=mytoken,src=.balena/secrets/secret_mytoken +RUN \ +### BEGIN DOCKER_BUILDKIT + --mount=type=secret,id=master_password \ + --mount=type=secret,id=mysql_database \ + --mount=type=secret,id=mysql_password \ + --mount=type=secret,id=mysql_root_password \ + --mount=type=secret,id=mysql_user \ +### END DOCKER_BUILDKIT + MASTER_PASSWORD=$(cat /run/secrets/master_password) \ + MYSQL_DATABASE=$(cat /run/secrets/mysql_database) \ + MYSQL_PASSWORD=$(cat /run/secrets/mysql_password) \ + MYSQL_ROOT_PASSWORD=$(cat /run/secrets/mysql_root_password) \ + MYSQL_USER=$(cat /run/secrets/mysql_user) \ + ./configure.sh --runner -g acake2php -p ${MASTER_PASSWORD} -s "$(openssl rand -hex 4)" --dev \ + && ./migrate-database.sh --runner -i -u ${MIGRATE_OPTION} \ + && ./migrate-database.sh --runner -u ${MIGRATE_OPTION} --connection=test # As set in /etc/php-fpm.d/ EXPOSE 9001 RUN php-fpm -t -ENTRYPOINT ["php-fpm", "-F"] +CMD ["php-fpm", "-F"] diff --git a/Dockerfile.armhf b/Dockerfile.armhf index f403ccc50..b877b818a 100644 --- a/Dockerfile.armhf +++ b/Dockerfile.armhf @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # BEGIN - Multi-stage Build # Stage 1: Install NodeJS dependencies FROM node:20-alpine3.16 AS node-build @@ -6,11 +7,14 @@ WORKDIR /usr/local/project # Copy Node.js dependency files, Fixing global modules issue COPY package.json yarn.lock .yarnrc.yml ./ -COPY .yarn/ .yarn -RUN apk update \ - && apk add --no-cache yarn \ - && yarn install --immutable \ - && yarn cache clean +COPY .yarn .yarn +RUN apk add --no-cache nodejs npm \ + && npm install -g corepack \ + && corepack enable \ + && corepack prepare yarn@4 --activate \ + && corepack install -g yarn \ + && yarn install --immutable \ + && yarn cache clean # Stage 2: Build PHP-FPM environment FROM php:7.4.33-fpm-alpine3.16 @@ -22,7 +26,7 @@ WORKDIR ${HTDOCS} # Add mor NodeJS, CakePHP and Composer commands to system path ENV PATH="${HTDOCS}/node_modules/.bin:${HTDOCS}/app/bin:${HTDOCS}/bin:${PATH}" -COPY --from=node-build /usr/local/project/node_modules/ node_modules +COPY --from=node-build /usr/local/project/node_modules node_modules # END - Multi-stage Build ARG PUID @@ -43,23 +47,25 @@ RUN apk update \ zip \ unzip \ mariadb-client \ - gettext + gettext \ + openssh-client RUN apk del build-base \ && rm -rf /var/cache/apk/* # ---------------------- # About Dockerfile ARGS: -# ALL the following ARGS are DUPLICATED in common.env and $BALENA_ARCH.env -# and Scripts/fooargs.sh for development phase. -# If not set in common.env, it must be set in the host machine environment -# when ./start.sh or ./test.sh or in development phase. +# ALL the following ARGS have defaults values in common.env and $BALENA_ARCH.env +# and Scripts/fooargs.sh (testing fake values) +# For Production, use environment variables in your container orchestrator (Docker). # ---------------------- # Values in default (no-arg) production mode # May be overriden in docker-compose.yml #----------------------- +ARG DEBUG +ENV DEBUG=${DEBUG:-0} ARG CAKEPHP_DEBUG_LEVEL -ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-2} +ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-1} ARG BALENA_MACHINE_NAME ENV BALENA_MACHINE_NAME=${BALENA_MACHINE_NAME:-raspberrypi3} ARG SECONDARY_HUB @@ -84,18 +90,8 @@ ARG MYSQL_HOST ENV MYSQL_HOST=${MYSQL_HOST:-"db"} ARG MYSQL_TCP_PORT ENV MYSQL_TCP_PORT=${MYSQL_TCP_PORT:-3306} -ARG MYSQL_DATABASE -ENV MYSQL_DATABASE=${MYSQL_DATABASE:-"aria_db"} -ARG MYSQL_ROOT_PASSWORD -ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"mariadb"} ARG TEST_MYSQL_DATABASE ENV TEST_MYSQL_DATABASE=${TEST_MYSQL_DATABASE:-"test"} -ARG MYSQL_USER -ENV MYSQL_USER=${MYSQL_USER:-"maria"} -ARG MYSQL_PASSWORD -ENV MYSQL_PASSWORD=${MYSQL_PASSWORD:-"maria-abc"} -ARG HASH_PASSWORD -ENV HASH_PASSWORD=${HASH_PASSWORD:-"password"} # Install necessary PHP extensions and dependencies COPY --from=ghcr.io/mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ @@ -120,19 +116,38 @@ RUN addgroup -g $GUID acake2php \ && adduser -h $PWD -G acake2php -D -u $PUID acake2php # Copy application files -COPY --chown=$PUID:$GUID . . +COPY --chown=acake2php:acake2php . . +RUN printf "%s\n" "Ensure to remove secrets folder..." && rm -Rfv .balena # Run initialization scripts -RUN chmod +x Scripts/*.sh \ +RUN chmod +x Scripts/*.sh *.sh \ + && ./deploy.sh ${BALENA_ARCH} --no-build \ && git init \ - && git config --global --add safe.directory $PWD \ - && update_templates \ - && ./configure.sh --runner -p ${HASH_PASSWORD} -s "$(openssl rand -hex 4)" --development \ - && ./migrate-database.sh -r -i -u ${MIGRATE_OPTION} \ - && ./migrate-database.sh -r -u ${MIGRATE_OPTION} --connection=test + && git config --global user.email "b23prodtm@users.sourceforge.net" \ + && git config --global user.name "B R T" \ + && git config --global --add safe.directory $PWD + +# These are now expected at RUNTIME only — no ARG, no default values baked in +# They must be provided via: docker buildx build --secret id=mytoken,src=.balena/secrets/secret_mytoken +RUN \ +### BEGIN DOCKER_BUILDKIT + --mount=type=secret,id=master_password \ + --mount=type=secret,id=mysql_database \ + --mount=type=secret,id=mysql_password \ + --mount=type=secret,id=mysql_root_password \ + --mount=type=secret,id=mysql_user \ +### END DOCKER_BUILDKIT + MASTER_PASSWORD=$(cat /run/secrets/master_password) \ + MYSQL_DATABASE=$(cat /run/secrets/mysql_database) \ + MYSQL_PASSWORD=$(cat /run/secrets/mysql_password) \ + MYSQL_ROOT_PASSWORD=$(cat /run/secrets/mysql_root_password) \ + MYSQL_USER=$(cat /run/secrets/mysql_user) \ + ./configure.sh --runner -g acake2php -p ${MASTER_PASSWORD} -s "$(openssl rand -hex 4)" --dev \ + && ./migrate-database.sh --runner -i -u ${MIGRATE_OPTION} \ + && ./migrate-database.sh --runner -u ${MIGRATE_OPTION} --connection=test # As set in /etc/php-fpm.d/ EXPOSE 9001 RUN php-fpm -t -ENTRYPOINT ["php-fpm", "-F"] +CMD ["php-fpm", "-F"] diff --git a/Dockerfile.template b/Dockerfile.template index 1a0ca8d7f..76c41e0a4 100644 --- a/Dockerfile.template +++ b/Dockerfile.template @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # BEGIN - Multi-stage Build # Stage 1: Install NodeJS dependencies FROM node:20-alpine3.16 AS node-build @@ -6,11 +7,14 @@ WORKDIR /usr/local/project # Copy Node.js dependency files, Fixing global modules issue COPY package.json yarn.lock .yarnrc.yml ./ -COPY .yarn/ .yarn -RUN apk update \ - && apk add --no-cache yarn \ - && yarn install --immutable \ - && yarn cache clean +COPY .yarn .yarn +RUN apk add --no-cache nodejs npm \ + && npm install -g corepack \ + && corepack enable \ + && corepack prepare yarn@4 --activate \ + && corepack install -g yarn \ + && yarn install --immutable \ + && yarn cache clean # Stage 2: Build PHP-FPM environment FROM php:7.4.33-fpm-alpine3.16 @@ -22,7 +26,7 @@ WORKDIR ${HTDOCS} # Add mor NodeJS, CakePHP and Composer commands to system path ENV PATH="${HTDOCS}/node_modules/.bin:${HTDOCS}/app/bin:${HTDOCS}/bin:${PATH}" -COPY --from=node-build /usr/local/project/node_modules/ node_modules +COPY --from=node-build /usr/local/project/node_modules node_modules # END - Multi-stage Build ARG PUID @@ -43,23 +47,25 @@ RUN apk update \ zip \ unzip \ mariadb-client \ - gettext + gettext \ + openssh-client RUN apk del build-base \ && rm -rf /var/cache/apk/* # ---------------------- # About Dockerfile ARGS: -# ALL the following ARGS are DUPLICATED in common.env and $BALENA_ARCH.env -# and Scripts/fooargs.sh for development phase. -# If not set in common.env, it must be set in the host machine environment -# when ./start.sh or ./test.sh or in development phase. +# ALL the following ARGS have defaults values in common.env and $BALENA_ARCH.env +# and Scripts/fooargs.sh (testing fake values) +# For Production, use environment variables in your container orchestrator (Docker). # ---------------------- # Values in default (no-arg) production mode # May be overriden in docker-compose.yml #----------------------- +ARG DEBUG +ENV DEBUG=${DEBUG:-0} ARG CAKEPHP_DEBUG_LEVEL -ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-2} +ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-1} ARG BALENA_MACHINE_NAME ENV BALENA_MACHINE_NAME=${BALENA_MACHINE_NAME:-"%%BALENA_MACHINE_NAME%%"} ARG SECONDARY_HUB @@ -84,18 +90,8 @@ ARG MYSQL_HOST ENV MYSQL_HOST=${MYSQL_HOST:-"db"} ARG MYSQL_TCP_PORT ENV MYSQL_TCP_PORT=${MYSQL_TCP_PORT:-3306} -ARG MYSQL_DATABASE -ENV MYSQL_DATABASE=${MYSQL_DATABASE:-"aria_db"} -ARG MYSQL_ROOT_PASSWORD -ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"mariadb"} ARG TEST_MYSQL_DATABASE ENV TEST_MYSQL_DATABASE=${TEST_MYSQL_DATABASE:-"test"} -ARG MYSQL_USER -ENV MYSQL_USER=${MYSQL_USER:-"maria"} -ARG MYSQL_PASSWORD -ENV MYSQL_PASSWORD=${MYSQL_PASSWORD:-"maria-abc"} -ARG HASH_PASSWORD -ENV HASH_PASSWORD=${HASH_PASSWORD:-"password"} # Install necessary PHP extensions and dependencies COPY --from=ghcr.io/mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ @@ -120,19 +116,38 @@ RUN addgroup -g $GUID acake2php \ && adduser -h $PWD -G acake2php -D -u $PUID acake2php # Copy application files -COPY --chown=$PUID:$GUID . . +COPY --chown=acake2php:acake2php . . +RUN printf "%s\n" "Ensure to remove secrets folder..." && rm -Rfv .balena # Run initialization scripts -RUN chmod +x Scripts/*.sh \ +RUN chmod +x Scripts/*.sh *.sh \ + && ./deploy.sh ${BALENA_ARCH} --no-build \ && git init \ - && git config --global --add safe.directory $PWD \ - && update_templates \ - && ./configure.sh --runner -p ${HASH_PASSWORD} -s "$(openssl rand -hex 4)" --development \ - && ./migrate-database.sh -r -i -u ${MIGRATE_OPTION} \ - && ./migrate-database.sh -r -u ${MIGRATE_OPTION} --connection=test + && git config --global user.email "b23prodtm@users.sourceforge.net" \ + && git config --global user.name "B R T" \ + && git config --global --add safe.directory $PWD + +# These are now expected at RUNTIME only — no ARG, no default values baked in +# They must be provided via: docker buildx build --secret id=mytoken,src=.balena/secrets/secret_mytoken +RUN \ +### BEGIN DOCKER_BUILDKIT + --mount=type=secret,id=master_password \ + --mount=type=secret,id=mysql_database \ + --mount=type=secret,id=mysql_password \ + --mount=type=secret,id=mysql_root_password \ + --mount=type=secret,id=mysql_user \ +### END DOCKER_BUILDKIT + MASTER_PASSWORD=$(cat /run/secrets/master_password) \ + MYSQL_DATABASE=$(cat /run/secrets/mysql_database) \ + MYSQL_PASSWORD=$(cat /run/secrets/mysql_password) \ + MYSQL_ROOT_PASSWORD=$(cat /run/secrets/mysql_root_password) \ + MYSQL_USER=$(cat /run/secrets/mysql_user) \ + ./configure.sh --runner -g acake2php -p ${MASTER_PASSWORD} -s "$(openssl rand -hex 4)" --dev \ + && ./migrate-database.sh --runner -i -u ${MIGRATE_OPTION} \ + && ./migrate-database.sh --runner -u ${MIGRATE_OPTION} --connection=test # As set in /etc/php-fpm.d/ EXPOSE 9001 RUN php-fpm -t -ENTRYPOINT ["php-fpm", "-F"] +CMD ["php-fpm", "-F"] diff --git a/Dockerfile.x86_64 b/Dockerfile.x86_64 index c63de2176..f39b7b930 100644 --- a/Dockerfile.x86_64 +++ b/Dockerfile.x86_64 @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # BEGIN - Multi-stage Build # Stage 1: Install NodeJS dependencies FROM node:20-alpine3.16 AS node-build @@ -6,11 +7,14 @@ WORKDIR /usr/local/project # Copy Node.js dependency files, Fixing global modules issue COPY package.json yarn.lock .yarnrc.yml ./ -COPY .yarn/ .yarn -RUN apk update \ - && apk add --no-cache yarn \ - && yarn install --immutable \ - && yarn cache clean +COPY .yarn .yarn +RUN apk add --no-cache nodejs npm \ + && npm install -g corepack \ + && corepack enable \ + && corepack prepare yarn@4 --activate \ + && corepack install -g yarn \ + && yarn install --immutable \ + && yarn cache clean # Stage 2: Build PHP-FPM environment FROM php:7.4.33-fpm-alpine3.16 @@ -22,7 +26,7 @@ WORKDIR ${HTDOCS} # Add mor NodeJS, CakePHP and Composer commands to system path ENV PATH="${HTDOCS}/node_modules/.bin:${HTDOCS}/app/bin:${HTDOCS}/bin:${PATH}" -COPY --from=node-build /usr/local/project/node_modules/ node_modules +COPY --from=node-build /usr/local/project/node_modules node_modules # END - Multi-stage Build ARG PUID @@ -43,23 +47,25 @@ RUN apk update \ zip \ unzip \ mariadb-client \ - gettext + gettext \ + openssh-client RUN apk del build-base \ && rm -rf /var/cache/apk/* # ---------------------- # About Dockerfile ARGS: -# ALL the following ARGS are DUPLICATED in common.env and $BALENA_ARCH.env -# and Scripts/fooargs.sh for development phase. -# If not set in common.env, it must be set in the host machine environment -# when ./start.sh or ./test.sh or in development phase. +# ALL the following ARGS have defaults values in common.env and $BALENA_ARCH.env +# and Scripts/fooargs.sh (testing fake values) +# For Production, use environment variables in your container orchestrator (Docker). # ---------------------- # Values in default (no-arg) production mode # May be overriden in docker-compose.yml #----------------------- +ARG DEBUG +ENV DEBUG=${DEBUG:-0} ARG CAKEPHP_DEBUG_LEVEL -ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-2} +ENV CAKEPHP_DEBUG_LEVEL=${CAKEPHP_DEBUG_LEVEL:-1} ARG BALENA_MACHINE_NAME ENV BALENA_MACHINE_NAME=${BALENA_MACHINE_NAME:-intel-nuc} ARG SECONDARY_HUB @@ -84,18 +90,8 @@ ARG MYSQL_HOST ENV MYSQL_HOST=${MYSQL_HOST:-"db"} ARG MYSQL_TCP_PORT ENV MYSQL_TCP_PORT=${MYSQL_TCP_PORT:-3306} -ARG MYSQL_DATABASE -ENV MYSQL_DATABASE=${MYSQL_DATABASE:-"aria_db"} -ARG MYSQL_ROOT_PASSWORD -ENV MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"mariadb"} ARG TEST_MYSQL_DATABASE ENV TEST_MYSQL_DATABASE=${TEST_MYSQL_DATABASE:-"test"} -ARG MYSQL_USER -ENV MYSQL_USER=${MYSQL_USER:-"maria"} -ARG MYSQL_PASSWORD -ENV MYSQL_PASSWORD=${MYSQL_PASSWORD:-"maria-abc"} -ARG HASH_PASSWORD -ENV HASH_PASSWORD=${HASH_PASSWORD:-"password"} # Install necessary PHP extensions and dependencies COPY --from=ghcr.io/mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ @@ -120,19 +116,38 @@ RUN addgroup -g $GUID acake2php \ && adduser -h $PWD -G acake2php -D -u $PUID acake2php # Copy application files -COPY --chown=$PUID:$GUID . . +COPY --chown=acake2php:acake2php . . +RUN printf "%s\n" "Ensure to remove secrets folder..." && rm -Rfv .balena # Run initialization scripts -RUN chmod +x Scripts/*.sh \ +RUN chmod +x Scripts/*.sh *.sh \ + && ./deploy.sh ${BALENA_ARCH} --no-build \ && git init \ - && git config --global --add safe.directory $PWD \ - && update_templates \ - && ./configure.sh --runner -p ${HASH_PASSWORD} -s "$(openssl rand -hex 4)" --development \ - && ./migrate-database.sh -r -i -u ${MIGRATE_OPTION} \ - && ./migrate-database.sh -r -u ${MIGRATE_OPTION} --connection=test + && git config --global user.email "b23prodtm@users.sourceforge.net" \ + && git config --global user.name "B R T" \ + && git config --global --add safe.directory $PWD + +# These are now expected at RUNTIME only — no ARG, no default values baked in +# They must be provided via: docker buildx build --secret id=mytoken,src=.balena/secrets/secret_mytoken +RUN \ +### BEGIN DOCKER_BUILDKIT + --mount=type=secret,id=master_password \ + --mount=type=secret,id=mysql_database \ + --mount=type=secret,id=mysql_password \ + --mount=type=secret,id=mysql_root_password \ + --mount=type=secret,id=mysql_user \ +### END DOCKER_BUILDKIT + MASTER_PASSWORD=$(cat /run/secrets/master_password) \ + MYSQL_DATABASE=$(cat /run/secrets/mysql_database) \ + MYSQL_PASSWORD=$(cat /run/secrets/mysql_password) \ + MYSQL_ROOT_PASSWORD=$(cat /run/secrets/mysql_root_password) \ + MYSQL_USER=$(cat /run/secrets/mysql_user) \ + ./configure.sh --runner -g acake2php -p ${MASTER_PASSWORD} -s "$(openssl rand -hex 4)" --dev \ + && ./migrate-database.sh --runner -i -u ${MIGRATE_OPTION} \ + && ./migrate-database.sh --runner -u ${MIGRATE_OPTION} --connection=test # As set in /etc/php-fpm.d/ EXPOSE 9001 RUN php-fpm -t -ENTRYPOINT ["php-fpm", "-F"] +CMD ["php-fpm", "-F"] diff --git a/FAQ.md b/FAQ.md index b811d9da6..e50ea295d 100644 --- a/FAQ.md +++ b/FAQ.md @@ -23,12 +23,12 @@ Common Issues grant all PRIVILEGES on $TEST_DATABASE_NAME.* to '$MYSQL_USER'@'$MYSQL_HOST'; exit - ./configure.sh -c + ./configure.sh -m -i -u - This will reset the connection profile in ..etc/ properties file with the template. - More about environment variables are located in the remote pod (OpenShift) settings and locally in ./Scripts/fooargs.sh. + Try reset the connection profile in ..etc/ properties file with the template. + More about environment variables are located in the remote pod (OpenShift) settings and locally in ./Scripts/bootargs.sh (start-cake.sh) and fooargs.sh (test-cake.sh) - > Note: + > Note:u ./configure.sh --mig-database -p -i --sql-password @@ -180,7 +180,7 @@ Verify configuration in environment variables and app/config: app/config/app.template ./configure.sh -d -If locally testing, edit: +If locally testing, edit the bash script as your needs: ./Scripts/fooargs.sh diff --git a/INSTALL.md b/INSTALL.md index ceed1148b..fd6bd109f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,113 +1,375 @@ -## Build the Docker VM images -Docker :whale: VM or a remote fleet Balena/Kubernetes/etc. -A typical install script could look like the following script .circleci/config.yml -It will eventually run the container as the startup script succeeds. - -## Quick VM Startup -Everyting is ready to launch a container in real cluster environment. The process is described further. -We have provided 3 ways to make use of this project. It supports: - - [Docker CE](https://docs.docker.com/machine/get-started/) - `docker-compose up` may be enough to run and test the configuration - - [Balena Cloud](https://www.balena.io/docs/learn/getting-started/raspberrypi3/nodejs/) - `./deploy.sh armhf --balena` - - [Kubernetes](https://kubernetes.io//docs/concepts/overview/what-is-kubernetes/) - `./kompose.sh up` may be run if your shell run on a valid working cluster environment - -Please read README.md file to get more information on how to setup the cluster and handle common issues. - -## Requirements -- Broadband Internet access to the Worldwide Web, to download the packages and container images dependencies from the remote Docker registries. -- The Docker CE described with the Dockerfile. >:whale: [Get Started](https://docs.docker.com/machine/get-started/) application. -- NodeJS command line package manager interface, [npmjs](https://www.npmjs.com/get-npm) with yarn package manager -- A BASH Terminal (Linux or Darwin OS are known to work) - -Once everything is installed, please reboot your system. - -## Webserver configuration (Source balena.yml) -Very few variables are defined by default. It provides host-container-server communication. Host Firewall and file attributes set to the host platform values. - - # Open https://${SERVER_NAME}/etc/getHashPassword.php or type $ ./configure.sh -p password -s hash - # Get new staff credentials (url=/admin/index.php) - - GET_HASH_PASSWORD: (let's encrypt it from above) - - # Database name - - MYSQL_DATABASE: aria_db - # Persistent ROOT connection credentials - - MYSQL_HOST: localhost - - MYSQL_ROOT_PASSWORD: mariadb - -## Some configuration. All variables may be changed to your needs: - - # CakePHP secrets - - CAKEPHP_SECRET_TOKEN: - - CAKEPHP_SECRET_SALT: - - CAKEPHP_SECURITY_CIPHER_SEED: - - # Deployed Migration option - - MIGRATE_OPTION: -v - - # The following values are options to change if needed - # Binding a mysql container to a specific (public) IP address or all (0.0.0.0) - - MYSQL_BIND_ADDRESS: 0.0.0.0 - - MYSQL_TCP_PORT: 3306 - - # Persistent USER connection credentials - - MYSQL_USER: maria - - MYSQL_PASSWORD: maria-abc - - # Run as a different user-group space ($ id -g $USER) - - PGID: 0 - # Run as a different user space ($ id -u $USER) - - PUID: 0 - - # Apache 2 httpd, or DNS CNAME of the host machine ($ hostname) - - SERVER_NAME: www-machine.local - - # MariaDB Timezone - - TZ: Europe/Paris - -## Validate the configuration, and eventually test it: -Requirements: A Docker or any compatible must be installed and running. -Argument value `--docker` set up a local docker test configuration. - - ./configure.sh --docker --mig-database -u -i - ./test-cake.sh --docker - -## Circle CI -Developer build continuous integration -The current project is a full PHP (CakePHP) with MySQL (MariaDB) container for Docker-CE, or even a ```Dockerfile``` compatible container interface. We choose Circle CI because it's able to achieve full remote tests with docker :whale: before we deploy to a devices swarm. It actually can run on self hosted runners and remote runnners from .circle/config.yml configuration file presets. - -### [developers] Update the Docker deployment image -A. Fork the master repository (development branch). - - yarn - -B. Rebuild image registry from deployment folder if you make change to the primary. E.g. change of Linux distribution. Edit the file deployment/images/primary/Dockerfile.template to your needs and perform a build from the a Docker client machine. -If you make use of [Balena OS base image list](https://www.balena.io/docs/reference/base-images/base-images-ref/) repository you can use blocks to cross build for ARM ```# [ "cross-build-start" ] # [ "cross-build-end" ]``` command lines in the Dockerfile.template files: - - ./deploy.sh aarch64 --local --build-deps - -C. With the BalenaCloud and BalenaHub service, you may see the message on build logs: -[Error] Error: The command 'cross-build-start' returned a non-zero code: 1: -Run a push, even if it's unsuccessful: - - ./deploy.sh aarch64 --balena --push - -You have now disabled the cross-build environment. -Browse up to your github master fork-repository. -Click on Deploy with Balena in the README, choose a fleet and deploy it! - -### License - Copyright 2016-2025 www.b23prodtm.info - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +# INSTALL.md (Version Développeur) + +*Guide complet pour configurer, développer, tester et déployer le projet (nginx + PHP-FPM + MariaDB) en Docker, Balena Cloud ou Kubernetes.* + +--- + +## 🛠️ Prérequis + +### ✅ Obligatoires (pour tous les environnements) + + +| Outil | Version recommandée | Vérification | Lien | +| ------------------ | ------------------- | -------------------------- | ------------------------------------------------------- | +| **Docker CE** | ≥ 20.10 | `docker --version` | [Installer Docker](https://docs.docker.com/get-docker/) | +| **Docker Compose** | ≥ 2.0 | `docker-compose --version` | Inclus avec Docker Desktop | +| **Git** | ≥ 2.30 | `git --version` | [Installer Git](https://git-scm.com/downloads) | +| **Bash** | ≥ 5.0 | `bash --version` | Terminal Linux/macOS | + + +### ⚠️ Optionnels (selon l'environnement cible) + + +| Outil | Nécessaire pour | Installation | +| --------------------- | ---------------------------------------------- | ------------------------------------------------------------ | +| **Node.js + Yarn** | Déploiement Balena Cloud (`balena-cloud-apps`) | `npm install -g yarn` | +| **Balena CLI** | Déploiement sur Balena Cloud | `npm install -g balena-cli` | +| **Kubectl + Kompose** | Déploiement Kubernetes | [Installer kubectl](https://kubernetes.io/docs/tasks/tools/) | + + +> **⚡ Note** : Pour du **développement local pur**, seul **Docker + Bash** sont nécessaires. + +--- + +## 🚀 Installation et Configuration Initiale + +### 1️⃣ Cloner le dépôt + +```bash +git clone +cd +``` + +### 2️⃣ Initialiser l'environnement + +#### Pour du développement local (Docker) + +```bash +# Configurer les variables et la base de données +./configure.sh --docker --mig-database -u -i + +# Lancer les conteneurs +docker-compose up -d +``` + +> **Options de `configure.sh**` : +> +> - `--docker` : Configure pour Docker local. +> - `--mig-database` : Applique les migrations MySQL. +> - `-u` : Met à jour les dépendances. +> - `-i` : Installe les paquets manquants. + +#### Pour le déploiement Balena Cloud + +```bash +# Installer les dépendances Node.js (si balena-cloud-apps est utilisé) +yarn install + +# Ajouter balena-cloud-apps (si non déjà installé) +yarn add balena-cloud-apps + +# Exporter le chemin des binaires localement (optionnel) +export PATH="./node_modules/.bin:$PATH" +``` + +--- + +## ⚙️ Configuration du Projet + +### 📂 Fichiers de configuration principaux + + +| Fichier | Description | Exemple | +| ------------------------------------------ | ------------------------------------------------- | ----------------------------------------------------------------- | +| `docker-compose.yml` | Définition des services (nginx, PHP-FPM, MariaDB) | [Voir la doc Docker](https://docs.docker.com/compose/) | +| `balena.yml` | Configuration pour Balena Cloud | [Doc Balena](https://www.balena.io/docs/learn/deploy/deployment/) | +| `common.env` | Variables communes à toutes les architectures | `BALENA_PROJECTS=(.)` | +| `armhf.env` / `aarch64.env` / `x86_64.env` | Variables spécifiques à l'architecture | `BALENA_ARCH=armhf` | + + +--- + +### 🔐 Variables d'Environnement Essentielles + +#### Base de Données (MariaDB/MySQL) + +```ini +# common.env ou .env +MYSQL_DATABASE=aria_db +MYSQL_USER=maria +MYSQL_USER_PASSWORD=Some-robust-Password +MYSQL_ROOT_PASSWORD=SoMe-MorE-Robust-PAssWOrd! +MYSQL_BIND_ADDRESS=0.0.0.0 +MYSQL_TCP_PORT=3306 +``` + +#### Sécurité (CakePHP) + +```ini +CAKEPHP_SECRET_TOKEN=your_random_token_here +CAKEPHP_SECRET_SALT=your_random_salt_here +CAKEPHP_SECURITY_CIPHER_SEED=your_cipher_seed_here +``` + +> **⚠️ Générer des valeurs sécurisées** : +> +> ```bash +> # Pour un mot de passe hashé (MASTER_PASSWORD_HASH) +> ./configure.sh -p "votre_mot_de_passe" -s hash +> +> # Ou via PHP (si disponible) +> php -r 'echo password_hash("votre_mot_de_passe", PASSWORD_DEFAULT);' +> ``` + +#### Serveur Web (nginx/Apache) + +```ini +SERVER_NAME=www-machine.local # ou votre domaine +PGID=0 # ID du groupe (0 = root) +PUID=0 # ID de l'utilisateur (0 = root) +TZ=Europe/Paris # Timezone +``` + +--- + +## 💻 Développement Local + +### Lancer le projet + +```bash +# Démarrer tous les services +docker-compose up -d + +# Accéder aux logs +docker-compose logs -f + +# Arrêter les services +docker-compose down +``` + +### Tester l'application + +1. **Valider la configuration** : + ```bash + ./configure.sh --docker --mig-database -u -i + ``` +2. **Exécuter les tests** : + ```bash + ./test-cake.sh --docker + ``` +3. **Accéder à l'application** : + - URL : `http://localhost` ou `http://${SERVER_NAME}` + - Admin : `/admin/index.php` (utilisez `MASTER_PASSWORD_HASH`) + +--- + + + + +|   |   | +| ------ | ------ | +|   |   | +|   |   | +|   |   | +|   |   | + + +--- + + + + + +```bash +docker-compose up -d --build +``` + +>   + +--- + + + + + +1. **Installer les dépendances** : + ```bash + yarn install + yarn add balena-cloud-apps # Si non déjà présent + ``` +2. **Configurer les variables** : + - Éditez `balena.yml` et `.env` (ex: `armhf.env` pour Raspberry Pi 32 bits). + - Exemple de `balena.yml` : + ```yaml + name: mon-projet + type: sw + version: 1.0.0 + ``` + + + +```bash +# Pour ARM 32 bits (Raspberry Pi 3/4) +./deploy.sh armhf --balena + +# Pour ARM 64 bits +./deploy.sh aarch64 --balena + +# Pour x86_64 (PC) +./deploy.sh x86_64 --balena +``` + +>   +> +> - `--balena` : Déploie sur Balena Cloud. +> - `--local` : Construit localement (pour tests). +> - `--push` : Force le push même en cas d'erreur de cross-build. +> - `--build-deps` : Rebuild les dépendances Docker. + + + +  + +``` +[Error] The command 'cross-build-start' returned a non-zero code: 1 +``` + +  + +```bash +./deploy.sh aarch64 --balena --push +``` + + + +1. Forkez le dépôt. +2. Cliquez sur **"Deploy with Balena"** dans le README. +3. Sélectionnez une **fleet** et déployez. + +--- + + + +1. **Convertir docker-compose en Kubernetes** : + ```bash + ./kompose.sh up + ``` +2. **Vérifier le déploiement** : + ```bash + kubectl get pods + kubectl get services + ``` + +>   + +--- + + + + + +1. Éditez `deployment/images/primary/Dockerfile.template`. +2. **Blocs de cross-build pour ARM** (Balena) : + ```dockerfile + # [ "cross-build-start" ] + RUN apt-get update && apt-get install -y \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + # [ "cross-build-end" ] + ``` +3. **Rebuild l'image** : + ```bash + ./deploy.sh aarch64 --local --build-deps + ``` + + + +```bash +# Mettre à jour la version (ex: 1.0.0 -> 1.0.1) +npm version patch # ou minor/major + +# Pousser les tags +git push --tags +git push +``` + +>   + +--- + + + + +|   |   |   | +| ------ | ------ | ------ | +|   |   |   | +|   |   |   | +|   |   |   | +|   |   |   | + + +>   + +--- + + + + + + +|   |   |   | +| ------ | ------ | ------ | +|   |   |   | +|   |   |   | +|   |   |   | +|   |   |   | +|   |   |   | + + + + +```bash +# Voir les logs de tous les services +docker-compose logs -f + +# Entrer dans un conteneur (ex: php-fpm) +docker-compose exec php-fpm bash + +# Vérifier les variables d'environnement +docker-compose exec php-fpm env + +# Lister les conteneurs en cours +docker ps + +# Nettoyer les volumes et réseaux +docker system prune -a --volumes +``` + +--- + + + +1. **Forker** le dépôt sur GitHub. +2. **Créer une branche** pour votre fonctionnalité : + ```bash + git checkout -b feature/ma-fonctionnalité + ``` +3. **Commiter** vos changements : + ```bash + git commit -m "Ajout de ma fonctionnalité" + ``` +4. **Pousser** vers votre fork : + ```bash + git push origin feature/ma-fonctionnalité + ``` +5. **Ouvrir une Pull Request** sur le dépôt principal. + +--- + + + +  + +  + +> \ No newline at end of file diff --git a/README.md b/README.md index d2388df98..fdc714c22 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A Cake2PHP website ================== -[![TravisCI Status](https://app.travis-ci.com/b23prodtm/acake2php.svg?token=VkN3AkpvB5yVGfXx1qj5&branch=development)](https://travis-ci.com/b23prodtm/acake2php) +[![Build Status](https://github.com/b23prodtm/acake2php/actions/workflows/build.yml/badge.svg)](https://github.com/b23prodtm/acake2php/actions/workflows/build.yml) [![CircleCI Status](https://circleci.com/gh/b23prodtm/acake2php.svg?style=svg)](https://app.circleci.com/pipelines/github/b23prodtm/acake2php) > [The PHP-CMS eShop project was at the origin of this application](https://sourceforge.net/projects/pohse/) @@ -33,6 +33,15 @@ Based on [Balena engine](http://www.balena.io). See more about [NodeJs dependenc [![balena deploy button](https://www.balena.io/deploy.svg)](https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/b23prodtm/acake2php) +Define Environment Secrets: + + - MYSQL_ROOT_PASSWORD=A-roOt!-Password + - MYSQL_USER=someUser + - MYSQL_PASSWORD=SomePassword + - MASTER_PASSWORD=AdminPassword + +MASTER_PASSWORD_HASH as described further in this file. + Softwares --------- To deploy a server or onto a container manager like docker, you need at least a developer environment with the following software: @@ -93,17 +102,18 @@ More [common issues](#common-issues) #### Generate new administrator password -To sign in with staff rights, at http://localhost/admin/index.php, somebody needs a unique password stored in `GET_HASH_PASSWORD`. One way to generate this hashed password with "hashed“ encryption and setup: +To sign in with staff rights, at http://localhost/admin/index.php, somebody needs a unique password stored in `MASTER_PASSWORD_HASH`. One way to generate this hashed password with "hashed“ encryption and setup: ./configure.sh -p -s -To regenerate or read the current password hash again, simply browse to http://localhost/php-cms/e13/etc/getHashPassword.php +To regenerate or read the current password hash again, simply browse to `http://localhost/php-cms/e13/etc/getHashPassword.php` or use php: - HASH_PASSWORD= + php app/webroot/php-cms/e13/etc/getHashPassword.php -p password -s SomeSalt -f output.txt -or: +Then copy it to environment variable `MASTER_PASSWORD_HASH` in runtime context: - GET_HASH_PASSWORD= + MASTER_PASSWORD_HASH= + One of them must be stored in the local server environment as a system readable variable. @@ -147,11 +157,11 @@ Plugins are registered in both _packages.json_ and _app/composer.json_ [Packagist](https://packagist.org). - ```. Plugins home folder: app/Vendor/ app/Plugin// + * **Templates files** Setup environment variables, build files, ready for deployment with any of the available targets: diff --git a/Scripts/bootargs.sh b/Scripts/bootargs.sh index 5b073e3e1..5edcfb851 100755 --- a/Scripts/bootargs.sh +++ b/Scripts/bootargs.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash -TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +TOPDIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) incBOOT_ARGS=${incBOOT_ARGS:-0}; if [ "$incBOOT_ARGS" -eq 0 ]; then export incBOOT_ARGS=1 + log_daemon_msg "PRODUCTION MODE $0...: $*" [[ ! -e "$TOPDIR/.env" || ! -e "$TOPDIR/common.env" ]] \ - && printf "Missing environment configuration, please run ./update-templates.sh\n" && exit 1 + && printf "Missing environment configuration files.\n" && exit 1 eval "$(cat "$TOPDIR/.env" "$TOPDIR/common.env" | awk 'BEGIN{ FS="\n" }{ print "export " $1 }')" fi diff --git a/Scripts/bootstrap.sh b/Scripts/bootstrap.sh index 7ae2eac7e..b98c7f57d 100755 --- a/Scripts/bootstrap.sh +++ b/Scripts/bootstrap.sh @@ -1,34 +1,24 @@ #!/usr/bin/env bash set -e -TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +TOPDIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) # shellcheck source=lib/logging.sh . "${TOPDIR}/Scripts/lib/logging.sh" -# shellcheck source=lib/shell_prompt.sh -. "${TOPDIR}/Scripts/lib/shell_prompt.sh" -runner=$(parse_arg "-[rR]+|--runner" "$@") -pargs=$(parse_arg_trim "-[rR]+|--runner" "$@") -slogger -st "$0" "Auto configuration..." -#; hash file that is stored in webroot to allow administrator privileges -if [ -z "${GET_HASH_PASSWORD:-}" ] && [ -z "$runner" ]; then - hash="$TOPDIR/${MYPHPCMS_DIR}/e13/etc/export_hash_password.sh" - while [ ! -f "$hash" ]; do - shell_prompt "$TOPDIR/Scripts/config_etc_pass.sh" "define a value for missing GET_HASH_PASSWORD" "${DEBIAN_FRONTEND:-}" - done - # shellcheck source=app/webroot/php-cms/e13/etc/export_hash_password.sh - . "$hash" -fi -# shellcheck disable=SC2154 -echo -e "${nc}Password ${green}${GET_HASH_PASSWORD}${nc}" +parse_args_lazy "$@" < -s= [-f=]" && exit 1 -TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +set -e +#; [ $# -lt 1 ] && echo "Usage: $0 -p -s [-f ]" && exit 1 +TOPDIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) # shellcheck source=lib/logging.sh . "$TOPDIR/Scripts/lib/logging.sh" -pwd=$(pwd) -pass="" -hash="" -hash_file="" -MYPHPCMS_DIR=${MYPHPCMS_DIR:-'app/webroot/php-cms'} -dir="$TOPDIR/$MYPHPCMS_DIR/e13/etc/" -cd "$dir" || log_failure_msg "No such directory %s\n" "$dir" # passed args from shell_prompt -while [ "$#" -gt 0 ]; do case $1 in - -[pP]* ) - parse_arg_export "pass" "some password" "$@";; - -[sS]* ) - parse_arg_export "hash" "some hash" "$@";; - -[fF]* ) - parse_arg_export "hash_file" "a filename.sh" "$@";; - *);; -esac; shift; done +parse_args_lazy "$@" <]..." && exit 1 -TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +TOPDIR=$(cd "$(dirname "$(dirname "${BASH_SOURCE[0]}")")" && pwd) sqlversion="5.7" # shellcheck source=lib/logging.sh . "${TOPDIR}/Scripts/lib/logging.sh" diff --git a/Scripts/configure_path.sh b/Scripts/configure_path.sh index ca57623de..74e2b989b 100755 --- a/Scripts/configure_path.sh +++ b/Scripts/configure_path.sh @@ -1,10 +1,25 @@ #!/usr/bin/env bash TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +# shellcheck source=Scripts/lib/logging.sh +. "$TOPDIR/Scripts/lib/logging.sh" +parse_args_lazy "$@" <"; fi -touch site.conf -echo -e " - - ServerName $1 - DocumentRoot /var/www/html/app/webroot/ - - - Options FollowSymLinks - AllowOverride All - Require all granted - -" >> site.conf -cat site.conf -mv site.conf docker/apache/. diff --git a/Scripts/fooargs.sh b/Scripts/fooargs.sh index 549267cd8..e2157495a 100755 --- a/Scripts/fooargs.sh +++ b/Scripts/fooargs.sh @@ -2,8 +2,12 @@ incFOO_ARGS=${incFOO_ARGS:-0}; if [ "$incFOO_ARGS" -eq 0 ]; then export incFOO_ARGS=1 set -eu - docker=$(parse_arg "--docker" "$@") - travis=$(parse_arg "--travis" "$@") +parse_args_lazy "$@" < instead - [ ! "$travis" ] && export MYSQL_PASSWORD=${MYSQL_PASSWORD:-'maria-abc'} + export MYSQL_PASSWORD=${MYSQL_PASSWORD:-'maria-abc'} export MYSQL_DATABASE=${MYSQL_DATABASE:-'aria_db'} #; To override, use shell parameter -dbase= instead export MYSQL_ROOT_USER=${MYSQL_ROOT_USER:-'root'} #; To override, shell parameter -p= instead - [ ! "$travis" ] && export MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-'mariadb'} + export MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-'mariadb'} #; To override, use shell parameter -tbase= instead export TEST_MYSQL_DATABASE=${TEST_MYSQL_DATABASE:-'test'} - export HASH_PASSWORD=password - if [ -n "$(parse_arg "-[vV]+|--verbose" "$@")" ]; then + export MASTER_PASSWORD=password + if [ ${#verbose} -gt 0 ]; then echo "MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}" echo "MYSQL_PASSWORD=${MYSQL_PASSWORD}" fi diff --git a/Scripts/lib/logging.sh b/Scripts/lib/logging.sh index 5eaa1d3dc..99563da20 100644 --- a/Scripts/lib/logging.sh +++ b/Scripts/lib/logging.sh @@ -3,39 +3,38 @@ TOPDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) . init_functions . # shellcheck source=lib/parsing.sh . "${TOPDIR}/Scripts/lib/parsing.sh" -runner=$(parse_arg "-[rR]+|--runner" "$@") -pargs=$(parse_arg_trim "-[rR]+|--runner" "$@") -if [ -n "$runner" ]; then - slogger -st "$0" "Bootargs...: ${pargs}" - export CAKEPHP_DEBUG_LEVEL=1 - # shellcheck source=bootargs.sh - . "${TOPDIR}/Scripts/bootargs.sh" "$@" -else - slogger -st "$0" "Locally Testing values, bootargs...: ${pargs}" - export CAKEPHP_DEBUG_LEVEL=2 - # shellcheck source=fooargs.sh - . "${TOPDIR}/Scripts/fooargs.sh" "$@" -fi -#; Make logs folders available -mkdir -p "${MYPHPCMS_LOG}" -function new_cake_log_long() { - while [ "$#" -gt 0 ]; do case $1 in - --travis) - new_log "$TOPDIR/${MYPHPCMS_LOG}" "travis.${TRAVIS_BUILD_NUMBER:-'TRAVIS_BUILD_NUMBER'}.log" - return;; - --docker) - new_log "$TOPDIR/${MYPHPCMS_LOG}" "docker.log" - return;; - -[oO]+|--openshift) - new_log "$TOPDIR/${MYPHPCMS_LOG}" "openshift.log" - return;; - *) - ;; - esac; shift; done - new_log "$TOPDIR/${MYPHPCMS_LOG}" "acake2php.log" +parse_args_lazy "$@" < - " \ - && exit 1 - evar="$1" - desc="$2" - shift 2 - zval=$(echo "$@" | awk 'BEGIN{ FS="[ =]+" }{ print $2 }') - while true; do case "$zval" in - "") - read -r -s -p " -Please, enter the $desc value now: -" zval - ;; - *) - break;; - esac; done - eval "export ${evar}=${zval}" -} -#; export -f parse_arg_export -parse_arg_exists() { - [ $# -eq 1 ] && return - [ $# -lt 2 ] && printf "%s\n" \ - "Usage: ${FUNCNAME[0]} list-or-\$*" \ - "Prints the argument index that's matched in the regex-arg-case (~ patn|patn2)" \ - && exit 1 - # add mask case |---- to avoid unknown state in split when $# = 1 - export arg_case="$1|----" - shift - echo "$@" | awk 'BEGIN{FS=" "; ORS=" "; split(ENVIRON["arg_case"], a, "|")} { - n=-1 - for(i in a) { - for(f=1; f<=NF; f++) { - if($f ~ a[i]) { - n=f - break - } - if(n != -1) break - } - } - } - END { - if(n >= 0) print n - }' - unset arg_case -} -#; export -f parse_arg_exists() -parse_arg_trim() { - [ $# -eq 1 ] && return - [ $# -lt 2 ] && printf "%s\n" \ - "Usage: ${FUNCNAME[0]} list-or-\$*" \ - "Prints the argument list that's not matched in the regex-arg-case (~ patn|patn2)" \ - && exit 1 - export arg_case=$1 - shift - echo "$@" | awk 'BEGIN{FS=" "; ORS=" "; split(ENVIRON["arg_case"], a, "|"); n[0]=""} { - for(f=1;f<=NF;f++) { - n[f]=$f - for(i in a) { - if($f ~ a[i]) n[f]="" - } - } - } END{ - for(f in n) { - if(n[f] != "") print n[f] - } - }' -} -#; export -f parse_dom_host() -### ------------------------- -# Only short options (e.g. -a -f) are supported. -# Long options must be transformed into short ones before. -# When an argument --name=Bob passes, transform into -n Bob: +# parse_args: +# Parse arguments inside a function without touching globals. +# Supports: +# - Flags: -v --verbose +# - Key/Value: -o X --output=X --output X +# - Positionals: stored in "$@" # -# set -- $(echo "$1" \ -# | awk 'BEGIN{ FS="[ =]+" }{ print "-n " $2 }') "${@:2}" -# parse_and_export -n NAME "Set user name" "${@:2}" +# Usage inside a function: +# parse_args "$@" <&2 + return 1 + ;; + esac + done + + # printf "Flags list: %s\n" "$_flags" >&2 + # printf "Options list: %s\n" "$_opts" >&2 + + # Initialize all variables to empty/0 + for entry in $_flags; do + var=${entry##*:} + eval "$var=0" + done + for entry in $_opts; do + var=${entry##*:} + eval "$var=''" + done + + # Now parse actual arguments + _positional=""; local _s="" + while [ $# -gt 0 ]; do + arg=$1 + shift + + case "$arg" in + --*=*) + key=${arg%%=*} + val=${arg#*=} + ;; + -*) + key=$arg + val="" + ;; + *) + _positional="${_positional}${_s}${arg}" + _s=" " + continue + ;; + esac + + # Match flags + matched=0 + for entry in $_flags; do + short=${entry%%:*} + rest=${entry#*:} + long=${rest%%:*} + var=${rest#*:} + + if [ "$key" = "$short" ] || [ "$key" = "$long" ]; then + eval "$var=1" + matched=1 + break + fi + done + [ "$matched" -eq 1 ] && continue + + # Match options + for entry in $_opts; do + short=${entry%%:*} + rest=${entry#*:} + long=${rest%%:*} + var=${rest#*:} + + if [ "$key" = "$short" ] || [ "$key" = "$long" ]; then + if [ -z "$val" ]; then + [ $# -eq 0 ] && { + printf "Missing value for %s\n" "$key" >&2 + return 1 + } + val=$1 + shift + fi + eval "$var=\$val" + matched=1 + break + fi + done + + if [ "$matched" -eq 0 ]; then + _positional="${_positional}${_s}${arg}" + _s=" " + fi + done + set -- "$_positional" +} +#; export -f parse_args_lazy() + +function parse_args() { + parse_args_lazy "$@" + if [ "${#_positional}" -gt 0 ]; then printf "%s\n" "Unkown argument(s): $_positional" >&2; exit 1; fi + unset _positional +} +#; export -f parse_args() + +# Trim arguments inside a function without touching globals and returns it +# Supports: +# - Flags: -v --verbose +# - Key/Value: -o X --output=X --output X +# - Resting args: stored in "$@" # +function trim_args() { + parse_args_lazy "$@" + echo -e "$_positional" + unset _positional +} +#; export -f trim_args() + +# parse_and_export: +# set and export variables from arg list or PROMPT if the value is not set +# Supports short and long argument: +# OPTION -s "--longOption" "arg-list" +# Usage examples: +# parse_and_export FILENAME -f "--file" $* (args: "-f afile.txt") +# parse_and_export FILENAME -f "--file" $* (args: "-s --file=out") +# After parsing: +# FILENAME=afile.txt +# FILENAME=out parse_and_export() { [ $# -lt 4 ] && printf "%s\n" \ - "Usage: ${FUNCNAME[0]} <-arg list> " \ + "Usage: ${FUNCNAME[0]} <-arg list> " >&2 \ && exit 1 - optstr=$1 - evar=$2 - desc=$3 + local evar=$1 + local flag=$2 + local long=$3 shift 3 - unset OPTIND - while [ "$#" -gt 0 ]; do - OPTIND=$(("$(parse_arg_exists "$optstr" "$@")" +1)) - if [ "$OPTIND" -gt 0 ]; then - shift $((OPTIND -1)) - parse_arg_export "$evar" "${desc}" "-${optstr}" "${1:-}" - [ -n "${1:-}" ] && OPTIND=$((OPTIND +1)) - break - fi - shift - done - export OPTIND + parse_args_lazy "$@" < [-y|n]" "${FUNCNAME[0]}" && exit 1 + [ $# -lt 2 ] && printf "Usage: %s