feat: implement automated environment synchronization and conditional CI/CD deployment workflows

This commit is contained in:
yonastewabe
2026-06-30 15:16:47 +03:00
parent 5e43820efa
commit 2bbb37207e
5 changed files with 90 additions and 39 deletions

View File

@@ -50,7 +50,7 @@ jobs:
SERVICES=() SERVICES=()
NON_DEPLOYABLE_PATTERN="^docs/|^README[.]md$|^DEPLOYMENT[.]md$|^CLAUDE[.]md$|^checkpoint[.]md$|^orgstructure[.]md$|^ITMLS_DB_Design[.]md$|.*[.]md$|^[.]eslintrc|^[.]prettierrc|^[.]editorconfig|^[.]gitignore|^[.]gitattributes|^commitlint[.]config[.]js$" NON_DEPLOYABLE_PATTERN="^docs/|^README[.]md$|^DEPLOYMENT[.]md$|^CLAUDE[.]md$|^checkpoint[.]md$|^orgstructure[.]md$|^ITMLS_DB_Design[.]md$|.*[.]md$|^[.]eslintrc|^[.]prettierrc|^[.]editorconfig|^[.]gitignore|^[.]gitattributes|^commitlint[.]config[.]js$|^scripts/deploy/sync-env-from-server-jenkins[.]sh$"
GLOBAL_PATTERN="^[.]github/|^docker-compose[.]yaml$|^turbo[.]json$|^tsconfig[.]json$|^tsconfig[.]base[.]json$|^pnpm-workspace[.]yaml$|^pnpm-lock[.]yaml$|^package[.]json$|^[.]env([.][a-z]+)?$|^packages/|^local-packages/|^infrastructure/|^scripts/deploy/|^wagon[.][^/]*[.]ts$|^cargo[.][^/]*[.]ts$|^container[.][^/]*[.]ts$|^use-[^/]*[.]ts$|^[^/]*[.]service[.]ts$|^[^/]*[.]entity[.]ts$|^[^/]*-types[.]ts$" GLOBAL_PATTERN="^[.]github/|^docker-compose[.]yaml$|^turbo[.]json$|^tsconfig[.]json$|^tsconfig[.]base[.]json$|^pnpm-workspace[.]yaml$|^pnpm-lock[.]yaml$|^package[.]json$|^[.]env([.][a-z]+)?$|^packages/|^local-packages/|^infrastructure/|^scripts/deploy/|^wagon[.][^/]*[.]ts$|^cargo[.][^/]*[.]ts$|^container[.][^/]*[.]ts$|^use-[^/]*[.]ts$|^[^/]*[.]service[.]ts$|^[^/]*[.]entity[.]ts$|^[^/]*-types[.]ts$"

View File

@@ -39,14 +39,14 @@ services:
args: args:
TURBO_FILTER: "@edr/freight-portal" TURBO_FILTER: "@edr/freight-portal"
APP_PATH: apps/edr-freight-web/portal APP_PATH: apps/edr-freight-web/portal
VITE_API_URL: ${VITE_API_URL:-https://edrfreightapi.triaplc.com/api} VITE_API_URL: ${VITE_API_URL:?VITE_API_URL must be set}
VITE_BASE_API_URL: ${VITE_BASE_API_URL:-https://edrfreightapi.triaplc.com} VITE_BASE_API_URL: ${VITE_BASE_API_URL:?VITE_BASE_API_URL must be set}
VITE_USER_MANAGEMENT_BASE: ${VITE_USER_MANAGEMENT_BASE:-/_um} VITE_USER_MANAGEMENT_BASE: ${VITE_USER_MANAGEMENT_BASE:?VITE_USER_MANAGEMENT_BASE must be set}
secrets: secrets:
- npmrc - npmrc
ports: ports:
- "${FREIGHT_PORTAL_PORT:-5173}:80" - "${FREIGHT_PORTAL_PORT:-5173}:80"
freight-backoffice: freight-backoffice:
build: build:
context: . context: .
@@ -54,14 +54,14 @@ services:
args: args:
TURBO_FILTER: "@edr/freight-backoffice" TURBO_FILTER: "@edr/freight-backoffice"
APP_PATH: apps/edr-freight-web/backoffice APP_PATH: apps/edr-freight-web/backoffice
VITE_API_URL: ${VITE_API_URL:-https://edrfreightapi.triaplc.com/api} VITE_API_URL: ${VITE_API_URL:?VITE_API_URL must be set}
VITE_BASE_API_URL: ${VITE_BASE_API_URL:-https://edrfreightapi.triaplc.com} VITE_BASE_API_URL: ${VITE_BASE_API_URL:?VITE_BASE_API_URL must be set}
VITE_USER_MANAGEMENT_BASE: ${VITE_USER_MANAGEMENT_BASE:-/_um} VITE_USER_MANAGEMENT_BASE: ${VITE_USER_MANAGEMENT_BASE:?VITE_USER_MANAGEMENT_BASE must be set}
secrets: secrets:
- npmrc - npmrc
ports: ports:
- "${FREIGHT_BACKOFFICE_PORT:-5183}:80" - "${FREIGHT_BACKOFFICE_PORT:-5183}:80"
passenger-portal: passenger-portal:
build: build:
context: . context: .
@@ -69,14 +69,14 @@ services:
args: args:
APP_PACKAGE: "@edr/passenger-portal" APP_PACKAGE: "@edr/passenger-portal"
APP_PATH: apps/edr-passenger-web/portal APP_PATH: apps/edr-passenger-web/portal
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:4000} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:?NEXT_PUBLIC_API_URL must be set}
secrets: secrets:
- npmrc - npmrc
ports: ports:
- "${PASSENGER_PORTAL_PORT:-5174}:${PASSENGER_PORTAL_PORT:-5174}" - "${PASSENGER_PORTAL_PORT:-5174}:${PASSENGER_PORTAL_PORT:-5174}"
env_file: env_file:
- apps/edr-passenger-web/portal/.env - apps/edr-passenger-web/portal/.env
passenger-backoffice: passenger-backoffice:
build: build:
context: . context: .
@@ -84,14 +84,14 @@ services:
args: args:
APP_PACKAGE: "@edr/passenger-backoffice" APP_PACKAGE: "@edr/passenger-backoffice"
APP_PATH: apps/edr-passenger-web/backoffice APP_PATH: apps/edr-passenger-web/backoffice
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:4000} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:?NEXT_PUBLIC_API_URL must be set}
secrets: secrets:
- npmrc - npmrc
ports: ports:
- "${PASSENGER_BACKOFFICE_PORT:-5184}:${PASSENGER_BACKOFFICE_PORT:-5184}" - "${PASSENGER_BACKOFFICE_PORT:-5184}:${PASSENGER_BACKOFFICE_PORT:-5184}"
env_file: env_file:
- apps/edr-passenger-web/backoffice/.env - apps/edr-passenger-web/backoffice/.env
payment-api: payment-api:
build: build:
context: . context: .

View File

@@ -2,10 +2,6 @@
ARG TURBO_FILTER=@edr/freight-portal ARG TURBO_FILTER=@edr/freight-portal
ARG APP_PATH=apps/edr-freight-web/portal ARG APP_PATH=apps/edr-freight-web/portal
ARG VITE_API_URL=https://edrfreightapi.triaplc.com/api
ARG VITE_BASE_API_URL=https://edrfreightapi.triaplc.com
ARG VITE_USER_MANAGEMENT_BASE=/_um
ARG NEXT_PUBLIC_API_URL=http://localhost:4000
FROM node:24.15.0-alpine AS base FROM node:24.15.0-alpine AS base
RUN apk add --no-cache libc6-compat RUN apk add --no-cache libc6-compat

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Sync .env files from the self-hosted runner filesystem into the repo.
# Jenkins variant — exports variables as KEY=VALUE lines into $CI_ENV_FILE,
# which the Jenkinsfile loads with readProperties + withEnv. Jenkins has no
# equivalent of GitHub Actions' $GITHUB_ENV, and each `sh` step runs in its
# own process, so this file is the hand-off point between stages.
#
# Usage:
# PROJECT=edr-freight BRANCH=main CI_ENV_FILE=/tmp/passenger-api.env \
# ./scripts/deploy/sync-env-from-server-jenkins.sh passenger-api
#
# Server layout (one file per service):
# /home/user/environmen/<project>/<branch-slug>/freight-api.env
# /home/user/environmen/<project>/<branch-slug>/freight-portal.env
set -euo pipefail
DEPLOY_USER="${DEPLOY_USER:-tria}"
BRANCH="${BRANCH:?BRANCH is required}"
BRANCH_SLUG="${BRANCH_SLUG:-$(echo "${BRANCH}" | tr "[:upper:]" "[:lower:]" | sed -E "s/[^a-z0-9]+/-/g; s/^-+//; s/-+$//")}"
ENV_ROOT="${ENV_ROOT:-/home/${DEPLOY_USER}/environment/edr/${BRANCH_SLUG}/${PROJECT:?PROJECT is required}}"
CI_ENV_FILE="${CI_ENV_FILE:?CI_ENV_FILE is required (e.g. \${WORKSPACE}/.ci-env/<service>.env)}"
if [[ ! -d "${ENV_ROOT}" ]]; then
echo "Environment directory not found: ${ENV_ROOT}" >&2
exit 1
fi
echo "Using environment directory: ${ENV_ROOT}"
mkdir -p "$(dirname "${CI_ENV_FILE}")"
: > "${CI_ENV_FILE}"
declare -A SERVICE_ENV_TARGET=(
["freight-api"]="apps/edr-freight-api/.env"
["freight-portal"]="apps/edr-freight-web/portal/.env"
["freight-backoffice"]="apps/edr-freight-web/backoffice/.env"
["passenger-api"]="apps/edr-passenger-api/.env"
["passenger-portal"]="apps/edr-passenger-web/portal/.env"
["passenger-backoffice"]="apps/edr-passenger-web/backoffice/.env"
["payment-api"]="apps/edr-payment-api/.env"
)
for service in "$@"; do
src="${ENV_ROOT}/${service}.env"
dest="${SERVICE_ENV_TARGET[${service}]:-}"
if [[ -z "${dest}" ]]; then
echo "Unknown service: ${service}" >&2
exit 1
fi
if [[ ! -f "${src}" ]]; then
echo "Missing env file: ${src}" >&2
exit 1
fi
mkdir -p "$(dirname "${dest}")"
cp "${src}" "${dest}"
echo "Synced ${src} -> ${dest}"
port_value=$(sed -n -E 's/^[[:space:]]*PORT[[:space:]]*=[[:space:]]*"?([^"#]+)"?[[:space:]]*(#.*)?$/\1/p' "${src}" | head -n1 | tr -d '[:space:]')
if [[ -z "${port_value}" ]]; then
echo "Missing required PORT in env file: ${src}" >&2
exit 1
fi
service_var=$(echo "${service}" | tr '[:lower:]-' '[:upper:]_')
echo "${service_var}_PORT=${port_value}" >> "${CI_ENV_FILE}"
echo "Exported ${service_var}_PORT from ${src}"
# Forward NEXT_PUBLIC_* and VITE_* vars so docker compose build can inject them as build args.
grep -E '^[[:space:]]*(NEXT_PUBLIC_|VITE_)[A-Za-z0-9_]+=' "${src}" \
| sed -E 's/^[[:space:]]*//' >> "${CI_ENV_FILE}" || true
done

View File

@@ -7,7 +7,6 @@
# Server layout (one file per service): # Server layout (one file per service):
# /home/user/environmen/<project>/<branch-slug>/freight-api.env # /home/user/environmen/<project>/<branch-slug>/freight-api.env
# /home/user/environmen/<project>/<branch-slug>/freight-portal.env # /home/user/environmen/<project>/<branch-slug>/freight-portal.env
# /home/user/environmen/<project>/<branch-slug>/freight-web.build.env (optional, exports VITE_API_URL etc.)
set -euo pipefail set -euo pipefail
@@ -62,26 +61,8 @@ for service in "$@"; do
echo "${service_var}_PORT=${port_value}" >> "${GITHUB_ENV}" echo "${service_var}_PORT=${port_value}" >> "${GITHUB_ENV}"
echo "Exported ${service_var}_PORT from ${src}" echo "Exported ${service_var}_PORT from ${src}"
# Forward NEXT_PUBLIC_* vars so docker compose build can inject them as build args. # Forward NEXT_PUBLIC_* and VITE_* vars so docker compose build can inject them as build args.
grep -E '^[[:space:]]*NEXT_PUBLIC_[A-Za-z0-9_]+=' "${src}" \ grep -E '^[[:space:]]*(NEXT_PUBLIC_|VITE_)[A-Za-z0-9_]+=' "${src}" \
| sed -E 's/^[[:space:]]*//' >> "${GITHUB_ENV}" || true | sed -E 's/^[[:space:]]*//' >> "${GITHUB_ENV}" || true
fi fi
done done
# Optional build-time variables (VITE_API_URL, etc.)
# Set BUILD_ENV_FILE=freight-web.build.env or passenger-web.build.env per workflow.
build_env_file="${BUILD_ENV_FILE:-web.build.env}"
build_env="${ENV_ROOT}/${build_env_file}"
if [[ -f "${build_env}" ]]; then
echo "Loading build variables from ${build_env}"
set -a
# shellcheck disable=SC1090
source "${build_env}"
set +a
if [[ -n "${GITHUB_ENV:-}" ]]; then
grep -E '^[[:space:]]*(export[[:space:]]+)?[A-Za-z_][A-Za-z0-9_]*=' "${build_env}" \
| sed -E 's/^[[:space:]]*export[[:space:]]+//' >> "${GITHUB_ENV}"
echo "Wrote build variables to GITHUB_ENV"
fi
fi