From b76510accc3b0b29e8b389f6f8c3a059a4076259 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Fri, 22 Aug 2025 19:38:13 +1000 Subject: [PATCH 1/8] Attempting to build prod env --- Dockerfile.prod | 48 ++++++++++++++++++++ Dockerfile.prod.db | 12 +++++ Dockerfile.prod.go | 20 ++++++++ app/config/core.php | 20 ++++++-- conf/nginx-site.prod.conf | 26 +++++++++++ deploy/deploy-prod.sh | 78 +++++++++++++++++++++++++++++++ docker-compose.prod.yml | 96 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 Dockerfile.prod create mode 100644 Dockerfile.prod.db create mode 100644 Dockerfile.prod.go create mode 100644 conf/nginx-site.prod.conf create mode 100644 deploy/deploy-prod.sh create mode 100644 docker-compose.prod.yml diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 00000000..254d6f58 --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,48 @@ +# Use the official PHP 5.6 Apache image for classic mod_php +FROM php:5.6-apache + +# Install required system libraries and PHP extensions for CakePHP +RUN sed -i 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' /etc/apt/sources.list && \ + sed -i 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' /etc/apt/sources.list && \ + sed -i '/stretch-updates/d' /etc/apt/sources.list && \ + echo 'Acquire::AllowInsecureRepositories "true";' > /etc/apt/apt.conf.d/99allow-insecure && \ + echo 'Acquire::AllowDowngradeToInsecureRepositories "true";' >> /etc/apt/apt.conf.d/99allow-insecure && \ + apt-get update && \ + apt-get install --allow-unauthenticated -y libc-client2007e-dev libkrb5-dev libpng-dev libjpeg-dev libfreetype6-dev libcurl4-openssl-dev libxml2-dev libssl-dev libmcrypt-dev libicu-dev && \ + docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ && \ + docker-php-ext-configure imap --with-kerberos --with-imap-ssl && \ + docker-php-ext-install mysql mbstring gd curl imap + +# Set environment variables. +ENV HOME /root + +# Define working directory. +WORKDIR /root + +ARG COMMIT +ENV COMMIT_SHA=${COMMIT} + +EXPOSE 80 + +# Copy vhost config to Apache's sites-available +ADD conf/apache-vhost.conf /etc/apache2/sites-available/cmc-sales.conf +ADD conf/ripmime /bin/ripmime + +RUN chmod +x /bin/ripmime \ + && a2ensite cmc-sales \ + && a2dissite 000-default \ + && a2enmod rewrite \ + && a2enmod headers + +# Copy site into place. +ADD . /var/www/cmc-sales +ADD app/config/database.php /var/www/cmc-sales/app/config/database.php +RUN mkdir /var/www/cmc-sales/app/tmp +RUN mkdir /var/www/cmc-sales/app/tmp/logs +RUN chmod -R 755 /var/www/cmc-sales/app/tmp +RUN chmod +x /var/www/cmc-sales/run_vault.sh + +# Ensure CakePHP tmp directory is writable by web server +RUN chmod -R 777 /var/www/cmc-sales/app/tmp +# By default, simply start apache. +CMD /usr/sbin/apache2ctl -D FOREGROUND diff --git a/Dockerfile.prod.db b/Dockerfile.prod.db new file mode 100644 index 00000000..b978f785 --- /dev/null +++ b/Dockerfile.prod.db @@ -0,0 +1,12 @@ +# Use the same content as Dockerfile.stg.db, but for prod. If you want to customize, edit this file. +# For now, copy the staging DB Dockerfile and adjust as needed for production. +FROM mysql:5.7 + +ENV MYSQL_ROOT_PASSWORD=secureRootPassword +ENV MYSQL_DATABASE=cmc +ENV MYSQL_USER=cmc +ENV MYSQL_PASSWORD=xVRQI&cA?7AU=hqJ!%au + +EXPOSE 3306 + +VOLUME ["/var/lib/mysql"] diff --git a/Dockerfile.prod.go b/Dockerfile.prod.go new file mode 100644 index 00000000..fbfa0623 --- /dev/null +++ b/Dockerfile.prod.go @@ -0,0 +1,20 @@ +FROM golang:1.24-alpine AS builder + +RUN apk add --no-cache git +WORKDIR /app +COPY go-app/go.mod go-app/go.sum ./ +RUN go mod download +COPY go-app/ . +RUN go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest +RUN sqlc generate +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server cmd/server/main.go + +FROM alpine:latest +RUN apk --no-cache add ca-certificates +WORKDIR /root/ +COPY --from=builder /app/server . +COPY go-app/templates ./templates +COPY go-app/static ./static +COPY go-app/.env.example .env +EXPOSE 8082 +CMD ["./server"] diff --git a/app/config/core.php b/app/config/core.php index 6baf049a..618ffba2 100644 --- a/app/config/core.php +++ b/app/config/core.php @@ -62,11 +62,21 @@ $host = $_SERVER['HTTP_HOST']; // 'timeout' => '30', // 'host' => '172.17.0.1')); -// SMTP settings for staging -Configure::write('smtp_settings', array( - 'port' => '1025', - 'timeout' => '30', - 'host' => 'mailpit')); + +// SMTP settings for production +if (in_array($host, $production_hosts)) { + Configure::write('smtp_settings', array( + 'port' => '25', + 'timeout' => '30', + 'host' => '172.17.0.1' + )); +} else { + // SMTP settings for staging + Configure::write('smtp_settings', array( + 'port' => '1025', + 'timeout' => '30', + 'host' => 'mailpit')); +} // Mailhog SMTP settings for local development diff --git a/conf/nginx-site.prod.conf b/conf/nginx-site.prod.conf new file mode 100644 index 00000000..8c1a129e --- /dev/null +++ b/conf/nginx-site.prod.conf @@ -0,0 +1,26 @@ +server { + server_name cmclocal; + auth_basic_user_file /etc/nginx/userpasswd; + auth_basic "Restricted"; + location /go/ { + proxy_pass http://cmc-prod-go:8082; + proxy_read_timeout 300s; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location / { + proxy_pass http://cmc-prod-php:80; + proxy_read_timeout 300s; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + listen 0.0.0.0:80; +# include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot +# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot + +} diff --git a/deploy/deploy-prod.sh b/deploy/deploy-prod.sh new file mode 100644 index 00000000..c5aeef47 --- /dev/null +++ b/deploy/deploy-prod.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Deploy production environment for cmc-sales + +# Usage: ./deploy-prod.sh [--no-cache] + +USE_CACHE=true +for arg in "$@"; do + if [[ "$arg" == "--no-cache" ]]; then + USE_CACHE=false + echo "No cache flag detected: will rebuild images without cache." + fi +done +if [[ "$USE_CACHE" == "true" ]]; then + echo "Using cached layers for docker build." +fi + +echo "Starting production deployment for cmc-sales..." +echo "Setting variables..." +SERVER="cmc-sales" +REPO="git@code.springupsoftware.com:cmc/cmc-sales.git" +BRANCH="master" +PROD_DIR="cmc-sales-prod" + +echo "Connecting to server $SERVER via SSH..." +# Pass variables into SSH session +ssh $SERVER \ + "SERVER=$SERVER REPO='$REPO' BRANCH='$BRANCH' PROD_DIR='$PROD_DIR' USE_CACHE='$USE_CACHE' bash -s" << 'ENDSSH' + set -e + echo "Connected to $SERVER." + cd /home/cmc + # Clone or update production branch + if [ -d "$PROD_DIR" ]; then + echo "Updating existing production directory $PROD_DIR..." + cd "$PROD_DIR" + git fetch origin + git checkout $BRANCH + git reset --hard origin/$BRANCH + else + echo "Cloning repository $REPO to $PROD_DIR..." + git clone -b $BRANCH $REPO $PROD_DIR + cd "$PROD_DIR" + fi + + # Create .env file for go-app if it doesn't exist + ENV_PATH="/home/cmc/$PROD_DIR/go-app/.env" + echo "(Re)creating .env file for go-app..." + cat > "$ENV_PATH" <<'ENVEOF' +# Database configuration +DB_HOST=db +DB_PORT=3306 +DB_USER=cmc +DB_PASSWORD=xVRQI&cA?7AU=hqJ!%au +DB_NAME=cmc + +# Root database password (for dbshell-root) +DB_ROOT_PASSWORD=secureRootPassword + +# Environment variables for Go app mail configuration +SMTP_HOST="172.17.0.1" +SMTP_PORT=25 +SMTP_USER="" +SMTP_PASS="" +SMTP_FROM="CMC Sales " +ENVEOF + + if [[ "$USE_CACHE" == "false" ]]; then + echo "Building and starting docker compose for production (no cache)..." + docker compose -f docker-compose.prod.yml build --no-cache + docker compose -f docker-compose.prod.yml up -d --remove-orphans + else + echo "Building and starting docker compose for production (using cache)..." + docker compose -f docker-compose.prod.yml build + docker compose -f docker-compose.prod.yml up -d --remove-orphans + fi + + echo "Checking running containers..." + echo "Production deployment complete." +ENDSSH diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..cc3af45d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,96 @@ +services: + nginx: + image: nginx:latest + container_name: cmc-prod-nginx + hostname: nginx-prod + ports: + - "80:80" # Production nginx on standard port + volumes: + - ./conf/nginx-site.conf:/etc/nginx/conf.d/cmc.conf + - ./userpasswd:/etc/nginx/userpasswd:ro + depends_on: + - cmc-prod-php + restart: unless-stopped + networks: + - cmc-prod-network + + cmc-prod-php: + build: + context: . + dockerfile: Dockerfile.prod + container_name: cmc-prod-php + environment: + MAIL_HOST: 172.17.0.1 + MAIL_PORT: 25 + DB_HOST: db + DB_PORT: 3306 + DB_USER: cmc + DB_PASSWORD: xVRQI&cA?7AU=hqJ!%au + DB_NAME: cmc + volumes: + - ./userpasswd:/etc/nginx/userpasswd:ro + networks: + - cmc-prod-network + restart: unless-stopped + depends_on: + - db + + cmc-prod-go: + build: + context: . + dockerfile: Dockerfile.prod.go + container_name: cmc-prod-go + environment: + DB_HOST: db + DB_PORT: 3306 + DB_USER: cmc + DB_PASSWORD: xVRQI&cA?7AU=hqJ!%au + DB_NAME: cmc + PORT: 8082 + SMTP_HOST: 172.17.0.1 + SMTP_PORT: 25 + SMTP_USER: "" + SMTP_PASS: "" + SMTP_FROM: "sales@cmctechnologies.com.au" + ports: + - "8082:8082" + volumes: + - /var/www/cmc-sales/app/webroot/pdf:/root/webroot/pdf:ro + networks: + - cmc-prod-network + restart: unless-stopped + depends_on: + - db + + db: + build: + context: . + dockerfile: Dockerfile.prod.db + container_name: cmc-prod-db + environment: + MYSQL_ROOT_PASSWORD: secureRootPassword + MYSQL_DATABASE: cmc + MYSQL_USER: cmc + MYSQL_PASSWORD: xVRQI&cA?7AU=hqJ!%au + volumes: + - db_data:/var/lib/mysql + ports: + - "3306:3306" + networks: + - cmc-prod-network + + mailpit: + image: axllent/mailpit:latest + container_name: mailpit + ports: + - "8025:8025" # Mailpit web UI + - "1025:1025" # SMTP + networks: + - cmc-prod-network + restart: unless-stopped + +networks: + cmc-prod-network: + +volumes: + db_data: From 161f4041298947186b936dae1b9cde465292a5df Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Fri, 22 Aug 2025 19:42:54 +1000 Subject: [PATCH 2/8] Adding deploy script --- deploy/deploy-prod.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 deploy/deploy-prod.sh diff --git a/deploy/deploy-prod.sh b/deploy/deploy-prod.sh old mode 100644 new mode 100755 From a47d1e9e514faead4cfce61abae50cb37fba6c99 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Fri, 22 Aug 2025 20:37:15 +1000 Subject: [PATCH 3/8] Removing mailpit from prod --- deploy/deploy-prod.sh | 2 +- docker-compose.prod.yml | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/deploy/deploy-prod.sh b/deploy/deploy-prod.sh index c5aeef47..de4e8089 100755 --- a/deploy/deploy-prod.sh +++ b/deploy/deploy-prod.sh @@ -18,7 +18,7 @@ echo "Starting production deployment for cmc-sales..." echo "Setting variables..." SERVER="cmc-sales" REPO="git@code.springupsoftware.com:cmc/cmc-sales.git" -BRANCH="master" +BRANCH="prod" PROD_DIR="cmc-sales-prod" echo "Connecting to server $SERVER via SSH..." diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index cc3af45d..60089356 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -79,16 +79,6 @@ services: networks: - cmc-prod-network - mailpit: - image: axllent/mailpit:latest - container_name: mailpit - ports: - - "8025:8025" # Mailpit web UI - - "1025:1025" # SMTP - networks: - - cmc-prod-network - restart: unless-stopped - networks: cmc-prod-network: From 9e1c5e95f0e26f698b91a53ac893f5503edd4aba Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Fri, 22 Aug 2025 20:54:50 +1000 Subject: [PATCH 4/8] Changing ports --- docker-compose.prod.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 60089356..7b4880ae 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -4,7 +4,7 @@ services: container_name: cmc-prod-nginx hostname: nginx-prod ports: - - "80:80" # Production nginx on standard port + - "8080:80" # Production nginx on port 8080 to avoid conflict volumes: - ./conf/nginx-site.conf:/etc/nginx/conf.d/cmc.conf - ./userpasswd:/etc/nginx/userpasswd:ro @@ -75,7 +75,7 @@ services: volumes: - db_data:/var/lib/mysql ports: - - "3306:3306" + - "3307:3306" networks: - cmc-prod-network From c059b38e805bce8048f364eb48792d8ba24cd596 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Fri, 22 Aug 2025 21:09:16 +1000 Subject: [PATCH 5/8] Changing ports --- docker-compose.prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 7b4880ae..084f5907 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -53,7 +53,7 @@ services: SMTP_PASS: "" SMTP_FROM: "sales@cmctechnologies.com.au" ports: - - "8082:8082" + - "8083:8082" volumes: - /var/www/cmc-sales/app/webroot/pdf:/root/webroot/pdf:ro networks: From 62c44c3fda3469e99afc4f836c4474ca8e084ef4 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Sat, 13 Sep 2025 16:52:10 +1000 Subject: [PATCH 6/8] Updating nginx to point to correct containers --- conf/nginx-site.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/nginx-site.conf b/conf/nginx-site.conf index 0940883b..1765f6f2 100644 --- a/conf/nginx-site.conf +++ b/conf/nginx-site.conf @@ -3,7 +3,7 @@ server { auth_basic_user_file /etc/nginx/userpasswd; auth_basic "Restricted"; location /go/ { - proxy_pass http://cmc-go:8080; + proxy_pass http://cmc-prod-go:8082; proxy_read_timeout 300s; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -11,7 +11,7 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } location / { - proxy_pass http://cmc-php:80; + proxy_pass http://cmc-prod-php:80; proxy_read_timeout 300s; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; From 8866703a9f3464fecf2dbc9b4971f6cb8aa467e5 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Sat, 13 Sep 2025 17:17:23 +1000 Subject: [PATCH 7/8] Pointing docker compose file to correct nginx --- docker-compose.prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 084f5907..1dcc270b 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,7 +6,7 @@ services: ports: - "8080:80" # Production nginx on port 8080 to avoid conflict volumes: - - ./conf/nginx-site.conf:/etc/nginx/conf.d/cmc.conf + - ./conf/nginx-site.prod.conf:/etc/nginx/conf.d/cmc.conf - ./userpasswd:/etc/nginx/userpasswd:ro depends_on: - cmc-prod-php From eb130720b9f93171fec29c1bd295fb4af2cbb319 Mon Sep 17 00:00:00 2001 From: Finley Ghosh Date: Sat, 13 Sep 2025 17:53:26 +1000 Subject: [PATCH 8/8] Updating scripts, changing db dockerfile --- Dockerfile.prod.db | 14 ++------- deploy/scripts/restore_db_from_backup.sh | 31 ++++++++++++++++++-- deploy/scripts/run_all_migrations.sh | 37 +++++++++++++++++++----- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/Dockerfile.prod.db b/Dockerfile.prod.db index b978f785..f18d6a48 100644 --- a/Dockerfile.prod.db +++ b/Dockerfile.prod.db @@ -1,12 +1,4 @@ -# Use the same content as Dockerfile.stg.db, but for prod. If you want to customize, edit this file. -# For now, copy the staging DB Dockerfile and adjust as needed for production. -FROM mysql:5.7 +FROM mariadb:latest -ENV MYSQL_ROOT_PASSWORD=secureRootPassword -ENV MYSQL_DATABASE=cmc -ENV MYSQL_USER=cmc -ENV MYSQL_PASSWORD=xVRQI&cA?7AU=hqJ!%au - -EXPOSE 3306 - -VOLUME ["/var/lib/mysql"] +COPY deploy/scripts/restore_db_from_backup.sh /docker-entrypoint-initdb.d/restore_db_from_backup.sh +RUN chmod +x /docker-entrypoint-initdb.d/restore_db_from_backup.sh diff --git a/deploy/scripts/restore_db_from_backup.sh b/deploy/scripts/restore_db_from_backup.sh index b9c8bafb..67fa941e 100644 --- a/deploy/scripts/restore_db_from_backup.sh +++ b/deploy/scripts/restore_db_from_backup.sh @@ -1,12 +1,37 @@ #!/bin/bash set -e +# Default to staging +TARGET="stg" +for arg in "$@"; do + if [[ "$arg" == "-target" ]]; then + NEXT_IS_TARGET=1 + continue + fi + if [[ $NEXT_IS_TARGET == 1 ]]; then + TARGET="$arg" + NEXT_IS_TARGET=0 + fi +done + +if [[ "$TARGET" == "prod" ]]; then + DB_CONTAINER="cmc-prod-db" + DB_USER="cmc" + DB_PASS="xVRQI&cA?7AU=hqJ!%au" + DB_NAME="cmc" +else + DB_CONTAINER="cmc-db" + DB_USER="cmc" + DB_PASS="xVRQI&cA?7AU=hqJ!%au" + DB_NAME="cmc" +fi + # Sync latest backup from production rsync -avz -e "ssh -i ~/.ssh/cmc-old" --progress cmc@sales.cmctechnologies.com.au:~/backups /home/cmc/ LATEST_BACKUP=$(ls -t /home/cmc/backups/backup_*.sql.gz | head -n1) -echo "Restoring database from latest backup: $LATEST_BACKUP" +echo "Restoring database from latest backup: $LATEST_BACKUP to $TARGET ($DB_CONTAINER)" if [ -f "$LATEST_BACKUP" ]; then - docker cp "$LATEST_BACKUP" cmc-db:/tmp/backup.sql.gz - docker exec cmc-db sh -c "gunzip < /tmp/backup.sql.gz | mariadb -u cmc -p'xVRQI&cA?7AU=hqJ!%au' cmc" + docker cp "$LATEST_BACKUP" "$DB_CONTAINER":/tmp/backup.sql.gz + docker exec "$DB_CONTAINER" sh -c "gunzip < /tmp/backup.sql.gz | mariadb -u $DB_USER -p'$DB_PASS' $DB_NAME" echo "Database restore complete." else echo "No backup file found in /home/cmc/backups. Skipping database restore." diff --git a/deploy/scripts/run_all_migrations.sh b/deploy/scripts/run_all_migrations.sh index 7650581a..b00f79a3 100644 --- a/deploy/scripts/run_all_migrations.sh +++ b/deploy/scripts/run_all_migrations.sh @@ -1,10 +1,31 @@ #!/bin/bash set -e -SQL_DIR="/home/cmc/cmc-sales-staging/go-app/sql/schema" -DB_USER="cmc" -DB_PASS="xVRQI&cA?7AU=hqJ!%au" -DB_NAME="cmc" -DB_HOST="127.0.0.1" +# Default to staging +TARGET="stg" +for arg in "$@"; do + if [[ "$arg" == "-target" ]]; then + NEXT_IS_TARGET=1 + continue + fi + if [[ $NEXT_IS_TARGET == 1 ]]; then + TARGET="$arg" + NEXT_IS_TARGET=0 + fi +done + +if [[ "$TARGET" == "prod" ]]; then + DB_CONTAINER="cmc-prod-db" + DB_USER="cmc" + DB_PASS="xVRQI&cA?7AU=hqJ!%au" + DB_NAME="cmc" + SQL_DIR="/home/cmc/cmc-sales-prod/go-app/sql/schema" +else + DB_CONTAINER="cmc-db" + DB_USER="cmc" + DB_PASS="xVRQI&cA?7AU=hqJ!%au" + DB_NAME="cmc" + SQL_DIR="/home/cmc/cmc-sales-staging/go-app/sql/schema" +fi for sqlfile in "$SQL_DIR"/*.sql; do # Skip files starting with ignore_ @@ -12,8 +33,8 @@ for sqlfile in "$SQL_DIR"/*.sql; do echo "Skipping ignored migration: $sqlfile" continue fi - echo "Running migration: $sqlfile" - docker cp "$sqlfile" cmc-db:/tmp/migration.sql - docker exec cmc-db sh -c "mariadb -u $DB_USER -p'$DB_PASS' $DB_NAME < /tmp/migration.sql" + echo "Running migration: $sqlfile on $TARGET ($DB_CONTAINER)" + docker cp "$sqlfile" "$DB_CONTAINER":/tmp/migration.sql + docker exec "$DB_CONTAINER" sh -c "mariadb -u $DB_USER -p'$DB_PASS' $DB_NAME < /tmp/migration.sql" done echo "All migrations applied."