commit 8feb949d7dcca8cc1d6cced937271478707246ac Author: Mohammad Sajad Pourajam Date: Mon Apr 27 04:31:32 2026 +0330 UPDATE diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa53f44 --- /dev/null +++ b/README.md @@ -0,0 +1,236 @@ +# اسکریپت‌های مدیریت ماژول‌ها + +این ریپو چند اسکریپت کمکی برای مدیریت ماژول‌ها، ساب‌ماژول `Schemas` و اجرای Docker دارد. در این فایل، کاربرد هر اسکریپت و نحوه استفاده از آن به فارسی توضیح داده شده است. + +## 1) افزودن همه ماژول‌ها به‌صورت submodule + +فایل: + +```bash +./add_submodules.sh +``` + +کار این اسکریپت: + +- ماژول‌های `Ai`، `SensorHub`، `Backend`، `Accsess` و `Schemas` را به‌صورت submodule اضافه می‌کند +- branch پیش‌فرض روی `develop` است +- اگر submodule از قبل ثبت شده باشد، همان ماژول را skip می‌کند + +استفاده: + +```bash +./add_submodules.sh +``` + +اگر می‌خواهید `git submodule add` با `--force` اجرا شود: + +```bash +./add_submodules.sh --force +``` + +## 2) افزودن `Schemas` به پروژه‌ها + +فایل: + +```bash +./add_schemas_submodule.sh +``` + +کار این اسکریپت: + +- ساب‌ماژول `Schemas` را داخل پروژه‌های `Ai`، `Backend` و `SensorHub` اضافه می‌کند +- اگر `Schemas` از قبل اضافه شده باشد، آن پروژه را رد می‌کند +- اگر پوشه `Schemas` از قبل وجود داشته باشد، چیزی را overwrite نمی‌کند + +استفاده: + +```bash +./add_schemas_submodule.sh +``` + +اگر می‌خواهید `git submodule add` با `--force` اجرا شود: + +```bash +./add_schemas_submodule.sh --force +``` + +فقط برای بعضی پروژه‌ها: + +```bash +./add_schemas_submodule.sh Ai Backend +``` + +ترکیب با `--force`: + +```bash +./add_schemas_submodule.sh --force Ai Backend +``` + +اگر آدرس ریپوی `Schemas` تغییر کرد: + +```bash +SCHEMAS_REPO_URL="ssh://git@host:port/path/Schemas.git" ./add_schemas_submodule.sh +``` + +## 3) pull گرفتن از `Schemas` داخل پروژه‌ها + +فایل: + +```bash +./pull_schemas_submodule.sh +``` + +کار این اسکریپت: + +- داخل `Ai`، `Backend` و `SensorHub` می‌رود +- اگر `Schemas` به‌عنوان submodule وجود داشته باشد، روی آن `git pull --ff-only` اجرا می‌کند +- اگر submodule هنوز initialize نشده باشد، آن پروژه را رد می‌کند + +استفاده: + +```bash +./pull_schemas_submodule.sh +``` + +فقط برای بعضی پروژه‌ها: + +```bash +./pull_schemas_submodule.sh Ai SensorHub +``` + +## 4) pull گرفتن از همه ماژول‌ها + +فایل: + +```bash +./pull_all_modules.sh +``` + +کار این اسکریپت: + +- روی ماژول‌های اصلی `Accsess`، `Ai`، `Backend`، `Schemas` و `SensorHub` دستور `git pull --ff-only` اجرا می‌کند +- اگر داخل هر ماژول submodule وجود داشته باشد: + - ابتدا `submodule update --init --recursive` اجرا می‌کند + - سپس روی submoduleهای داخلی هم `git pull --ff-only` اجرا می‌کند + +استفاده: + +```bash +./pull_all_modules.sh +``` + +فقط برای چند ماژول مشخص: + +```bash +./pull_all_modules.sh Ai Backend +``` + +## 5) اجرای Docker برای ماژول‌ها + +فایل: + +```bash +./run_docker_modules.sh +``` + +کار این اسکریپت: + +- همه پوشه‌های داخل ریشه پروژه را بررسی می‌کند +- هر پوشه‌ای که فایل Docker Compose داشته باشد اجرا می‌شود +- این فایل‌ها پشتیبانی می‌شوند: + - `docker-compose.yaml` + - `docker-compose.yml` + - `compose.yaml` + - `compose.yml` +- اجرای پیش‌فرض به شکل detached است: + - `docker compose up -d` + +استفاده: + +```bash +./run_docker_modules.sh +``` + +فقط برای چند ماژول مشخص: + +```bash +./run_docker_modules.sh Ai Backend +``` + +### اجرای Docker با build + +اگر بخواهید قبل از اجرا build هم انجام شود، از فلگ `--build` استفاده کنید: + +```bash +./run_docker_modules.sh --build +``` + +برای چند ماژول مشخص همراه با build: + +```bash +./run_docker_modules.sh --build Ai SensorHub +``` + +### راهنمای اسکریپت Docker + +```bash +./run_docker_modules.sh --help +``` + +## 6) bootstrap کامل ماژول‌ها + +فایل: + +```bash +./bootstrap_modules.sh +``` + +کار این اسکریپت: + +- در step 1 اسکریپت `add_submodules.sh` را اجرا می‌کند +- در step 2 اسکریپت `add_schemas_submodule.sh` را اجرا می‌کند +- در step 3 اسکریپت `run_docker_modules.sh` را اجرا می‌کند +- می‌تواند `--force` را به اسکریپت‌های add و `--build` را به Docker پاس بدهد + +استفاده: + +```bash +./bootstrap_modules.sh +``` + +اجرا فقط تا step 2: + +```bash +./bootstrap_modules.sh --step 2 +``` + +اجرا با `--force`: + +```bash +./bootstrap_modules.sh --force +``` + +اجرا با `--force` فقط تا step 2: + +```bash +./bootstrap_modules.sh --force --step 2 +``` + +اجرا با build برای Docker: + +```bash +./bootstrap_modules.sh --build +``` + +## نکات مهم + +- تمام اسکریپت‌ها طوری نوشته شده‌اند که اگر مسیر موردنظر git repo نباشد یا فایل لازم را نداشته باشد، به‌جای خطای مخرب، آن مورد را skip کنند +- در اسکریپت‌های pull از `--ff-only` استفاده شده تا merge ناخواسته ایجاد نشود +- قبل از اجرا، مطمئن شوید اسکریپت‌ها executable هستند: + +```bash +chmod +x add_schemas_submodule.sh +chmod +x pull_schemas_submodule.sh +chmod +x pull_all_modules.sh +chmod +x run_docker_modules.sh +``` diff --git a/add_schemas_submodule.sh b/add_schemas_submodule.sh new file mode 100755 index 0000000..2d88c90 --- /dev/null +++ b/add_schemas_submodule.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCHEMAS_REPO_URL="${SCHEMAS_REPO_URL:-ssh://git@git.crop-logic.ir:2222/sajad-dev/Schemas.git}" +TARGET_BRANCH="${TARGET_BRANCH:-develop}" +FORCE_SUBMODULE_ADD=false + +usage() { + cat <<'EOF' +Usage: ./add_schemas_submodule.sh [--force|-f] [repo...] + +Options: + -f, --force Pass --force to `git submodule add` +EOF +} + +TARGET_REPOS=() + +while [[ "$#" -gt 0 ]]; do + case "$1" in + -f|--force) + FORCE_SUBMODULE_ADD=true + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + while [[ "$#" -gt 0 ]]; do + TARGET_REPOS+=("$1") + shift + done + break + ;; + *) + TARGET_REPOS+=("$1") + ;; + esac + shift +done + +if [[ "${#TARGET_REPOS[@]}" -eq 0 ]]; then + TARGET_REPOS=("Ai" "Backend" "SensorHub") +fi + +add_schemas_submodule() { + local repo_dir="$1" + local submodule_path="Schemas" + local submodule_dir="${repo_dir}/${submodule_path}" + local add_args=(-b "$TARGET_BRANCH") + + if [[ "$FORCE_SUBMODULE_ADD" == true ]]; then + add_args+=(--force) + fi + + if ! git -C "$repo_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Skipping ${repo_dir}: not a git repository." + return 0 + fi + + if git -C "$repo_dir" config --file .gitmodules --get-regexp "submodule\\..*\\.path" 2>/dev/null \ + | awk '{print $2}' \ + | grep -Fxq "$submodule_path"; then + echo "Skipping ${repo_dir}: ${submodule_path} submodule already exists." + return 0 + fi + + if [[ -e "${repo_dir}/${submodule_path}" ]]; then + echo "Skipping ${repo_dir}: ${repo_dir}/${submodule_path} already exists." + return 0 + fi + + echo "Adding ${submodule_path} to ${repo_dir} on branch ${TARGET_BRANCH}" + git -C "$repo_dir" submodule add "${add_args[@]}" "$SCHEMAS_REPO_URL" + + git -C "$submodule_dir" fetch origin "$TARGET_BRANCH" + if git -C "$submodule_dir" show-ref --verify --quiet "refs/heads/${TARGET_BRANCH}"; then + git -C "$submodule_dir" checkout "$TARGET_BRANCH" + else + git -C "$submodule_dir" checkout -b "$TARGET_BRANCH" "origin/${TARGET_BRANCH}" + fi + + echo "Checked out ${submodule_dir} on ${TARGET_BRANCH}" +} + +for repo_dir in "${TARGET_REPOS[@]}"; do + add_schemas_submodule "$repo_dir" +done + +echo "Done." diff --git a/add_submodules.sh b/add_submodules.sh new file mode 100755 index 0000000..5a70ed4 --- /dev/null +++ b/add_submodules.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BASE_URL="ssh://git@git.crop-logic.ir:2222/sajad-dev" +TARGET_BRANCH="${TARGET_BRANCH:-develop}" +FORCE_SUBMODULE_ADD=false +MODULES=( + "Ai" + "SensorHub" + "Backend" + "Accsess" + "Schemas" +) + +usage() { + cat <<'EOF' +Usage: ./add_submodules.sh [--force|-f] + +Options: + -f, --force Pass --force to `git submodule add` +EOF +} + +while [[ "$#" -gt 0 ]]; do + case "$1" in + -f|--force) + FORCE_SUBMODULE_ADD=true + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac + shift +done + +ensure_gitmodules_file() { + if [[ ! -f ".gitmodules" ]]; then + touch .gitmodules + echo "Created empty .gitmodules" + fi +} + +checkout_target_branch() { + local repo_dir="$1" + + git -C "$repo_dir" fetch origin "$TARGET_BRANCH" + + if git -C "$repo_dir" show-ref --verify --quiet "refs/heads/${TARGET_BRANCH}"; then + git -C "$repo_dir" checkout "$TARGET_BRANCH" + else + git -C "$repo_dir" checkout -b "$TARGET_BRANCH" "origin/${TARGET_BRANCH}" + fi +} + +ensure_gitmodules_file + +for module in "${MODULES[@]}"; do + repo_url="${BASE_URL}/${module}.git" + add_args=(-b "$TARGET_BRANCH") + + if [[ "$FORCE_SUBMODULE_ADD" == true ]]; then + add_args+=(--force) + fi + + if git config --file .gitmodules --get-regexp "submodule\\..*\\.path" 2>/dev/null | awk '{print $2}' | grep -Fxq "$module"; then + echo "Skipping ${module}: submodule already exists." + else + echo "Adding ${module} from ${repo_url} on branch ${TARGET_BRANCH}" + git submodule add "${add_args[@]}" "$repo_url" + fi + + checkout_target_branch "$module" + echo "Checked out ${module} on ${TARGET_BRANCH}" + + env_example_path="${module}/.env.example" + env_path="${module}/.env" + + if [[ -f "$env_example_path" ]]; then + if [[ -f "$env_path" ]]; then + echo "Skipping ${module}: .env already exists." + else + cp "$env_example_path" "$env_path" + echo "Created ${env_path} from .env.example" + fi + else + echo "Skipping ${module}: .env.example not found." + fi +done + +echo "Done." diff --git a/bootstrap_modules.sh b/bootstrap_modules.sh new file mode 100755 index 0000000..b80c9b4 --- /dev/null +++ b/bootstrap_modules.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BUILD_FLAG=0 +FORCE_FLAG=0 +DOCKER_MODULES=() +MAX_STEP=3 + +usage() { + cat <<'EOF' +Usage: + ./bootstrap_modules.sh [--build] [--force] [--step N] [module ...] + +Steps: + 1. Run `add_submodules.sh` + 2. Run `add_schemas_submodule.sh` + 3. Run `run_docker_modules.sh` + +Options: + --build Pass `--build` to `run_docker_modules.sh` + --force Pass `--force` to submodule add scripts + --step N Run only up to step N (1, 2, or 3) + -h, --help Show this help message + +Examples: + ./bootstrap_modules.sh + ./bootstrap_modules.sh --step 2 + ./bootstrap_modules.sh --force + ./bootstrap_modules.sh --build + ./bootstrap_modules.sh Ai Backend + ./bootstrap_modules.sh --build Ai SensorHub + ./bootstrap_modules.sh --force --step 2 +EOF +} + +while [[ "$#" -gt 0 ]]; do + case "$1" in + --build) + BUILD_FLAG=1 + shift + ;; + --force) + FORCE_FLAG=1 + shift + ;; + --step) + if [[ "$#" -lt 2 ]]; then + echo "Missing value for --step" >&2 + usage + exit 1 + fi + MAX_STEP="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + while [[ "$#" -gt 0 ]]; do + DOCKER_MODULES+=("$1") + shift + done + ;; + -*) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + DOCKER_MODULES+=("$1") + shift + ;; + esac +done + +if [[ ! "$MAX_STEP" =~ ^[1-3]$ ]]; then + echo "Invalid value for --step: ${MAX_STEP}. Use 1, 2, or 3." >&2 + exit 1 +fi + +run_script() { + local script_path="$1" + shift || true + + if [[ ! -x "$script_path" ]]; then + echo "Script is not executable: ${script_path}" >&2 + exit 1 + fi + + echo "Running ${script_path} $*" + "$script_path" "$@" +} + +submodule_args=() +if [[ "$FORCE_FLAG" -eq 1 ]]; then + submodule_args+=("--force") +fi + +if [[ "$MAX_STEP" -ge 1 ]]; then + run_script "./add_submodules.sh" "${submodule_args[@]}" +fi + +if [[ "$MAX_STEP" -ge 2 ]]; then + run_script "./add_schemas_submodule.sh" "${submodule_args[@]}" +fi + +docker_args=() +if [[ "$BUILD_FLAG" -eq 1 ]]; then + docker_args+=("--build") +fi +if [[ "${#DOCKER_MODULES[@]}" -gt 0 ]]; then + docker_args+=("${DOCKER_MODULES[@]}") +fi + +if [[ "$MAX_STEP" -ge 3 ]]; then + run_script "./run_docker_modules.sh" "${docker_args[@]}" +fi + +echo "Bootstrap completed." diff --git a/pull_all_modules.sh b/pull_all_modules.sh new file mode 100755 index 0000000..08f2556 --- /dev/null +++ b/pull_all_modules.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +set -euo pipefail + +TARGET_BRANCH="${TARGET_BRANCH:-develop}" + +if [[ "$#" -gt 0 ]]; then + TARGET_MODULES=("$@") +else + TARGET_MODULES=("Accsess" "Ai" "Backend" "Schemas" "SensorHub") +fi + +checkout_and_pull_branch() { + local repo_dir="$1" + + git -C "$repo_dir" fetch origin "$TARGET_BRANCH" + + if git -C "$repo_dir" show-ref --verify --quiet "refs/heads/${TARGET_BRANCH}"; then + git -C "$repo_dir" checkout "$TARGET_BRANCH" + else + git -C "$repo_dir" checkout -b "$TARGET_BRANCH" "origin/${TARGET_BRANCH}" + fi + + git -C "$repo_dir" pull --ff-only origin "$TARGET_BRANCH" +} + +pull_repo() { + local repo_dir="$1" + + if ! git -C "$repo_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Skipping ${repo_dir}: not a git repository." + return 0 + fi + + echo "Pulling ${repo_dir} on branch ${TARGET_BRANCH}" + checkout_and_pull_branch "$repo_dir" + + if [[ -f "${repo_dir}/.gitmodules" ]]; then + echo "Updating nested submodules in ${repo_dir}" + git -C "$repo_dir" submodule update --init --recursive + git -C "$repo_dir" submodule foreach --recursive " + echo \"Updating \${displaypath} on branch ${TARGET_BRANCH}\" + git fetch origin ${TARGET_BRANCH} + if git show-ref --verify --quiet refs/heads/${TARGET_BRANCH}; then + git checkout ${TARGET_BRANCH} + else + git checkout -b ${TARGET_BRANCH} origin/${TARGET_BRANCH} + fi + git pull --ff-only origin ${TARGET_BRANCH} + " + fi +} + +for module_dir in "${TARGET_MODULES[@]}"; do + pull_repo "$module_dir" +done + +echo "Done." diff --git a/pull_schemas_submodule.sh b/pull_schemas_submodule.sh new file mode 100755 index 0000000..1b78529 --- /dev/null +++ b/pull_schemas_submodule.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +set -euo pipefail + +TARGET_BRANCH="${TARGET_BRANCH:-develop}" + +if [[ "$#" -gt 0 ]]; then + TARGET_REPOS=("$@") +else + TARGET_REPOS=("Ai" "Backend" "SensorHub") +fi + +pull_schemas_submodule() { + local repo_dir="$1" + local submodule_path="Schemas" + local submodule_dir="${repo_dir}/${submodule_path}" + + if ! git -C "$repo_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Skipping ${repo_dir}: not a git repository." + return 0 + fi + + if ! git -C "$submodule_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Skipping ${repo_dir}: ${submodule_path} submodule is not initialized." + return 0 + fi + + echo "Updating ${submodule_path} in ${repo_dir} on branch ${TARGET_BRANCH}" + git -C "$submodule_dir" fetch origin "$TARGET_BRANCH" + + if git -C "$submodule_dir" show-ref --verify --quiet "refs/heads/${TARGET_BRANCH}"; then + git -C "$submodule_dir" checkout "$TARGET_BRANCH" + else + git -C "$submodule_dir" checkout -b "$TARGET_BRANCH" "origin/${TARGET_BRANCH}" + fi + + git -C "$submodule_dir" pull --ff-only origin "$TARGET_BRANCH" +} + +for repo_dir in "${TARGET_REPOS[@]}"; do + pull_schemas_submodule "$repo_dir" +done + +echo "Done." diff --git a/run_docker_modules.sh b/run_docker_modules.sh new file mode 100755 index 0000000..8d27590 --- /dev/null +++ b/run_docker_modules.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BUILD_FLAG=0 +TARGET_MODULES=() + +usage() { + cat <<'EOF' +Usage: + ./run_docker_modules.sh [--build] [module ...] + +Options: + --build Run `docker compose up --build -d` + -h, --help Show this help message + +Examples: + ./run_docker_modules.sh + ./run_docker_modules.sh --build + ./run_docker_modules.sh Ai Backend + ./run_docker_modules.sh --build Ai SensorHub +EOF +} + +while [[ "$#" -gt 0 ]]; do + case "$1" in + --build) + BUILD_FLAG=1 + shift + ;; + -h|--help) + usage + exit 0 + ;; + --) + shift + while [[ "$#" -gt 0 ]]; do + TARGET_MODULES+=("$1") + shift + done + ;; + -*) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + TARGET_MODULES+=("$1") + shift + ;; + esac +done + +find_compose_file() { + local module_dir="$1" + local candidates=( + "docker-compose.yaml" + "docker-compose.yml" + "compose.yaml" + "compose.yml" + ) + + local candidate + for candidate in "${candidates[@]}"; do + if [[ -f "${module_dir}/${candidate}" ]]; then + printf '%s\n' "${candidate}" + return 0 + fi + done + + return 1 +} + +collect_modules_with_compose() { + local dir + for dir in */ ; do + dir="${dir%/}" + [[ -d "$dir" ]] || continue + + if find_compose_file "$dir" >/dev/null; then + printf '%s\n' "$dir" + fi + done +} + +run_module() { + local module_dir="$1" + local compose_file + local compose_path + + if [[ ! -d "$module_dir" ]]; then + echo "Skipping ${module_dir}: directory not found." + return 0 + fi + + if ! compose_file="$(find_compose_file "$module_dir")"; then + echo "Skipping ${module_dir}: no docker compose file found." + return 0 + fi + + compose_path="${module_dir}/${compose_file}" + + echo "Starting ${module_dir} with ${compose_file}" + if [[ "$BUILD_FLAG" -eq 1 ]]; then + docker compose -f "${compose_path}" up --build -d + else + docker compose -f "${compose_path}" up -d + fi +} + +if [[ "${#TARGET_MODULES[@]}" -eq 0 ]]; then + mapfile -t TARGET_MODULES < <(collect_modules_with_compose) +fi + +if [[ "${#TARGET_MODULES[@]}" -eq 0 ]]; then + echo "No modules with docker compose files found." + exit 0 +fi + +for module_dir in "${TARGET_MODULES[@]}"; do + run_module "$module_dir" +done + +echo "Done."